]> git.lizzy.rs Git - rust.git/blob - src/config/options.rs
warn on use of default value for an option
[rust.git] / src / config / options.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use config::config_type::ConfigType;
12 use config::lists::*;
13 use config::{Config, FileName};
14
15 use isatty::stdout_isatty;
16
17 use std::collections::HashSet;
18 use std::path::{Path, PathBuf};
19
20 /// Macro for deriving implementations of Serialize/Deserialize for enums
21 #[macro_export]
22 macro_rules! impl_enum_serialize_and_deserialize {
23     ( $e:ident, $( $x:ident ),* ) => {
24         impl ::serde::ser::Serialize for $e {
25             fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26                 where S: ::serde::ser::Serializer
27             {
28                 use serde::ser::Error;
29
30                 // We don't know whether the user of the macro has given us all options.
31                 #[allow(unreachable_patterns)]
32                 match *self {
33                     $(
34                         $e::$x => serializer.serialize_str(stringify!($x)),
35                     )*
36                     _ => {
37                         Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
38                     }
39                 }
40             }
41         }
42
43         impl<'de> ::serde::de::Deserialize<'de> for $e {
44             fn deserialize<D>(d: D) -> Result<Self, D::Error>
45                     where D: ::serde::Deserializer<'de> {
46                 use serde::de::{Error, Visitor};
47                 use std::marker::PhantomData;
48                 use std::fmt;
49                 struct StringOnly<T>(PhantomData<T>);
50                 impl<'de, T> Visitor<'de> for StringOnly<T>
51                         where T: ::serde::Deserializer<'de> {
52                     type Value = String;
53                     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54                         formatter.write_str("string")
55                     }
56                     fn visit_str<E>(self, value: &str) -> Result<String, E> {
57                         Ok(String::from(value))
58                     }
59                 }
60                 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
61                 $(
62                     if stringify!($x).eq_ignore_ascii_case(&s) {
63                       return Ok($e::$x);
64                     }
65                 )*
66                 static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
67                 Err(D::Error::unknown_variant(&s, ALLOWED))
68             }
69         }
70
71         impl ::std::str::FromStr for $e {
72             type Err = &'static str;
73
74             fn from_str(s: &str) -> Result<Self, Self::Err> {
75                 $(
76                     if stringify!($x).eq_ignore_ascii_case(s) {
77                         return Ok($e::$x);
78                     }
79                 )*
80                 Err("Bad variant")
81             }
82         }
83
84         impl ConfigType for $e {
85             fn doc_hint() -> String {
86                 let mut variants = Vec::new();
87                 $(
88                     variants.push(stringify!($x));
89                 )*
90                 format!("[{}]", variants.join("|"))
91             }
92         }
93     };
94 }
95
96 macro_rules! configuration_option_enum{
97     ($e:ident: $( $x:ident ),+ $(,)*) => {
98         #[derive(Copy, Clone, Eq, PartialEq, Debug)]
99         pub enum $e {
100             $( $x ),+
101         }
102
103         impl_enum_serialize_and_deserialize!($e, $( $x ),+);
104     }
105 }
106
107 configuration_option_enum! { NewlineStyle:
108     Windows, // \r\n
109     Unix, // \n
110     Native, // \r\n in Windows, \n on other platforms
111 }
112
113 configuration_option_enum! { BraceStyle:
114     AlwaysNextLine,
115     PreferSameLine,
116     // Prefer same line except where there is a where clause, in which case force
117     // the brace to the next line.
118     SameLineWhere,
119 }
120
121 configuration_option_enum! { ControlBraceStyle:
122     // K&R style, Rust community default
123     AlwaysSameLine,
124     // Stroustrup style
125     ClosingNextLine,
126     // Allman style
127     AlwaysNextLine,
128 }
129
130 configuration_option_enum! { IndentStyle:
131     // First line on the same line as the opening brace, all lines aligned with
132     // the first line.
133     Visual,
134     // First line is on a new line and all lines align with block indent.
135     Block,
136 }
137
138 configuration_option_enum! { Density:
139     // Fit as much on one line as possible.
140     Compressed,
141     // Use more lines.
142     Tall,
143     // Place every item on a separate line.
144     Vertical,
145 }
146
147 configuration_option_enum! { TypeDensity:
148     // No spaces around "=" and "+"
149     Compressed,
150     // Spaces around " = " and " + "
151     Wide,
152 }
153
154 configuration_option_enum! { Heuristics:
155     // Turn off any heuristics
156     Off,
157     // Use Rustfmt's defaults
158     Default,
159 }
160
161 impl Density {
162     pub fn to_list_tactic(self) -> ListTactic {
163         match self {
164             Density::Compressed => ListTactic::Mixed,
165             Density::Tall => ListTactic::HorizontalVertical,
166             Density::Vertical => ListTactic::Vertical,
167         }
168     }
169 }
170
171 configuration_option_enum! { ReportTactic:
172     Always,
173     Unnumbered,
174     Never,
175 }
176
177 // What Rustfmt should emit. Mostly corresponds to the `--emit` command line
178 // option.
179 configuration_option_enum! { EmitMode:
180     // Emits to files.
181     Files,
182     // Writes the output to stdout.
183     Stdout,
184     // Displays how much of the input file was processed
185     Coverage,
186     // Unfancy stdout
187     Checkstyle,
188     // Output the changed lines (for internal value only)
189     ModifiedLines,
190     // Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
191     // This option is designed to be run in CI where a non-zero exit signifies non-standard code
192     // formatting. Used for `--check`.
193     Diff,
194 }
195
196 // Client-preference for coloured output.
197 configuration_option_enum! { Color:
198     // Always use color, whether it is a piped or terminal output
199     Always,
200     // Never use color
201     Never,
202     // Automatically use color, if supported by terminal
203     Auto,
204 }
205
206 impl Color {
207     /// Whether we should use a coloured terminal.
208     pub fn use_colored_tty(&self) -> bool {
209         match self {
210             Color::Always => true,
211             Color::Never => false,
212             Color::Auto => stdout_isatty(),
213         }
214     }
215 }
216
217 // How chatty should Rustfmt be?
218 configuration_option_enum! { Verbosity:
219     // Emit more.
220     Verbose,
221     Normal,
222     // Emit as little as possible.
223     Quiet,
224 }
225
226 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
227 pub struct WidthHeuristics {
228     // Maximum width of the args of a function call before falling back
229     // to vertical formatting.
230     pub fn_call_width: usize,
231     // Maximum width in the body of a struct lit before falling back to
232     // vertical formatting.
233     pub struct_lit_width: usize,
234     // Maximum width in the body of a struct variant before falling back
235     // to vertical formatting.
236     pub struct_variant_width: usize,
237     // Maximum width of an array literal before falling back to vertical
238     // formatting.
239     pub array_width: usize,
240     // Maximum length of a chain to fit on a single line.
241     pub chain_width: usize,
242     // Maximum line length for single line if-else expressions. A value
243     // of zero means always break if-else expressions.
244     pub single_line_if_else_max_width: usize,
245 }
246
247 impl WidthHeuristics {
248     // Using this WidthHeuristics means we ignore heuristics.
249     pub fn null() -> WidthHeuristics {
250         WidthHeuristics {
251             fn_call_width: usize::max_value(),
252             struct_lit_width: 0,
253             struct_variant_width: 0,
254             array_width: usize::max_value(),
255             chain_width: usize::max_value(),
256             single_line_if_else_max_width: 0,
257         }
258     }
259
260     // scale the default WidthHeuristics according to max_width
261     pub fn scaled(max_width: usize) -> WidthHeuristics {
262         const DEFAULT_MAX_WIDTH: usize = 100;
263         let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
264             let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
265             // round to the closest 0.1
266             (ratio * 10.0).round() / 10.0
267         } else {
268             1.0
269         };
270         WidthHeuristics {
271             fn_call_width: (60.0 * max_width_ratio).round() as usize,
272             struct_lit_width: (18.0 * max_width_ratio).round() as usize,
273             struct_variant_width: (35.0 * max_width_ratio).round() as usize,
274             array_width: (60.0 * max_width_ratio).round() as usize,
275             chain_width: (60.0 * max_width_ratio).round() as usize,
276             single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
277         }
278     }
279 }
280
281 impl ::std::str::FromStr for WidthHeuristics {
282     type Err = &'static str;
283
284     fn from_str(_: &str) -> Result<Self, Self::Err> {
285         Err("WidthHeuristics is not parsable")
286     }
287 }
288
289 impl Default for EmitMode {
290     fn default() -> EmitMode {
291         EmitMode::Files
292     }
293 }
294
295 /// A set of directories, files and modules that rustfmt should ignore.
296 #[derive(Default, Deserialize, Serialize, Clone, Debug, PartialEq)]
297 pub struct IgnoreList(HashSet<PathBuf>);
298
299 impl IgnoreList {
300     pub fn add_prefix(&mut self, dir: &Path) {
301         self.0 = self
302             .0
303             .iter()
304             .map(|s| {
305                 if s.has_root() {
306                     s.clone()
307                 } else {
308                     let mut path = PathBuf::from(dir);
309                     path.push(s);
310                     path
311                 }
312             })
313             .collect();
314     }
315
316     fn skip_file_inner(&self, file: &Path) -> bool {
317         self.0.iter().any(|path| file.starts_with(path))
318     }
319
320     pub fn skip_file(&self, file: &FileName) -> bool {
321         if let FileName::Real(ref path) = file {
322             self.skip_file_inner(path)
323         } else {
324             false
325         }
326     }
327 }
328
329 impl ::std::str::FromStr for IgnoreList {
330     type Err = &'static str;
331
332     fn from_str(_: &str) -> Result<Self, Self::Err> {
333         Err("IgnoreList is not parsable")
334     }
335 }
336
337 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
338 /// values in a config with values from the command line.
339 pub trait CliOptions {
340     fn apply_to(self, config: &mut Config);
341     fn config_path(&self) -> Option<&Path>;
342 }