rustfmt_nightly/config/
config_type.rs

1use crate::config::file_lines::FileLines;
2use crate::config::macro_names::MacroSelectors;
3use crate::config::options::{IgnoreList, WidthHeuristics};
4
5/// Trait for types that can be used in `Config`.
6pub(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
20impl ConfigType for bool {
21    fn doc_hint() -> String {
22        String::from("<boolean>")
23    }
24}
25
26impl ConfigType for usize {
27    fn doc_hint() -> String {
28        String::from("<unsigned integer>")
29    }
30}
31
32impl ConfigType for isize {
33    fn doc_hint() -> String {
34        String::from("<signed integer>")
35    }
36}
37
38impl ConfigType for String {
39    fn doc_hint() -> String {
40        String::from("<string>")
41    }
42}
43
44impl ConfigType for FileLines {
45    fn doc_hint() -> String {
46        String::from("<json>")
47    }
48}
49
50impl ConfigType for MacroSelectors {
51    fn doc_hint() -> String {
52        String::from("[<string>, ...]")
53    }
54}
55
56impl ConfigType for WidthHeuristics {
57    fn doc_hint() -> String {
58        String::new()
59    }
60}
61
62impl ConfigType for IgnoreList {
63    fn doc_hint() -> String {
64        String::from("[<string>,..]")
65    }
66}
67
68macro_rules! create_config {
69    // Options passed into the macro.
70    //
71    // - $i: the ident name of the option
72    // - $ty: the type of the option value
73    // - $stb: true if the option is stable
74    // - $dstring: description of the option
75    ($($i:ident: $ty:ty, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
76        #[cfg(test)]
77        use std::collections::HashSet;
78        use std::io::Write;
79
80        use serde::{Deserialize, Serialize};
81        use $crate::config::style_edition::StyleEditionDefault;
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            // - 4: true if the option was set manually from a CLI flag
93            // FIXME: 4 is probably unnecessary and duplicative
94            // https://github.com/rust-lang/rustfmt/issues/6252
95            $($i: (Cell<bool>, bool, <$ty as StyleEditionDefault>::ConfigType, bool, bool)),+
96        }
97
98        // Just like the Config struct but with each property wrapped
99        // as Option<T>. This is used to parse a rustfmt.toml that doesn't
100        // specify all properties of `Config`.
101        // We first parse into `PartialConfig`, then create a default `Config`
102        // and overwrite the properties with corresponding values from `PartialConfig`.
103        #[derive(Deserialize, Serialize, Clone)]
104        #[allow(unreachable_pub)]
105        pub struct PartialConfig {
106            $(pub $i: Option<<$ty as StyleEditionDefault>::ConfigType>),+
107        }
108
109        // Macro hygiene won't allow us to make `set_$i()` methods on Config
110        // for each item, so this struct is used to give the API to set values:
111        // `config.set().option(false)`. It's pretty ugly. Consider replacing
112        // with `config.set_option(false)` if we ever get a stable/usable
113        // `concat_idents!()`.
114        #[allow(unreachable_pub)]
115        pub struct ConfigSetter<'a>(&'a mut Config);
116
117        impl<'a> ConfigSetter<'a> {
118            $(
119            #[allow(unreachable_pub)]
120            pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
121                (self.0).$i.2 = value;
122                match stringify!($i) {
123                    "max_width"
124                    | "use_small_heuristics"
125                    | "fn_call_width"
126                    | "single_line_if_else_max_width"
127                    | "single_line_let_else_max_width"
128                    | "attr_fn_like_width"
129                    | "struct_lit_width"
130                    | "struct_variant_width"
131                    | "array_width"
132                    | "chain_width" => self.0.set_heuristics(),
133                    "merge_imports" => self.0.set_merge_imports(),
134                    "fn_args_layout" => self.0.set_fn_args_layout(),
135                    "hide_parse_errors" => self.0.set_hide_parse_errors(),
136                    "version" => self.0.set_version(),
137                    &_ => (),
138                }
139            }
140            )+
141        }
142
143        #[allow(unreachable_pub)]
144        pub struct CliConfigSetter<'a>(&'a mut Config);
145
146        impl<'a> CliConfigSetter<'a> {
147            $(
148            #[allow(unreachable_pub)]
149            pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
150                (self.0).$i.2 = value;
151                (self.0).$i.4 = true;
152                match stringify!($i) {
153                    "max_width"
154                    | "use_small_heuristics"
155                    | "fn_call_width"
156                    | "single_line_if_else_max_width"
157                    | "single_line_let_else_max_width"
158                    | "attr_fn_like_width"
159                    | "struct_lit_width"
160                    | "struct_variant_width"
161                    | "array_width"
162                    | "chain_width" => self.0.set_heuristics(),
163                    "merge_imports" => self.0.set_merge_imports(),
164                    "fn_args_layout" => self.0.set_fn_args_layout(),
165                    "hide_parse_errors" => self.0.set_hide_parse_errors(),
166                    "version" => self.0.set_version(),
167                    &_ => (),
168                }
169            }
170            )+
171        }
172
173        // Query each option, returns true if the user set the option, false if
174        // a default was used.
175        #[allow(unreachable_pub)]
176        pub struct ConfigWasSet<'a>(&'a Config);
177
178        impl<'a> ConfigWasSet<'a> {
179            $(
180            #[allow(unreachable_pub)]
181            pub fn $i(&self) -> bool {
182                (self.0).$i.1
183            }
184            )+
185        }
186
187        // Query each option, returns true if the user set the option via a CLI flag,
188        // false if a default was used.
189        #[allow(unreachable_pub)]
190        pub struct CliConfigWasSet<'a>(&'a Config);
191
192        impl<'a> CliConfigWasSet<'a> {
193            $(
194            #[allow(unreachable_pub)]
195            pub fn $i(&self) -> bool {
196                (self.0).$i.4
197            }
198            )+
199        }
200
201        impl Config {
202            $(
203            #[allow(unreachable_pub)]
204            pub fn $i(&self) -> <$ty as StyleEditionDefault>::ConfigType {
205                self.$i.0.set(true);
206                self.$i.2.clone()
207            }
208            )+
209
210            #[allow(unreachable_pub)]
211            pub(super) fn default_with_style_edition(style_edition: StyleEdition) -> Config {
212                Config {
213                    $(
214                        $i: (
215                                Cell::new(false),
216                                false,
217                                <$ty as StyleEditionDefault>::style_edition_default(
218                                    style_edition
219                                ),
220                                $stb,
221                                false,
222                            ),
223                    )+
224                }
225            }
226
227            #[allow(unreachable_pub)]
228            pub fn set(&mut self) -> ConfigSetter<'_> {
229                ConfigSetter(self)
230            }
231
232            #[allow(unreachable_pub)]
233            pub fn set_cli(&mut self) -> CliConfigSetter<'_> {
234                CliConfigSetter(self)
235            }
236
237            #[allow(unreachable_pub)]
238            pub fn was_set(&self) -> ConfigWasSet<'_> {
239                ConfigWasSet(self)
240            }
241
242            #[allow(unreachable_pub)]
243            pub fn was_set_cli(&self) -> CliConfigWasSet<'_> {
244                CliConfigWasSet(self)
245            }
246
247            fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
248            $(
249                if let Some(option_value) = parsed.$i {
250                    let option_stable = self.$i.3;
251                    if $crate::config::config_type::is_stable_option_and_value(
252                        stringify!($i), option_stable, &option_value
253                    ) {
254                        self.$i.1 = true;
255                        self.$i.2 = option_value;
256                    }
257                }
258            )+
259                self.set_heuristics();
260                self.set_ignore(dir);
261                self.set_merge_imports();
262                self.set_fn_args_layout();
263                self.set_hide_parse_errors();
264                self.set_version();
265                self
266            }
267
268            /// Returns a hash set initialized with every user-facing config option name.
269            #[cfg(test)]
270            pub(crate) fn hash_set() -> HashSet<String> {
271                let mut hash_set = HashSet::new();
272                $(
273                    hash_set.insert(stringify!($i).to_owned());
274                )+
275                hash_set
276            }
277
278            pub(crate) fn is_valid_name(name: &str) -> bool {
279                match name {
280                    $(
281                        stringify!($i) => true,
282                    )+
283                        _ => false,
284                }
285            }
286
287            #[allow(unreachable_pub)]
288            pub fn is_valid_key_val(key: &str, val: &str) -> bool {
289                match key {
290                    $(
291                        stringify!($i) => {
292                            val.parse::<<$ty as StyleEditionDefault>::ConfigType>().is_ok()
293                        }
294                    )+
295                        _ => false,
296                }
297            }
298
299            #[allow(unreachable_pub)]
300            pub fn used_options(&self) -> PartialConfig {
301                PartialConfig {
302                    $(
303                        $i: if self.$i.0.get() {
304                                Some(self.$i.2.clone())
305                            } else {
306                                None
307                            },
308                    )+
309                }
310            }
311
312            #[allow(unreachable_pub)]
313            pub fn all_options(&self) -> PartialConfig {
314                PartialConfig {
315                    $(
316                        $i: Some(self.$i.2.clone()),
317                    )+
318                }
319            }
320
321            #[allow(unreachable_pub)]
322            pub fn override_value(&mut self, key: &str, val: &str)
323            {
324                match key {
325                    $(
326                        stringify!($i) => {
327                            let value = val.parse::<<$ty as StyleEditionDefault>::ConfigType>()
328                                .expect(
329                                    &format!(
330                                        "Failed to parse override for {} (\"{}\") as a {}",
331                                        stringify!($i),
332                                        val,
333                                        stringify!(<$ty as StyleEditionDefault>::ConfigType)
334                                    )
335                                );
336
337                            // Users are currently allowed to set unstable
338                            // options/variants via the `--config` options override.
339                            //
340                            // There is ongoing discussion about how to move forward here:
341                            // https://github.com/rust-lang/rustfmt/pull/5379
342                            //
343                            // For now, do not validate whether the option or value is stable,
344                            // just always set it.
345                            self.$i.1 = true;
346                            self.$i.2 = value;
347                        }
348                    )+
349                    _ => panic!("Unknown config key in override: {}", key)
350                }
351
352                match key {
353                    "max_width"
354                    | "use_small_heuristics"
355                    | "fn_call_width"
356                    | "single_line_if_else_max_width"
357                    | "single_line_let_else_max_width"
358                    | "attr_fn_like_width"
359                    | "struct_lit_width"
360                    | "struct_variant_width"
361                    | "array_width"
362                    | "chain_width" => self.set_heuristics(),
363                    "merge_imports" => self.set_merge_imports(),
364                    "fn_args_layout" => self.set_fn_args_layout(),
365                    "hide_parse_errors" => self.set_hide_parse_errors(),
366                    "version" => self.set_version(),
367                    &_ => (),
368                }
369            }
370
371            #[allow(unreachable_pub)]
372            pub fn is_hidden_option(name: &str) -> bool {
373                const HIDE_OPTIONS: [&str; 7] = [
374                    "verbose",
375                    "verbose_diff",
376                    "file_lines",
377                    "width_heuristics",
378                    "merge_imports",
379                    "fn_args_layout",
380                    "hide_parse_errors"
381                ];
382                HIDE_OPTIONS.contains(&name)
383            }
384
385            #[allow(unreachable_pub)]
386            pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
387                let style_edition = StyleEdition::Edition2015;
388                use std::cmp;
389                let max = 0;
390                $( let max = cmp::max(max, stringify!($i).len()+1); )+
391                let space_str = " ".repeat(max);
392                writeln!(out, "Configuration Options:").unwrap();
393                $(
394                    if $stb || include_unstable {
395                        let name_raw = stringify!($i);
396
397                        if !Config::is_hidden_option(name_raw) {
398                            let mut name_out = String::with_capacity(max);
399                            for _ in name_raw.len()..max-1 {
400                                name_out.push(' ')
401                            }
402                            name_out.push_str(name_raw);
403                            name_out.push(' ');
404                            let default_value = <$ty as StyleEditionDefault>::style_edition_default(
405                                style_edition
406                            );
407                            let mut default_str = format!("{}", default_value);
408                            if default_str.is_empty() {
409                                default_str = String::from("\"\"");
410                            }
411                            writeln!(out,
412                                    "{}{} Default: {}{}",
413                                    name_out,
414                                    <<$ty as StyleEditionDefault>::ConfigType>::doc_hint(),
415                                    default_str,
416                                    if !$stb { " (unstable)" } else { "" }).unwrap();
417                            $(
418                                writeln!(out, "{}{}", space_str, $dstring).unwrap();
419                            )+
420                            writeln!(out).unwrap();
421                        }
422                    }
423                )+
424            }
425
426            fn set_width_heuristics(&mut self, heuristics: WidthHeuristics) {
427                let max_width = self.max_width.2;
428                let get_width_value = |
429                    was_set: bool,
430                    override_value: usize,
431                    heuristic_value: usize,
432                    config_key: &str,
433                | -> usize {
434                    if !was_set {
435                        return heuristic_value;
436                    }
437                    if override_value > max_width {
438                        eprintln!(
439                            "`{0}` cannot have a value that exceeds `max_width`. \
440                            `{0}` will be set to the same value as `max_width`",
441                            config_key,
442                        );
443                        return max_width;
444                    }
445                    override_value
446                };
447
448                let fn_call_width = get_width_value(
449                    self.was_set().fn_call_width(),
450                    self.fn_call_width.2,
451                    heuristics.fn_call_width,
452                    "fn_call_width",
453                );
454                self.fn_call_width.2 = fn_call_width;
455
456                let attr_fn_like_width = get_width_value(
457                    self.was_set().attr_fn_like_width(),
458                    self.attr_fn_like_width.2,
459                    heuristics.attr_fn_like_width,
460                    "attr_fn_like_width",
461                );
462                self.attr_fn_like_width.2 = attr_fn_like_width;
463
464                let struct_lit_width = get_width_value(
465                    self.was_set().struct_lit_width(),
466                    self.struct_lit_width.2,
467                    heuristics.struct_lit_width,
468                    "struct_lit_width",
469                );
470                self.struct_lit_width.2 = struct_lit_width;
471
472                let struct_variant_width = get_width_value(
473                    self.was_set().struct_variant_width(),
474                    self.struct_variant_width.2,
475                    heuristics.struct_variant_width,
476                    "struct_variant_width",
477                );
478                self.struct_variant_width.2 = struct_variant_width;
479
480                let array_width = get_width_value(
481                    self.was_set().array_width(),
482                    self.array_width.2,
483                    heuristics.array_width,
484                    "array_width",
485                );
486                self.array_width.2 = array_width;
487
488                let chain_width = get_width_value(
489                    self.was_set().chain_width(),
490                    self.chain_width.2,
491                    heuristics.chain_width,
492                    "chain_width",
493                );
494                self.chain_width.2 = chain_width;
495
496                let single_line_if_else_max_width = get_width_value(
497                    self.was_set().single_line_if_else_max_width(),
498                    self.single_line_if_else_max_width.2,
499                    heuristics.single_line_if_else_max_width,
500                    "single_line_if_else_max_width",
501                );
502                self.single_line_if_else_max_width.2 = single_line_if_else_max_width;
503
504                let single_line_let_else_max_width = get_width_value(
505                    self.was_set().single_line_let_else_max_width(),
506                    self.single_line_let_else_max_width.2,
507                    heuristics.single_line_let_else_max_width,
508                    "single_line_let_else_max_width",
509                );
510                self.single_line_let_else_max_width.2 = single_line_let_else_max_width;
511            }
512
513            fn set_heuristics(&mut self) {
514                let max_width = self.max_width.2;
515                match self.use_small_heuristics.2 {
516                    Heuristics::Default =>
517                        self.set_width_heuristics(WidthHeuristics::scaled(max_width)),
518                    Heuristics::Max => self.set_width_heuristics(WidthHeuristics::set(max_width)),
519                    Heuristics::Off => self.set_width_heuristics(WidthHeuristics::null()),
520                };
521            }
522
523            fn set_ignore(&mut self, dir: &Path) {
524                self.ignore.2.add_prefix(dir);
525            }
526
527            fn set_merge_imports(&mut self) {
528                if self.was_set().merge_imports() {
529                    eprintln!(
530                        "Warning: the `merge_imports` option is deprecated. \
531                        Use `imports_granularity=\"Crate\"` instead"
532                    );
533                    if !self.was_set().imports_granularity() {
534                        self.imports_granularity.2 = if self.merge_imports() {
535                            ImportGranularity::Crate
536                        } else {
537                            ImportGranularity::Preserve
538                        };
539                    }
540                }
541            }
542
543            fn set_fn_args_layout(&mut self) {
544                if self.was_set().fn_args_layout() {
545                    eprintln!(
546                        "Warning: the `fn_args_layout` option is deprecated. \
547                        Use `fn_params_layout`. instead"
548                    );
549                    if !self.was_set().fn_params_layout() {
550                        self.fn_params_layout.2 = self.fn_args_layout();
551                    }
552                }
553            }
554
555            fn set_hide_parse_errors(&mut self) {
556                if self.was_set().hide_parse_errors() {
557                    eprintln!(
558                        "Warning: the `hide_parse_errors` option is deprecated. \
559                        Use `show_parse_errors` instead"
560                    );
561                    if !self.was_set().show_parse_errors() {
562                        self.show_parse_errors.2 = self.hide_parse_errors();
563                    }
564                }
565            }
566
567            fn set_version(&mut self) {
568                if !self.was_set().version() {
569                    return;
570                }
571
572                eprintln!(
573                    "Warning: the `version` option is deprecated. \
574                    Use `style_edition` instead."
575                );
576
577                if self.was_set().style_edition() || self.was_set_cli().style_edition() {
578                    eprintln!(
579                        "Warning: the deprecated `version` option was \
580                        used in conjunction with the `style_edition` \
581                        option which takes precedence. \
582                        The value of the `version` option will be ignored."
583                    );
584                }
585            }
586
587            #[allow(unreachable_pub)]
588            /// Returns `true` if the config key was explicitly set and is the default value.
589            pub fn is_default(&self, key: &str) -> bool {
590                let style_edition = StyleEdition::Edition2015;
591                $(
592                    let default_value = <$ty as StyleEditionDefault>::style_edition_default(
593                        style_edition
594                    );
595                    if let stringify!($i) = key {
596                        return self.$i.1 && self.$i.2 == default_value;
597                    }
598                 )+
599                false
600            }
601        }
602
603        // Template for the default configuration
604        impl Default for Config {
605            fn default() -> Config {
606                Config::default_with_style_edition(StyleEdition::Edition2015)
607            }
608        }
609    )
610}
611
612pub(crate) fn is_stable_option_and_value<T>(
613    option_name: &str,
614    option_stable: bool,
615    option_value: &T,
616) -> bool
617where
618    T: PartialEq + std::fmt::Debug + ConfigType,
619{
620    let nightly = crate::is_nightly_channel!();
621    let variant_stable = option_value.stable_variant();
622    match (nightly, option_stable, variant_stable) {
623        // Stable with an unstable option
624        (false, false, _) => {
625            eprintln!(
626                "Warning: can't set `{option_name} = {option_value:?}`, unstable features are only \
627                       available in nightly channel."
628            );
629            false
630        }
631        // Stable with a stable option, but an unstable variant
632        (false, true, false) => {
633            eprintln!(
634                "Warning: can't set `{option_name} = {option_value:?}`, unstable variants are only \
635                       available in nightly channel."
636            );
637            false
638        }
639        // Nightly: everything allowed
640        // Stable with stable option and variant: allowed
641        (true, _, _) | (false, true, true) => true,
642    }
643}