]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/utils/conf.rs
New upstream version 1.55.0+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
17df50a5
XL
18/// Conf with parse errors
19#[derive(Default)]
20pub struct TryConf {
21 pub conf: Conf,
22 pub errors: Vec<String>,
f20569fa
XL
23}
24
17df50a5
XL
25impl TryConf {
26 fn from_error(error: impl Error) -> Self {
27 Self {
28 conf: Conf::default(),
29 errors: vec![error.to_string()],
f20569fa
XL
30 }
31 }
32}
33
136023e0
XL
34/// Note that the configuration parsing currently doesn't support documentation that will
35/// that spans over several lines. This will be possible with the new implementation
36/// See (rust-clippy#7172)
17df50a5
XL
37macro_rules! define_Conf {
38 ($(
39 #[doc = $doc:literal]
40 $(#[conf_deprecated($dep:literal)])?
41 ($name:ident: $ty:ty = $default:expr),
42 )*) => {
43 /// Clippy lint configuration
44 pub struct Conf {
45 $(#[doc = $doc] pub $name: $ty,)*
46 }
f20569fa 47
17df50a5
XL
48 mod defaults {
49 $(pub fn $name() -> $ty { $default })*
50 }
f20569fa 51
17df50a5
XL
52 impl Default for Conf {
53 fn default() -> Self {
54 Self { $($name: defaults::$name(),)* }
f20569fa 55 }
17df50a5
XL
56 }
57
58 impl<'de> Deserialize<'de> for TryConf {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
60 deserializer.deserialize_map(ConfVisitor)
61 }
62 }
f20569fa 63
17df50a5
XL
64 #[derive(Deserialize)]
65 #[serde(field_identifier, rename_all = "kebab-case")]
66 #[allow(non_camel_case_types)]
67 enum Field { $($name,)* third_party, }
f20569fa 68
17df50a5
XL
69 struct ConfVisitor;
70
71 impl<'de> Visitor<'de> for ConfVisitor {
72 type Value = TryConf;
73
74 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
75 formatter.write_str("Conf")
76 }
77
78 fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
79 let mut errors = Vec::new();
80 $(let mut $name = None;)*
81 // could get `Field` here directly, but get `str` first for diagnostics
82 while let Some(name) = map.next_key::<&str>()? {
83 match Field::deserialize(name.into_deserializer())? {
84 $(Field::$name => {
85 $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
86 match map.next_value() {
87 Err(e) => errors.push(e.to_string()),
88 Ok(value) => match $name {
89 Some(_) => errors.push(format!("duplicate field `{}`", name)),
90 None => $name = Some(value),
91 }
92 }
93 })*
94 // white-listed; ignore
95 Field::third_party => drop(map.next_value::<IgnoredAny>())
f20569fa
XL
96 }
97 }
17df50a5
XL
98 let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
99 Ok(TryConf { conf, errors })
100 }
101 }
f20569fa 102
17df50a5
XL
103 #[cfg(feature = "metadata-collector-lint")]
104 pub mod metadata {
105 use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
106
107 macro_rules! wrap_option {
108 () => (None);
109 ($x:literal) => (Some($x));
110 }
111
112 pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
113 vec![
114 $(
115 {
116 let deprecation_reason = wrap_option!($($dep)?);
117
118 ClippyConfiguration::new(
119 stringify!($name),
120 stringify!($ty),
121 format!("{:?}", super::defaults::$name()),
122 $doc,
123 deprecation_reason,
124 )
125 },
126 )+
127 ]
128 }
f20569fa
XL
129 }
130 };
131}
132
17df50a5 133// N.B., this macro is parsed by util/lintlib.py
f20569fa 134define_Conf! {
17df50a5
XL
135 /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. Suppress lints whenever the suggested change would cause breakage for other crates.
136 (avoid_breaking_exported_api: bool = true),
137 /// Lint: 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. The minimum rust version that the project supports
138 (msrv: Option<String> = None),
f20569fa 139 /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
17df50a5 140 (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
f20569fa 141 /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
17df50a5 142 (cognitive_complexity_threshold: u64 = 25),
f20569fa 143 /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
17df50a5
XL
144 #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
145 (cyclomatic_complexity_threshold: Option<u64> = None),
f20569fa 146 /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
17df50a5 147 (doc_valid_idents: Vec<String> = [
f20569fa
XL
148 "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
149 "DirectX",
150 "ECMAScript",
151 "GPLv2", "GPLv3",
152 "GitHub", "GitLab",
153 "IPv4", "IPv6",
154 "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
155 "NaN", "NaNs",
156 "OAuth", "GraphQL",
157 "OCaml",
158 "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
159 "WebGL",
160 "TensorFlow",
161 "TrueType",
136023e0 162 "iOS", "macOS", "FreeBSD",
f20569fa
XL
163 "TeX", "LaTeX", "BibTeX", "BibLaTeX",
164 "MinGW",
165 "CamelCase",
166 ].iter().map(ToString::to_string).collect()),
167 /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
17df50a5 168 (too_many_arguments_threshold: u64 = 7),
f20569fa 169 /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
17df50a5 170 (type_complexity_threshold: u64 = 250),
f20569fa 171 /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
17df50a5 172 (single_char_binding_names_threshold: u64 = 4),
f20569fa 173 /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
17df50a5 174 (too_large_for_stack: u64 = 200),
f20569fa 175 /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
17df50a5 176 (enum_variant_name_threshold: u64 = 3),
f20569fa 177 /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
17df50a5 178 (enum_variant_size_threshold: u64 = 200),
f20569fa 179 /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
17df50a5 180 (verbose_bit_mask_threshold: u64 = 1),
f20569fa 181 /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
17df50a5 182 (literal_representation_threshold: u64 = 16384),
f20569fa 183 /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
17df50a5 184 (trivial_copy_size_limit: Option<u64> = None),
f20569fa 185 /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
17df50a5 186 (pass_by_value_size_limit: u64 = 256),
f20569fa 187 /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
17df50a5 188 (too_many_lines_threshold: u64 = 100),
f20569fa 189 /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
17df50a5 190 (array_size_threshold: u64 = 512_000),
f20569fa 191 /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
17df50a5 192 (vec_box_size_threshold: u64 = 4096),
f20569fa 193 /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
17df50a5 194 (max_trait_bounds: u64 = 3),
136023e0 195 /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bool fields a struct can have
17df50a5 196 (max_struct_bools: u64 = 3),
136023e0 197 /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bool parameters a function can have
17df50a5 198 (max_fn_params_bools: u64 = 3),
f20569fa 199 /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
17df50a5 200 (warn_on_all_wildcard_imports: bool = false),
f20569fa 201 /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
17df50a5 202 (disallowed_methods: Vec<String> = Vec::new()),
136023e0
XL
203 /// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths.
204 (disallowed_types: Vec<String> = Vec::new()),
f20569fa 205 /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
17df50a5 206 (unreadable_literal_lint_fractions: bool = true),
f20569fa 207 /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
17df50a5 208 (upper_case_acronyms_aggressive: bool = false),
f20569fa 209 /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
17df50a5 210 (cargo_ignore_publish: bool = false),
136023e0
XL
211 /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified. <br> A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name.
212 (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
213 /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename.
214 (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
215 /// Lint: RESTRICTED_SCRIPTS. The list of unicode scripts allowed to be used in the scope.
216 (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
f20569fa
XL
217}
218
219/// Search for the configuration file.
220pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
221 /// Possible filename to search for.
222 const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
223
224 // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
225 // If neither of those exist, use ".".
226 let mut current = env::var_os("CLIPPY_CONF_DIR")
227 .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
228 .map_or_else(|| PathBuf::from("."), PathBuf::from);
229 loop {
230 for config_file_name in &CONFIG_FILE_NAMES {
17df50a5
XL
231 if let Ok(config_file) = current.join(config_file_name).canonicalize() {
232 match fs::metadata(&config_file) {
233 Err(e) if e.kind() == io::ErrorKind::NotFound => {},
234 Err(e) => return Err(e),
235 Ok(md) if md.is_dir() => {},
236 Ok(_) => return Ok(Some(config_file)),
237 }
f20569fa
XL
238 }
239 }
240
241 // If the current directory has no parent, we're done searching.
242 if !current.pop() {
243 return Ok(None);
244 }
245 }
246}
247
f20569fa
XL
248/// Read the `toml` configuration file.
249///
250/// In case of error, the function tries to continue as much as possible.
17df50a5 251pub fn read(path: &Path) -> TryConf {
f20569fa 252 let content = match fs::read_to_string(path) {
17df50a5 253 Err(e) => return TryConf::from_error(e),
f20569fa 254 Ok(content) => content,
f20569fa 255 };
17df50a5 256 toml::from_str(&content).unwrap_or_else(TryConf::from_error)
f20569fa 257}