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