]> git.proxmox.com Git - rustc.git/blame - src/tools/rustfmt/src/config/config_type.rs
Update upstream source from tag 'upstream/1.52.1+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 {
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" | "use_small_heuristics" => self.0.set_heuristics(),
99 "license_template_path" => self.0.set_license_template(),
100 "merge_imports" => self.0.set_merge_imports(),
101 &_ => (),
102 }
103 }
104 )+
105 }
106
107 // Query each option, returns true if the user set the option, false if
108 // a default was used.
109 #[allow(unreachable_pub)]
110 pub struct ConfigWasSet<'a>(&'a Config);
111
112 impl<'a> ConfigWasSet<'a> {
113 $(
114 #[allow(unreachable_pub)]
115 pub fn $i(&self) -> bool {
116 (self.0).$i.1
117 }
118 )+
119 }
120
121 impl Config {
122 $(
123 #[allow(unreachable_pub)]
124 pub fn $i(&self) -> $ty {
125 self.$i.0.set(true);
126 self.$i.2.clone()
127 }
128 )+
129
130 #[allow(unreachable_pub)]
131 pub fn set(&mut self) -> ConfigSetter<'_> {
132 ConfigSetter(self)
133 }
134
135 #[allow(unreachable_pub)]
136 pub fn was_set(&self) -> ConfigWasSet<'_> {
137 ConfigWasSet(self)
138 }
139
140 fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
141 $(
142 if let Some(val) = parsed.$i {
143 if self.$i.3 {
144 self.$i.1 = true;
145 self.$i.2 = val;
146 } else {
147 if crate::is_nightly_channel!() {
148 self.$i.1 = true;
149 self.$i.2 = val;
150 } else {
151 eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \
152 available in nightly channel.", stringify!($i), val);
153 }
154 }
155 }
156 )+
157 self.set_heuristics();
158 self.set_license_template();
159 self.set_ignore(dir);
160 self.set_merge_imports();
161 self
162 }
163
164 /// Returns a hash set initialized with every user-facing config option name.
165 #[cfg(test)]
166 pub(crate) fn hash_set() -> HashSet<String> {
167 let mut hash_set = HashSet::new();
168 $(
169 hash_set.insert(stringify!($i).to_owned());
170 )+
171 hash_set
172 }
173
174 pub(crate) fn is_valid_name(name: &str) -> bool {
175 match name {
176 $(
177 stringify!($i) => true,
178 )+
179 _ => false,
180 }
181 }
182
183 #[allow(unreachable_pub)]
184 pub fn is_valid_key_val(key: &str, val: &str) -> bool {
185 match key {
186 $(
187 stringify!($i) => val.parse::<$ty>().is_ok(),
188 )+
189 _ => false,
190 }
191 }
192
193 #[allow(unreachable_pub)]
194 pub fn used_options(&self) -> PartialConfig {
195 PartialConfig {
196 $(
197 $i: if self.$i.0.get() {
198 Some(self.$i.2.clone())
199 } else {
200 None
201 },
202 )+
203 }
204 }
205
206 #[allow(unreachable_pub)]
207 pub fn all_options(&self) -> PartialConfig {
208 PartialConfig {
209 $(
210 $i: Some(self.$i.2.clone()),
211 )+
212 }
213 }
214
215 #[allow(unreachable_pub)]
216 pub fn override_value(&mut self, key: &str, val: &str)
217 {
218 match key {
219 $(
220 stringify!($i) => {
221 self.$i.1 = true;
222 self.$i.2 = val.parse::<$ty>()
223 .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
224 stringify!($i),
225 val,
226 stringify!($ty)));
227 }
228 )+
229 _ => panic!("Unknown config key in override: {}", key)
230 }
231
232 match key {
233 "max_width" | "use_small_heuristics" => self.set_heuristics(),
234 "license_template_path" => self.set_license_template(),
235 "merge_imports" => self.set_merge_imports(),
236 &_ => (),
237 }
238 }
239
240 #[allow(unreachable_pub)]
241 pub fn is_hidden_option(name: &str) -> bool {
242 const HIDE_OPTIONS: [&str; 5] =
243 ["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports"];
244 HIDE_OPTIONS.contains(&name)
245 }
246
247 #[allow(unreachable_pub)]
248 pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
249 use std::cmp;
250 let max = 0;
251 $( let max = cmp::max(max, stringify!($i).len()+1); )+
252 let space_str = " ".repeat(max);
253 writeln!(out, "Configuration Options:").unwrap();
254 $(
255 if $stb || include_unstable {
256 let name_raw = stringify!($i);
257
258 if !Config::is_hidden_option(name_raw) {
259 let mut name_out = String::with_capacity(max);
260 for _ in name_raw.len()..max-1 {
261 name_out.push(' ')
262 }
263 name_out.push_str(name_raw);
264 name_out.push(' ');
265 let mut default_str = format!("{}", $def);
266 if default_str.is_empty() {
267 default_str = String::from("\"\"");
268 }
269 writeln!(out,
270 "{}{} Default: {}{}",
271 name_out,
272 <$ty>::doc_hint(),
273 default_str,
274 if !$stb { " (unstable)" } else { "" }).unwrap();
275 $(
276 writeln!(out, "{}{}", space_str, $dstring).unwrap();
277 )+
278 writeln!(out).unwrap();
279 }
280 }
281 )+
282 }
283
284 fn set_heuristics(&mut self) {
285 if self.use_small_heuristics.2 == Heuristics::Default {
286 let max_width = self.max_width.2;
287 self.set().width_heuristics(WidthHeuristics::scaled(max_width));
288 } else if self.use_small_heuristics.2 == Heuristics::Max {
289 let max_width = self.max_width.2;
290 self.set().width_heuristics(WidthHeuristics::set(max_width));
291 } else {
292 self.set().width_heuristics(WidthHeuristics::null());
293 }
294 }
295
296 fn set_license_template(&mut self) {
297 if self.was_set().license_template_path() {
298 let lt_path = self.license_template_path();
299 if lt_path.len() > 0 {
300 match license::load_and_compile_template(&lt_path) {
301 Ok(re) => self.license_template = Some(re),
302 Err(msg) => eprintln!("Warning for license template file {:?}: {}",
303 lt_path, msg),
304 }
305 } else {
306 self.license_template = None;
307 }
308 }
309 }
310
311 fn set_ignore(&mut self, dir: &Path) {
312 self.ignore.2.add_prefix(dir);
313 }
314
315 fn set_merge_imports(&mut self) {
316 if self.was_set().merge_imports() {
317 eprintln!(
318 "Warning: the `merge_imports` option is deprecated. \
319 Use `imports_granularity=Crate` instead"
320 );
321 if !self.was_set().imports_granularity() {
322 self.imports_granularity.2 = if self.merge_imports() {
323 ImportGranularity::Crate
324 } else {
325 ImportGranularity::Preserve
326 };
327 }
328 }
329 }
330
331 #[allow(unreachable_pub)]
332 /// Returns `true` if the config key was explicitly set and is the default value.
333 pub fn is_default(&self, key: &str) -> bool {
334 $(
335 if let stringify!($i) = key {
336 return self.$i.1 && self.$i.2 == $def;
337 }
338 )+
339 false
340 }
341 }
342
343 // Template for the default configuration
344 impl Default for Config {
345 fn default() -> Config {
346 Config {
347 license_template: None,
348 $(
349 $i: (Cell::new(false), false, $def, $stb),
350 )+
351 }
352 }
353 }
354 )
355}