]> git.lizzy.rs Git - rust.git/blob - src/tools/rustfmt/src/config/config_type.rs
Rollup merge of #106583 - estebank:suggest-result-coercion, r=compiler-errors
[rust.git] / src / tools / rustfmt / src / config / config_type.rs
1 use crate::config::file_lines::FileLines;
2 use crate::config::macro_names::MacroSelectors;
3 use crate::config::options::{IgnoreList, WidthHeuristics};
4
5 /// Trait for types that can be used in `Config`.
6 pub(crate) trait ConfigType: Sized {
7     /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
8     /// pipe-separated list of variants; for other types it returns `<type>`.
9     fn doc_hint() -> String;
10
11     /// Return `true` if the variant (i.e. value of this type) is stable.
12     ///
13     /// By default, return true for all values. Enums annotated with `#[config_type]`
14     /// are automatically implemented, based on the `#[unstable_variant]` annotation.
15     fn stable_variant(&self) -> bool {
16         true
17     }
18 }
19
20 impl ConfigType for bool {
21     fn doc_hint() -> String {
22         String::from("<boolean>")
23     }
24 }
25
26 impl ConfigType for usize {
27     fn doc_hint() -> String {
28         String::from("<unsigned integer>")
29     }
30 }
31
32 impl ConfigType for isize {
33     fn doc_hint() -> String {
34         String::from("<signed integer>")
35     }
36 }
37
38 impl ConfigType for String {
39     fn doc_hint() -> String {
40         String::from("<string>")
41     }
42 }
43
44 impl ConfigType for FileLines {
45     fn doc_hint() -> String {
46         String::from("<json>")
47     }
48 }
49
50 impl ConfigType for MacroSelectors {
51     fn doc_hint() -> String {
52         String::from("[<string>, ...]")
53     }
54 }
55
56 impl ConfigType for WidthHeuristics {
57     fn doc_hint() -> String {
58         String::new()
59     }
60 }
61
62 impl ConfigType for IgnoreList {
63     fn doc_hint() -> String {
64         String::from("[<string>,..]")
65     }
66 }
67
68 macro_rules! create_config {
69     // Options passed in to the macro.
70     //
71     // - $i: the ident name of the option
72     // - $ty: the type of the option value
73     // - $def: the default value of the option
74     // - $stb: true if the option is stable
75     // - $dstring: description of the option
76     ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
77         #[cfg(test)]
78         use std::collections::HashSet;
79         use std::io::Write;
80
81         use serde::{Deserialize, Serialize};
82
83         #[derive(Clone)]
84         #[allow(unreachable_pub)]
85         pub struct Config {
86             // For each config item, we store:
87             //
88             // - 0: true if the value has been access
89             // - 1: true if the option was manually initialized
90             // - 2: the option value
91             // - 3: true if the option is unstable
92             $($i: (Cell<bool>, bool, $ty, bool)),+
93         }
94
95         // Just like the Config struct but with each property wrapped
96         // as Option<T>. This is used to parse a rustfmt.toml that doesn't
97         // specify all properties of `Config`.
98         // We first parse into `PartialConfig`, then create a default `Config`
99         // and overwrite the properties with corresponding values from `PartialConfig`.
100         #[derive(Deserialize, Serialize, Clone)]
101         #[allow(unreachable_pub)]
102         pub struct PartialConfig {
103             $(pub $i: Option<$ty>),+
104         }
105
106         // Macro hygiene won't allow us to make `set_$i()` methods on Config
107         // for each item, so this struct is used to give the API to set values:
108         // `config.set().option(false)`. It's pretty ugly. Consider replacing
109         // with `config.set_option(false)` if we ever get a stable/usable
110         // `concat_idents!()`.
111         #[allow(unreachable_pub)]
112         pub struct ConfigSetter<'a>(&'a mut Config);
113
114         impl<'a> ConfigSetter<'a> {
115             $(
116             #[allow(unreachable_pub)]
117             pub fn $i(&mut self, value: $ty) {
118                 (self.0).$i.2 = value;
119                 match stringify!($i) {
120                     "max_width"
121                     | "use_small_heuristics"
122                     | "fn_call_width"
123                     | "single_line_if_else_max_width"
124                     | "attr_fn_like_width"
125                     | "struct_lit_width"
126                     | "struct_variant_width"
127                     | "array_width"
128                     | "chain_width" => self.0.set_heuristics(),
129                     "merge_imports" => self.0.set_merge_imports(),
130                     "fn_args_layout" => self.0.set_fn_args_layout(),
131                     &_ => (),
132                 }
133             }
134             )+
135         }
136
137         // Query each option, returns true if the user set the option, false if
138         // a default was used.
139         #[allow(unreachable_pub)]
140         pub struct ConfigWasSet<'a>(&'a Config);
141
142         impl<'a> ConfigWasSet<'a> {
143             $(
144             #[allow(unreachable_pub)]
145             pub fn $i(&self) -> bool {
146                 (self.0).$i.1
147             }
148             )+
149         }
150
151         impl Config {
152             $(
153             #[allow(unreachable_pub)]
154             pub fn $i(&self) -> $ty {
155                 self.$i.0.set(true);
156                 self.$i.2.clone()
157             }
158             )+
159
160             #[allow(unreachable_pub)]
161             pub fn set(&mut self) -> ConfigSetter<'_> {
162                 ConfigSetter(self)
163             }
164
165             #[allow(unreachable_pub)]
166             pub fn was_set(&self) -> ConfigWasSet<'_> {
167                 ConfigWasSet(self)
168             }
169
170             fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
171             $(
172                 if let Some(option_value) = parsed.$i {
173                     let option_stable = self.$i.3;
174                     if $crate::config::config_type::is_stable_option_and_value(
175                         stringify!($i), option_stable, &option_value
176                     ) {
177                         self.$i.1 = true;
178                         self.$i.2 = option_value;
179                     }
180                 }
181             )+
182                 self.set_heuristics();
183                 self.set_ignore(dir);
184                 self.set_merge_imports();
185                 self.set_fn_args_layout();
186                 self
187             }
188
189             /// Returns a hash set initialized with every user-facing config option name.
190             #[cfg(test)]
191             pub(crate) fn hash_set() -> HashSet<String> {
192                 let mut hash_set = HashSet::new();
193                 $(
194                     hash_set.insert(stringify!($i).to_owned());
195                 )+
196                 hash_set
197             }
198
199             pub(crate) fn is_valid_name(name: &str) -> bool {
200                 match name {
201                     $(
202                         stringify!($i) => true,
203                     )+
204                         _ => false,
205                 }
206             }
207
208             #[allow(unreachable_pub)]
209             pub fn is_valid_key_val(key: &str, val: &str) -> bool {
210                 match key {
211                     $(
212                         stringify!($i) => val.parse::<$ty>().is_ok(),
213                     )+
214                         _ => false,
215                 }
216             }
217
218             #[allow(unreachable_pub)]
219             pub fn used_options(&self) -> PartialConfig {
220                 PartialConfig {
221                     $(
222                         $i: if self.$i.0.get() {
223                                 Some(self.$i.2.clone())
224                             } else {
225                                 None
226                             },
227                     )+
228                 }
229             }
230
231             #[allow(unreachable_pub)]
232             pub fn all_options(&self) -> PartialConfig {
233                 PartialConfig {
234                     $(
235                         $i: Some(self.$i.2.clone()),
236                     )+
237                 }
238             }
239
240             #[allow(unreachable_pub)]
241             pub fn override_value(&mut self, key: &str, val: &str)
242             {
243                 match key {
244                     $(
245                         stringify!($i) => {
246                             let option_value = val.parse::<$ty>()
247                                 .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
248                                                  stringify!($i),
249                                                  val,
250                                                  stringify!($ty)));
251
252                             // Users are currently allowed to set unstable
253                             // options/variants via the `--config` options override.
254                             //
255                             // There is ongoing discussion about how to move forward here:
256                             // https://github.com/rust-lang/rustfmt/pull/5379
257                             //
258                             // For now, do not validate whether the option or value is stable,
259                             // just always set it.
260                             self.$i.1 = true;
261                             self.$i.2 = option_value;
262                         }
263                     )+
264                     _ => panic!("Unknown config key in override: {}", key)
265                 }
266
267                 match key {
268                     "max_width"
269                     | "use_small_heuristics"
270                     | "fn_call_width"
271                     | "single_line_if_else_max_width"
272                     | "attr_fn_like_width"
273                     | "struct_lit_width"
274                     | "struct_variant_width"
275                     | "array_width"
276                     | "chain_width" => self.set_heuristics(),
277                     "merge_imports" => self.set_merge_imports(),
278                     "fn_args_layout" => self.set_fn_args_layout(),
279                     &_ => (),
280                 }
281             }
282
283             #[allow(unreachable_pub)]
284             pub fn is_hidden_option(name: &str) -> bool {
285                 const HIDE_OPTIONS: [&str; 6] = [
286                     "verbose",
287                     "verbose_diff",
288                     "file_lines",
289                     "width_heuristics",
290                     "merge_imports",
291                     "fn_args_layout"
292                 ];
293                 HIDE_OPTIONS.contains(&name)
294             }
295
296             #[allow(unreachable_pub)]
297             pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
298                 use std::cmp;
299                 let max = 0;
300                 $( let max = cmp::max(max, stringify!($i).len()+1); )+
301                 let space_str = " ".repeat(max);
302                 writeln!(out, "Configuration Options:").unwrap();
303                 $(
304                     if $stb || include_unstable {
305                         let name_raw = stringify!($i);
306
307                         if !Config::is_hidden_option(name_raw) {
308                             let mut name_out = String::with_capacity(max);
309                             for _ in name_raw.len()..max-1 {
310                                 name_out.push(' ')
311                             }
312                             name_out.push_str(name_raw);
313                             name_out.push(' ');
314                             let mut default_str = format!("{}", $def);
315                             if default_str.is_empty() {
316                                 default_str = String::from("\"\"");
317                             }
318                             writeln!(out,
319                                     "{}{} Default: {}{}",
320                                     name_out,
321                                     <$ty>::doc_hint(),
322                                     default_str,
323                                     if !$stb { " (unstable)" } else { "" }).unwrap();
324                             $(
325                                 writeln!(out, "{}{}", space_str, $dstring).unwrap();
326                             )+
327                             writeln!(out).unwrap();
328                         }
329                     }
330                 )+
331             }
332
333             fn set_width_heuristics(&mut self, heuristics: WidthHeuristics) {
334                 let max_width = self.max_width.2;
335                 let get_width_value = |
336                     was_set: bool,
337                     override_value: usize,
338                     heuristic_value: usize,
339                     config_key: &str,
340                 | -> usize {
341                     if !was_set {
342                         return heuristic_value;
343                     }
344                     if override_value > max_width {
345                         eprintln!(
346                             "`{0}` cannot have a value that exceeds `max_width`. \
347                             `{0}` will be set to the same value as `max_width`",
348                             config_key,
349                         );
350                         return max_width;
351                     }
352                     override_value
353                 };
354
355                 let fn_call_width = get_width_value(
356                     self.was_set().fn_call_width(),
357                     self.fn_call_width.2,
358                     heuristics.fn_call_width,
359                     "fn_call_width",
360                 );
361                 self.fn_call_width.2 = fn_call_width;
362
363                 let attr_fn_like_width = get_width_value(
364                     self.was_set().attr_fn_like_width(),
365                     self.attr_fn_like_width.2,
366                     heuristics.attr_fn_like_width,
367                     "attr_fn_like_width",
368                 );
369                 self.attr_fn_like_width.2 = attr_fn_like_width;
370
371                 let struct_lit_width = get_width_value(
372                     self.was_set().struct_lit_width(),
373                     self.struct_lit_width.2,
374                     heuristics.struct_lit_width,
375                     "struct_lit_width",
376                 );
377                 self.struct_lit_width.2 = struct_lit_width;
378
379                 let struct_variant_width = get_width_value(
380                     self.was_set().struct_variant_width(),
381                     self.struct_variant_width.2,
382                     heuristics.struct_variant_width,
383                     "struct_variant_width",
384                 );
385                 self.struct_variant_width.2 = struct_variant_width;
386
387                 let array_width = get_width_value(
388                     self.was_set().array_width(),
389                     self.array_width.2,
390                     heuristics.array_width,
391                     "array_width",
392                 );
393                 self.array_width.2 = array_width;
394
395                 let chain_width = get_width_value(
396                     self.was_set().chain_width(),
397                     self.chain_width.2,
398                     heuristics.chain_width,
399                     "chain_width",
400                 );
401                 self.chain_width.2 = chain_width;
402
403                 let single_line_if_else_max_width = get_width_value(
404                     self.was_set().single_line_if_else_max_width(),
405                     self.single_line_if_else_max_width.2,
406                     heuristics.single_line_if_else_max_width,
407                     "single_line_if_else_max_width",
408                 );
409                 self.single_line_if_else_max_width.2 = single_line_if_else_max_width;
410             }
411
412             fn set_heuristics(&mut self) {
413                 let max_width = self.max_width.2;
414                 match self.use_small_heuristics.2 {
415                     Heuristics::Default =>
416                         self.set_width_heuristics(WidthHeuristics::scaled(max_width)),
417                     Heuristics::Max => self.set_width_heuristics(WidthHeuristics::set(max_width)),
418                     Heuristics::Off => self.set_width_heuristics(WidthHeuristics::null()),
419                 };
420             }
421
422             fn set_ignore(&mut self, dir: &Path) {
423                 self.ignore.2.add_prefix(dir);
424             }
425
426             fn set_merge_imports(&mut self) {
427                 if self.was_set().merge_imports() {
428                     eprintln!(
429                         "Warning: the `merge_imports` option is deprecated. \
430                         Use `imports_granularity=\"Crate\"` instead"
431                     );
432                     if !self.was_set().imports_granularity() {
433                         self.imports_granularity.2 = if self.merge_imports() {
434                             ImportGranularity::Crate
435                         } else {
436                             ImportGranularity::Preserve
437                         };
438                     }
439                 }
440             }
441
442             fn set_fn_args_layout(&mut self) {
443                 if self.was_set().fn_args_layout() {
444                     eprintln!(
445                         "Warning: the `fn_args_layout` option is deprecated. \
446                         Use `fn_params_layout`. instead"
447                     );
448                     if !self.was_set().fn_params_layout() {
449                         self.fn_params_layout.2 = self.fn_args_layout();
450                     }
451                 }
452             }
453
454             #[allow(unreachable_pub)]
455             /// Returns `true` if the config key was explicitly set and is the default value.
456             pub fn is_default(&self, key: &str) -> bool {
457                 $(
458                     if let stringify!($i) = key {
459                         return self.$i.1 && self.$i.2 == $def;
460                     }
461                  )+
462                 false
463             }
464         }
465
466         // Template for the default configuration
467         impl Default for Config {
468             fn default() -> Config {
469                 Config {
470                     $(
471                         $i: (Cell::new(false), false, $def, $stb),
472                     )+
473                 }
474             }
475         }
476     )
477 }
478
479 pub(crate) fn is_stable_option_and_value<T>(
480     option_name: &str,
481     option_stable: bool,
482     option_value: &T,
483 ) -> bool
484 where
485     T: PartialEq + std::fmt::Debug + ConfigType,
486 {
487     let nightly = crate::is_nightly_channel!();
488     let variant_stable = option_value.stable_variant();
489     match (nightly, option_stable, variant_stable) {
490         // Stable with an unstable option
491         (false, false, _) => {
492             eprintln!(
493                 "Warning: can't set `{} = {:?}`, unstable features are only \
494                        available in nightly channel.",
495                 option_name, option_value
496             );
497             false
498         }
499         // Stable with a stable option, but an unstable variant
500         (false, true, false) => {
501             eprintln!(
502                 "Warning: can't set `{} = {:?}`, unstable variants are only \
503                        available in nightly channel.",
504                 option_name, option_value
505             );
506             false
507         }
508         // Nightly: everything allowed
509         // Stable with stable option and variant: allowed
510         (true, _, _) | (false, true, true) => true,
511     }
512 }