]> git.proxmox.com Git - rustc.git/blame - src/tools/rustfmt/src/config/config_type.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / src / tools / rustfmt / src / config / config_type.rs
CommitLineData
f20569fa
XL
1use crate::config::file_lines::FileLines;
2use crate::config::options::{IgnoreList, WidthHeuristics};
3
4/// Trait for types that can be used in `Config`.
5pub(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
11impl ConfigType for bool {
12 fn doc_hint() -> String {
13 String::from("<boolean>")
14 }
15}
16
17impl ConfigType for usize {
18 fn doc_hint() -> String {
19 String::from("<unsigned integer>")
20 }
21}
22
23impl ConfigType for isize {
24 fn doc_hint() -> String {
25 String::from("<signed integer>")
26 }
27}
28
29impl ConfigType for String {
30 fn doc_hint() -> String {
31 String::from("<string>")
32 }
33}
34
35impl ConfigType for FileLines {
36 fn doc_hint() -> String {
37 String::from("<json>")
38 }
39}
40
41impl ConfigType for WidthHeuristics {
42 fn doc_hint() -> String {
43 String::new()
44 }
45}
46
47impl ConfigType for IgnoreList {
48 fn doc_hint() -> String {
49 String::from("[<string>,..]")
50 }
51}
52
53macro_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 {
f20569fa
XL
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) {
cdc7bbd5
XL
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(),
f20569fa
XL
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();
f20569fa
XL
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 {
cdc7bbd5
XL
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(),
f20569fa
XL
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
cdc7bbd5
XL
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
f20569fa 373 fn set_heuristics(&mut self) {
cdc7bbd5
XL
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 };
f20569fa
XL
381 }
382
f20569fa
XL
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. \
94222f64 391 Use `imports_granularity=\"Crate\"` instead"
f20569fa
XL
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 {
f20569fa
XL
419 $(
420 $i: (Cell::new(false), false, $def, $stb),
421 )+
422 }
423 }
424 }
425 )
426}