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