]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/utils/conf.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / utils / conf.rs
CommitLineData
f20569fa
XL
1//! Read configurations files.
2
17df50a5 3#![allow(clippy::module_name_repetitions)]
f20569fa 4
17df50a5
XL
5use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
6use serde::Deserialize;
7use std::error::Error;
f20569fa 8use std::path::{Path, PathBuf};
f20569fa
XL
9use std::{env, fmt, fs, io};
10
136023e0
XL
11/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
12#[derive(Clone, Debug, Deserialize)]
13pub struct Rename {
14 pub path: String,
15 pub rename: String,
16}
17
a2a8927a 18/// A single disallowed method, used by the `DISALLOWED_METHODS` lint.
c295e0f8
XL
19#[derive(Clone, Debug, Deserialize)]
20#[serde(untagged)]
21pub enum DisallowedMethod {
22 Simple(String),
23 WithReason { path: String, reason: Option<String> },
24}
25
5099ac24
FG
26impl DisallowedMethod {
27 pub fn path(&self) -> &str {
28 let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
29
30 path
31 }
32}
33
a2a8927a 34/// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
3c0e092e
XL
35#[derive(Clone, Debug, Deserialize)]
36#[serde(untagged)]
37pub enum DisallowedType {
38 Simple(String),
39 WithReason { path: String, reason: Option<String> },
40}
41
17df50a5
XL
42/// Conf with parse errors
43#[derive(Default)]
44pub struct TryConf {
45 pub conf: Conf,
46 pub errors: Vec<String>,
f20569fa
XL
47}
48
17df50a5
XL
49impl TryConf {
50 fn from_error(error: impl Error) -> Self {
51 Self {
52 conf: Conf::default(),
53 errors: vec![error.to_string()],
f20569fa
XL
54 }
55 }
56}
57
17df50a5
XL
58macro_rules! define_Conf {
59 ($(
94222f64 60 $(#[doc = $doc:literal])+
17df50a5
XL
61 $(#[conf_deprecated($dep:literal)])?
62 ($name:ident: $ty:ty = $default:expr),
63 )*) => {
64 /// Clippy lint configuration
65 pub struct Conf {
94222f64 66 $($(#[doc = $doc])+ pub $name: $ty,)*
17df50a5 67 }
f20569fa 68
17df50a5
XL
69 mod defaults {
70 $(pub fn $name() -> $ty { $default })*
71 }
f20569fa 72
17df50a5
XL
73 impl Default for Conf {
74 fn default() -> Self {
75 Self { $($name: defaults::$name(),)* }
f20569fa 76 }
17df50a5
XL
77 }
78
79 impl<'de> Deserialize<'de> for TryConf {
80 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
81 deserializer.deserialize_map(ConfVisitor)
82 }
83 }
f20569fa 84
17df50a5
XL
85 #[derive(Deserialize)]
86 #[serde(field_identifier, rename_all = "kebab-case")]
87 #[allow(non_camel_case_types)]
88 enum Field { $($name,)* third_party, }
f20569fa 89
17df50a5
XL
90 struct ConfVisitor;
91
92 impl<'de> Visitor<'de> for ConfVisitor {
93 type Value = TryConf;
94
95 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
96 formatter.write_str("Conf")
97 }
98
99 fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
100 let mut errors = Vec::new();
101 $(let mut $name = None;)*
102 // could get `Field` here directly, but get `str` first for diagnostics
103 while let Some(name) = map.next_key::<&str>()? {
104 match Field::deserialize(name.into_deserializer())? {
105 $(Field::$name => {
106 $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
107 match map.next_value() {
108 Err(e) => errors.push(e.to_string()),
109 Ok(value) => match $name {
110 Some(_) => errors.push(format!("duplicate field `{}`", name)),
111 None => $name = Some(value),
112 }
113 }
114 })*
115 // white-listed; ignore
116 Field::third_party => drop(map.next_value::<IgnoredAny>())
f20569fa
XL
117 }
118 }
17df50a5
XL
119 let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
120 Ok(TryConf { conf, errors })
121 }
122 }
f20569fa 123
5099ac24 124 #[cfg(feature = "internal")]
17df50a5
XL
125 pub mod metadata {
126 use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
127
128 macro_rules! wrap_option {
129 () => (None);
130 ($x:literal) => (Some($x));
131 }
132
133 pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
134 vec![
135 $(
136 {
137 let deprecation_reason = wrap_option!($($dep)?);
138
139 ClippyConfiguration::new(
140 stringify!($name),
141 stringify!($ty),
142 format!("{:?}", super::defaults::$name()),
94222f64 143 concat!($($doc, '\n',)*),
17df50a5
XL
144 deprecation_reason,
145 )
146 },
147 )+
148 ]
149 }
f20569fa
XL
150 }
151 };
152}
153
f20569fa 154define_Conf! {
c295e0f8 155 /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
94222f64
XL
156 ///
157 /// Suppress lints whenever the suggested change would cause breakage for other crates.
17df50a5 158 (avoid_breaking_exported_api: bool = true),
04454e1e 159 /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED.
94222f64
XL
160 ///
161 /// The minimum rust version that the project supports
17df50a5 162 (msrv: Option<String> = None),
94222f64
XL
163 /// Lint: BLACKLISTED_NAME.
164 ///
165 /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
17df50a5 166 (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
94222f64
XL
167 /// Lint: COGNITIVE_COMPLEXITY.
168 ///
169 /// The maximum cognitive complexity a function can have
17df50a5 170 (cognitive_complexity_threshold: u64 = 25),
94222f64
XL
171 /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
172 ///
173 /// Use the Cognitive Complexity lint instead.
17df50a5
XL
174 #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
175 (cyclomatic_complexity_threshold: Option<u64> = None),
94222f64
XL
176 /// Lint: DOC_MARKDOWN.
177 ///
178 /// The list of words this lint should not consider as identifiers needing ticks
17df50a5 179 (doc_valid_idents: Vec<String> = [
f20569fa
XL
180 "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
181 "DirectX",
182 "ECMAScript",
183 "GPLv2", "GPLv3",
184 "GitHub", "GitLab",
185 "IPv4", "IPv6",
186 "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
187 "NaN", "NaNs",
188 "OAuth", "GraphQL",
189 "OCaml",
190 "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
191 "WebGL",
192 "TensorFlow",
193 "TrueType",
136023e0 194 "iOS", "macOS", "FreeBSD",
f20569fa
XL
195 "TeX", "LaTeX", "BibTeX", "BibLaTeX",
196 "MinGW",
197 "CamelCase",
198 ].iter().map(ToString::to_string).collect()),
94222f64
XL
199 /// Lint: TOO_MANY_ARGUMENTS.
200 ///
201 /// The maximum number of argument a function or method can have
17df50a5 202 (too_many_arguments_threshold: u64 = 7),
94222f64
XL
203 /// Lint: TYPE_COMPLEXITY.
204 ///
205 /// The maximum complexity a type can have
17df50a5 206 (type_complexity_threshold: u64 = 250),
94222f64
XL
207 /// Lint: MANY_SINGLE_CHAR_NAMES.
208 ///
209 /// The maximum number of single char bindings a scope may have
17df50a5 210 (single_char_binding_names_threshold: u64 = 4),
94222f64
XL
211 /// Lint: BOXED_LOCAL, USELESS_VEC.
212 ///
213 /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
17df50a5 214 (too_large_for_stack: u64 = 200),
94222f64
XL
215 /// Lint: ENUM_VARIANT_NAMES.
216 ///
217 /// The minimum number of enum variants for the lints about variant names to trigger
17df50a5 218 (enum_variant_name_threshold: u64 = 3),
94222f64
XL
219 /// Lint: LARGE_ENUM_VARIANT.
220 ///
221 /// The maximum size of an enum's variant to avoid box suggestion
17df50a5 222 (enum_variant_size_threshold: u64 = 200),
94222f64
XL
223 /// Lint: VERBOSE_BIT_MASK.
224 ///
225 /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
17df50a5 226 (verbose_bit_mask_threshold: u64 = 1),
94222f64
XL
227 /// Lint: DECIMAL_LITERAL_REPRESENTATION.
228 ///
229 /// The lower bound for linting decimal literals
17df50a5 230 (literal_representation_threshold: u64 = 16384),
94222f64
XL
231 /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
232 ///
233 /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
17df50a5 234 (trivial_copy_size_limit: Option<u64> = None),
94222f64
XL
235 /// Lint: LARGE_TYPE_PASS_BY_MOVE.
236 ///
237 /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
17df50a5 238 (pass_by_value_size_limit: u64 = 256),
94222f64
XL
239 /// Lint: TOO_MANY_LINES.
240 ///
241 /// The maximum number of lines a function or method can have
17df50a5 242 (too_many_lines_threshold: u64 = 100),
94222f64
XL
243 /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
244 ///
245 /// The maximum allowed size for arrays on the stack
17df50a5 246 (array_size_threshold: u64 = 512_000),
94222f64
XL
247 /// Lint: VEC_BOX.
248 ///
249 /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
17df50a5 250 (vec_box_size_threshold: u64 = 4096),
94222f64
XL
251 /// Lint: TYPE_REPETITION_IN_BOUNDS.
252 ///
253 /// The maximum number of bounds a trait can have to be linted
17df50a5 254 (max_trait_bounds: u64 = 3),
94222f64
XL
255 /// Lint: STRUCT_EXCESSIVE_BOOLS.
256 ///
257 /// The maximum number of bool fields a struct can have
17df50a5 258 (max_struct_bools: u64 = 3),
94222f64
XL
259 /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
260 ///
261 /// The maximum number of bool parameters a function can have
17df50a5 262 (max_fn_params_bools: u64 = 3),
94222f64
XL
263 /// Lint: WILDCARD_IMPORTS.
264 ///
265 /// Whether to allow certain wildcard imports (prelude, super in tests).
17df50a5 266 (warn_on_all_wildcard_imports: bool = false),
a2a8927a 267 /// Lint: DISALLOWED_METHODS.
94222f64
XL
268 ///
269 /// The list of disallowed methods, written as fully qualified paths.
c295e0f8 270 (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
a2a8927a 271 /// Lint: DISALLOWED_TYPES.
94222f64
XL
272 ///
273 /// The list of disallowed types, written as fully qualified paths.
3c0e092e 274 (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
94222f64
XL
275 /// Lint: UNREADABLE_LITERAL.
276 ///
277 /// Should the fraction of a decimal be linted to include separators.
17df50a5 278 (unreadable_literal_lint_fractions: bool = true),
94222f64
XL
279 /// Lint: UPPER_CASE_ACRONYMS.
280 ///
281 /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
17df50a5 282 (upper_case_acronyms_aggressive: bool = false),
94222f64
XL
283 /// Lint: _CARGO_COMMON_METADATA.
284 ///
285 /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
17df50a5 286 (cargo_ignore_publish: bool = false),
94222f64
XL
287 /// Lint: NONSTANDARD_MACRO_BRACES.
288 ///
289 /// Enforce the named macros always use the braces specified.
290 ///
291 /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
292 /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
293 /// `crate_name::macro_name` and one with just the macro name.
136023e0 294 (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
94222f64
XL
295 /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
296 ///
297 /// The list of imports to always rename, a fully qualified path followed by the rename.
136023e0 298 (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
3c0e092e 299 /// Lint: DISALLOWED_SCRIPT_IDENTS.
94222f64
XL
300 ///
301 /// The list of unicode scripts allowed to be used in the scope.
3c0e092e 302 (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()),
c295e0f8
XL
303 /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
304 ///
305 /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
306 (enable_raw_pointer_heuristic_for_send: bool = true),
a2a8927a
XL
307 /// Lint: INDEX_REFUTABLE_SLICE.
308 ///
309 /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
310 /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
311 /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
312 (max_suggested_slice_pattern_length: u64 = 3),
04454e1e
FG
313 /// Lint: AWAIT_HOLDING_INVALID_TYPE
314 (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
315 /// Lint: LARGE_INCLUDE_FILE.
316 ///
317 /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
318 (max_include_file_size: u64 = 1_000_000),
f20569fa
XL
319}
320
321/// Search for the configuration file.
322pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
323 /// Possible filename to search for.
324 const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
325
326 // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
327 // If neither of those exist, use ".".
328 let mut current = env::var_os("CLIPPY_CONF_DIR")
329 .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
330 .map_or_else(|| PathBuf::from("."), PathBuf::from);
5099ac24
FG
331
332 let mut found_config: Option<PathBuf> = None;
333
f20569fa
XL
334 loop {
335 for config_file_name in &CONFIG_FILE_NAMES {
17df50a5
XL
336 if let Ok(config_file) = current.join(config_file_name).canonicalize() {
337 match fs::metadata(&config_file) {
338 Err(e) if e.kind() == io::ErrorKind::NotFound => {},
339 Err(e) => return Err(e),
340 Ok(md) if md.is_dir() => {},
5099ac24
FG
341 Ok(_) => {
342 // warn if we happen to find two config files #8323
343 if let Some(ref found_config_) = found_config {
344 eprintln!(
345 "Using config file `{}`\nWarning: `{}` will be ignored.",
346 found_config_.display(),
347 config_file.display(),
348 );
349 } else {
350 found_config = Some(config_file);
351 }
352 },
17df50a5 353 }
f20569fa
XL
354 }
355 }
356
5099ac24
FG
357 if found_config.is_some() {
358 return Ok(found_config);
359 }
360
f20569fa
XL
361 // If the current directory has no parent, we're done searching.
362 if !current.pop() {
363 return Ok(None);
364 }
365 }
366}
367
f20569fa
XL
368/// Read the `toml` configuration file.
369///
370/// In case of error, the function tries to continue as much as possible.
17df50a5 371pub fn read(path: &Path) -> TryConf {
f20569fa 372 let content = match fs::read_to_string(path) {
17df50a5 373 Err(e) => return TryConf::from_error(e),
f20569fa 374 Ok(content) => content,
f20569fa 375 };
17df50a5 376 toml::from_str(&content).unwrap_or_else(TryConf::from_error)
f20569fa 377}