From 2bf8a6bc3c0c409aa209407b84987698eb882536 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 20 Apr 2025 21:37:31 -0400 Subject: [PATCH 1/2] Rewrite of the config parsing code * More precise spans in diagnostics * Allow multiple diagnostics for each config value * Ability to capture spans in the parsed config * Easier support of `..` in lists * Remove `clippy_utils` dependancy from `clippy_config` * Multi-column support for long suggestion lists --- Cargo.toml | 1 - book/src/development/adding_lints.md | 2 +- clippy_config/Cargo.toml | 6 +- clippy_config/src/conf.rs | 996 ++++++------------ clippy_config/src/de.rs | 645 ++++++++++++ clippy_config/src/lib.rs | 9 +- clippy_config/src/metadata.rs | 58 +- clippy_config/src/types.rs | 893 ++++++++++------ clippy_dev/src/new_lint.rs | 12 +- clippy_lints/src/absolute_paths.rs | 10 +- clippy_lints/src/almost_complete_range.rs | 4 +- clippy_lints/src/approx_const.rs | 2 +- .../src/arbitrary_source_item_ordering.rs | 27 +- clippy_lints/src/assigning_clones.rs | 2 +- clippy_lints/src/attrs/mod.rs | 10 +- clippy_lints/src/await_holding_invalid.rs | 3 +- clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/cargo/mod.rs | 6 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/checked_conversions.rs | 2 +- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/disallowed_macros.rs | 2 + clippy_lints/src/disallowed_methods.rs | 2 + clippy_lints/src/disallowed_types.rs | 10 +- clippy_lints/src/doc/mod.rs | 6 +- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/functions/mod.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- clippy_lints/src/incompatible_msrv.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/instant_subtraction.rs | 2 +- clippy_lints/src/legacy_numeric_constants.rs | 2 +- clippy_lints/src/lib.rs | 13 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/lines_filter_map_ok.rs | 2 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/manual_abs_diff.rs | 2 +- clippy_lints/src/manual_bits.rs | 2 +- clippy_lints/src/manual_clamp.rs | 2 +- clippy_lints/src/manual_div_ceil.rs | 2 +- clippy_lints/src/manual_float_methods.rs | 2 +- clippy_lints/src/manual_hash_one.rs | 2 +- clippy_lints/src/manual_is_ascii_check.rs | 2 +- clippy_lints/src/manual_is_power_of_two.rs | 2 +- clippy_lints/src/manual_main_separator_str.rs | 2 +- clippy_lints/src/manual_non_exhaustive.rs | 2 +- clippy_lints/src/manual_option_as_slice.rs | 2 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/manual_retain.rs | 2 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/mem_replace.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/min_ident_chars.rs | 4 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- .../src/missing_const_for_thread_local.rs | 2 +- .../src/needless_borrows_for_generic_args.rs | 2 +- clippy_lints/src/non_std_lazy_statics.rs | 2 +- .../src/operators/arithmetic_side_effects.rs | 2 +- clippy_lints/src/operators/mod.rs | 2 +- clippy_lints/src/question_mark.rs | 2 +- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/redundant_field_names.rs | 4 +- .../src/redundant_static_lifetimes.rs | 4 +- clippy_lints/src/repeat_vec_with_capacity.rs | 2 +- clippy_lints/src/std_instead_of_core.rs | 2 +- clippy_lints/src/string_patterns.rs | 2 +- clippy_lints/src/trait_bounds.rs | 2 +- clippy_lints/src/transmute/mod.rs | 2 +- clippy_lints/src/tuple_array_conversions.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 4 +- clippy_lints/src/unused_trait_names.rs | 2 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/vec.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 6 +- clippy_utils/Cargo.toml | 1 - clippy_utils/src/msrvs.rs | 23 +- src/driver.rs | 3 +- src/main.rs | 4 - tests/compile-test.rs | 6 +- tests/config-metadata.rs | 16 +- .../ordering_good.bad_conf_1.stderr | 2 +- .../ordering_good.bad_conf_2.stderr | 2 +- .../ordering_good.bad_conf_3.stderr | 2 +- .../ordering_good.bad_conf_4.stderr | 2 +- .../ordering_good.bad_conf_5.stderr | 6 +- .../ordering_good.bad_conf_6.stderr | 6 +- .../await_holding_invalid_type.stderr | 8 +- tests/ui-toml/bad_toml/conf_bad_toml.rs | 2 +- tests/ui-toml/bad_toml/conf_bad_toml.stderr | 2 +- tests/ui-toml/bad_toml_type/conf_bad_type.rs | 2 +- .../bad_toml_type/conf_bad_type.stderr | 2 +- .../conf_deprecated_key.rs | 4 + .../conf_deprecated_key.stderr | 10 +- .../duplicated_keys/duplicated_keys.stderr | 2 +- .../duplicated_keys.rs | 5 +- .../duplicated_keys.stderr | 12 +- .../duplicated_keys.rs | 5 +- .../duplicated_keys.stderr | 16 +- .../invalid_min_rust_version.rs | 2 +- .../invalid_min_rust_version.stderr | 2 +- .../toml_unknown_key/conf_unknown_key.rs | 6 +- .../toml_unknown_key/conf_unknown_key.stderr | 313 +----- 105 files changed, 1826 insertions(+), 1481 deletions(-) create mode 100644 clippy_config/src/de.rs diff --git a/Cargo.toml b/Cargo.toml index 603169f72b61..048a20365a6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ clippy_utils = { path = "clippy_utils" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } clippy_lints_internal = { path = "clippy_lints_internal", optional = true } tempfile = { version = "3.3", optional = true } -termize = "0.1" color-print = "0.3.4" anstream = "0.6.18" diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index e5e82ede4fdf..c582e76cbc98 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -470,7 +470,7 @@ pub struct ManualStrip { impl ManualStrip { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } ``` diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 93fd2e35d1ba..36657b787dfd 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -9,10 +9,10 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clippy_utils = { path = "../clippy_utils" } +arrayvec = { version = "0.7", default-features = false } itertools = "0.12" -serde = { version = "1.0", features = ["derive"] } -toml = "0.7.3" +rustc-semver = "1.1" +toml_edit = { version = "0.22.9", default-features = false, features = ["parse"] } [dev-dependencies] walkdir = "2.3" diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 511cb84527d8..737aa359f8eb 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,28 +1,22 @@ -use crate::ClippyConfiguration; +use crate::ConfMetadata; +use crate::de::{DeserializeOrDefault, DiagCtxt, FromDefault, create_value_list_msg, find_closest_match}; use crate::types::{ DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, - Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, - SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, + Rename, SourceItemOrdering, SourceItemOrderingModuleItemGroupings, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; -use clippy_utils::msrvs::Msrv; -use itertools::Itertools; +use rustc_attr_parsing::{RustcVersion, parse_version}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_session::Session; -use rustc_span::edit_distance::edit_distance; -use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; -use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor}; -use serde::{Deserialize, Deserializer, Serialize}; -use std::collections::HashMap; -use std::fmt::{Debug, Display, Formatter}; -use std::ops::Range; +use rustc_span::{Pos, SourceFile, Symbol}; use std::path::PathBuf; -use std::str::FromStr; -use std::sync::OnceLock; -use std::{cmp, env, fmt, fs, io}; +use std::sync::{Arc, OnceLock}; +use std::{env, fs, io}; +use toml_edit as toml; #[rustfmt::skip] -const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ +static DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", @@ -49,341 +43,210 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "MinGW", "CamelCase", ]; -const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"]; -const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"]; -const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"]; -const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] = +static DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"]; +static DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"]; +static DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"]; +static DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] = &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]; -const DEFAULT_MODULE_ITEM_ORDERING_GROUPS: &[(&str, &[SourceItemOrderingModuleItemKind])] = { - #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. - use SourceItemOrderingModuleItemKind::*; - &[ - ("modules", &[ExternCrate, Mod, ForeignMod]), - ("use", &[Use]), - ("macros", &[Macro]), - ("global_asm", &[GlobalAsm]), - ("UPPER_SNAKE_CASE", &[Static, Const]), - ("PascalCase", &[TyAlias, Enum, Struct, Union, Trait, TraitAlias, Impl]), - ("lower_snake_case", &[Fn]), - ] -}; -const DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER: &[SourceItemOrderingTraitAssocItemKind] = { - #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. - use SourceItemOrderingTraitAssocItemKind::*; - &[Const, Type, Fn] -}; -const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = { - #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. - use SourceItemOrderingCategory::*; - &[Enum, Impl, Module, Struct, Trait] -}; - -/// Conf with parse errors -#[derive(Default)] -struct TryConf { - conf: Conf, - value_spans: HashMap>, - errors: Vec, - warnings: Vec, -} - -impl TryConf { - fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self { - Self { - conf: Conf::default(), - value_spans: HashMap::default(), - errors: vec![ConfError::from_toml(file, error)], - warnings: vec![], - } - } -} +static DEFAULT_ALLOWED_SCRIPTS: &[&str] = &["Latin"]; +static DEFAULT_IGNORE_INTERIOR_MUTABILITY: &[&str] = &["bytes::Bytes"]; -#[derive(Debug)] -struct ConfError { - message: String, - suggestion: Option, - span: Span, +macro_rules! first_expr { + ($e:expr $(,$_e:expr)*) => { + $e + }; } -impl ConfError { - fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self { - let span = error.span().unwrap_or(0..file.source_len.0 as usize); - Self::spanned(file, error.message(), None, span) - } - - fn spanned( - file: &SourceFile, - message: impl Into, - suggestion: Option, - span: Range, - ) -> Self { - Self { - message: message.into(), - suggestion, - span: span_from_toml_range(file, span), +macro_rules! define_Conf { + ( + $( + $(#[doc = $doc:literal])* + $(#[default_text = $default_text:literal])? + $(#[rename = $new_name:ident])? + $(#[lints($($for_lints:ident),* $(,)?)])? + // The type must exist for regular fields and shouldn't exist for deprecated ones. + $name:ident($name_str:literal) $(: $ty:ty $(= $default:expr)?)?, + )* + ) => { + #[allow(non_camel_case_types)] + #[derive(Clone, Copy)] + enum ConfField { + $($name,)* + ThirdParty, } - } -} - -// Remove code tags and code behind '# 's, as they are not needed for the lint docs and --explain -pub fn sanitize_explanation(raw_docs: &str) -> String { - // Remove tags and hidden code: - let mut explanation = String::with_capacity(128); - let mut in_code = false; - for line in raw_docs.lines() { - let line = line.strip_prefix(' ').unwrap_or(line); + impl ConfField { + const NAMES: &'static [&'static str] = &[$($name_str,)* "third-party"]; + const FIELDS: &'static [Self] = &[$(first_expr!($(Self::$new_name,)? Self::$name),)* Self::ThirdParty]; - if let Some(lang) = line.strip_prefix("```") { - let tag = lang.split_once(',').map_or(lang, |(left, _)| left); - if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") { - explanation += "```rust\n"; - } else { - explanation += line; - explanation.push('\n'); + fn name(self) -> &'static str { + Self::NAMES[self as usize] } - in_code = !in_code; - } else if !(in_code && line.starts_with("# ")) { - explanation += line; - explanation.push('\n'); - } - } - - explanation -} - -macro_rules! wrap_option { - () => { - None - }; - ($x:literal) => { - Some($x) - }; -} - -macro_rules! default_text { - ($value:expr) => {{ - let mut text = String::new(); - $value.serialize(toml::ser::ValueSerializer::new(&mut text)).unwrap(); - text - }}; - ($value:expr, $override:expr) => { - $override.to_string() - }; -} -macro_rules! deserialize { - ($map:expr, $ty:ty, $errors:expr, $file:expr) => {{ - let raw_value = $map.next_value::>()?; - let value_span = raw_value.span(); - let value = match <$ty>::deserialize(raw_value.into_inner()) { - Err(e) => { - $errors.push(ConfError::spanned( - $file, - e.to_string().replace('\n', " ").trim(), - None, - value_span, - )); - continue; - }, - Ok(value) => value, - }; - (value, value_span) - }}; + fn new_field(self) -> Self { + Self::FIELDS[self as usize] + } - ($map:expr, $ty:ty, $errors:expr, $file:expr, $replacements_allowed:expr) => {{ - let array = $map.next_value::>>()?; - let mut disallowed_paths_span = Range { - start: usize::MAX, - end: usize::MIN, - }; - let mut disallowed_paths = Vec::new(); - for raw_value in array { - let value_span = raw_value.span(); - let mut disallowed_path = match DisallowedPath::<$replacements_allowed>::deserialize(raw_value.into_inner()) - { - Err(e) => { - $errors.push(ConfError::spanned( - $file, - e.to_string().replace('\n', " ").trim(), - None, - value_span, - )); - continue; - }, - Ok(disallowed_path) => disallowed_path, - }; - disallowed_paths_span = union(&disallowed_paths_span, &value_span); - disallowed_path.set_span(span_from_toml_range($file, value_span)); - disallowed_paths.push(disallowed_path); + fn parse(s: &str) -> Option { + match s { + $($name_str => Some(Self::$name),)* + "third-party" => Some(Self::ThirdParty), + _ => None, + } + } } - (disallowed_paths, disallowed_paths_span) - }}; -} -macro_rules! define_Conf { - ($( - $(#[doc = $doc:literal])+ - $(#[conf_deprecated($dep:literal, $new_conf:ident)])? - $(#[default_text = $default_text:expr])? - $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? - $(#[lints($($for_lints:ident),* $(,)?)])? - $name:ident: $ty:ty = $default:expr, - )*) => { /// Clippy lint configuration pub struct Conf { - $($(#[cfg_attr(doc, doc = $doc)])+ pub $name: $ty,)* - } - - mod defaults { - use super::*; - $(pub fn $name() -> $ty { $default })* + // TODO: emit documentation + $($(pub $name: $ty,)?)* } impl Default for Conf { fn default() -> Self { - Self { $($name: defaults::$name(),)* } + Self { + $($($name: <$ty as FromDefault<_>>::from_default(first_expr!($($default,)? ())),)?)* + } } } - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "kebab-case")] - #[allow(non_camel_case_types)] - enum Field { $($name,)* third_party, } - - struct ConfVisitor<'a>(&'a SourceFile); - - impl<'de> Visitor<'de> for ConfVisitor<'_> { - type Value = TryConf; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("Conf") + impl Conf { + pub fn get_metadata() -> Vec { + vec![$( + ConfMetadata { + name: $name_str, + default: first_expr!( + $($default_text.into(),)? + $(<$ty as FromDefault<_>>::display_default(first_expr!($($default,)? ())).to_string(),)? + String::new() + ), + lints: &[$($(stringify!($for_lints)),*)?], + doc: concat!($($doc, '\n',)*), + renamed_to: first_expr!($(Some(ConfField::$new_name.name()),)? None), + }, + )*] } - fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de> { - let mut value_spans = HashMap::new(); - let mut errors = Vec::new(); - let mut warnings = Vec::new(); - - // Declare a local variable for each field available to a configuration file. - $(let mut $name = None;)* + fn deserialize(dcx: &DiagCtxt<'_>, table: &toml::Table) -> Self { + $($(let mut $name: Option<$ty> = None;)?)* - // could get `Field` here directly, but get `String` first for diagnostics - while let Some(name) = map.next_key::>()? { - let field = match Field::deserialize(name.get_ref().as_str().into_deserializer()) { - Err(e) => { - let e: FieldError = e; - errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span())); - continue; + for (key, value) in table.iter() { + let Some(mut conf_key) = ConfField::parse(key) else { + let sp = dcx.make_sp(table.get_key_value(key).unwrap().0.span()); + let mut diag = dcx.inner.struct_span_err(sp, "unknown field name"); + if let Some(sugg) = find_closest_match(key, ConfField::NAMES) { + diag.span_suggestion(sp, "did you mean", sugg, Applicability::MaybeIncorrect); } - Ok(field) => field + diag.note_once(create_value_list_msg(dcx.width, ConfField::NAMES)); + diag.emit(); + continue; }; - - match field { - $(Field::$name => { - // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. - $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let (value, value_span) = - deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); - // Was this field set previously? - if $name.is_some() { - errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); - continue; - } - $name = Some(value); - value_spans.insert(name.get_ref().as_str().to_string(), value_span); - // If this is a deprecated field, was the new field (`$new_conf`) set previously? - // Note that `$new_conf` is one of the defined `$name`s. - $(match $new_conf { - Some(_) => errors.push(ConfError::spanned(self.0, concat!( - "duplicate field `", stringify!($new_conf), - "` (provided as `", stringify!($name), "`)" - ), None, name.span())), - None => $new_conf = $name.clone(), - })? - })* - // ignore contents of the third_party key - Field::third_party => drop(map.next_value::()) + loop { + match conf_key { + $($(ConfField::$name => { + // Duplicate keys are handled by the toml parser + $name = Some( + <$ty as DeserializeOrDefault<_>>::deserialize_or_default( + dcx, + value.into(), + first_expr!($($default,)? ()), + ), + ); + },)?)* + ConfField::ThirdParty => {}, + // All deprecated fields. + _ => { + let sp = dcx.make_sp(table.get_key_value(key).unwrap().0.span()); + conf_key = conf_key.new_field(); + let other_value = table.get_key_value(conf_key.name()); + dcx.inner.struct_span_warn(sp, format!("use of a deprecated field")) + .with_span_suggestion( + sp, "use new name", conf_key.name(), + if other_value.is_some() { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + } + ).emit(); + + if let Some((other_key, _)) = other_value { + dcx.inner.struct_span_err(sp, format!("duplicate key in document root")) + .with_span_note(dcx.make_sp(other_key.span()), "previous definition here") + .emit(); + } else { + continue; + } + }, + } + break; } } - let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; - Ok(TryConf { conf, value_spans, errors, warnings }) + + Self {$($( + $name: $name.unwrap_or_else( + || <$ty as FromDefault<_>>::from_default(first_expr!($($default,)? ())) + ), + )?)*} } } - pub fn get_configuration_metadata() -> Vec { - vec![$( - ClippyConfiguration { - name: stringify!($name).replace('_', "-"), - default: default_text!(defaults::$name() $(, $default_text)?), - lints: &[$($(stringify!($for_lints)),*)?], - doc: concat!($($doc, '\n',)*), - deprecation_reason: wrap_option!($($dep)?) - }, - )*] + #[test] + fn check_conf_order() { + for [x, y] in ConfField::NAMES[..ConfField::NAMES.len() - 1].array_windows::<2>() { + assert!(x <= y, "configuration `{x}` and `{y}` are out of order"); + } } - }; -} - -fn union(x: &Range, y: &Range) -> Range { - Range { - start: cmp::min(x.start, y.start), - end: cmp::max(x.end, y.end), - } -} -fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { - Span::new( - file.start_pos + BytePos::from_usize(span.start), - file.start_pos + BytePos::from_usize(span.end), - SyntaxContext::root(), - None, - ) + #[test] + fn check_conf_names() {$( + assert_eq!(stringify!($name).replace('_', "-"), $name_str); + )*} + }; } define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] - absolute_paths_allowed_crates: Vec = Vec::new(), + absolute_paths_allowed_crates("absolute-paths-allowed-crates"): FxHashSet, /// The maximum number of segments a path can have before being linted, anything above this will /// be linted. #[lints(absolute_paths)] - absolute_paths_max_segments: u64 = 2, + absolute_paths_max_segments("absolute-paths-max-segments"): u64 = 2, /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block #[lints(undocumented_unsafe_blocks)] - accept_comment_above_attributes: bool = true, + accept_comment_above_attributes("accept-comment-above-attributes"): bool = true, /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block #[lints(undocumented_unsafe_blocks)] - accept_comment_above_statement: bool = true, + accept_comment_above_statement("accept-comment-above-statement"): bool = true, /// Don't lint when comparing the result of a modulo operation to zero. #[lints(modulo_arithmetic)] - allow_comparison_to_zero: bool = true, + allow_comparison_to_zero("allow-comparison-to-zero"): bool = true, /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` #[lints(dbg_macro)] - allow_dbg_in_tests: bool = false, + allow_dbg_in_tests("allow-dbg-in-tests"): bool = false, /// Whether `expect` should be allowed in code always evaluated at compile time #[lints(expect_used)] - allow_expect_in_consts: bool = true, + allow_expect_in_consts("allow-expect-in-consts"): bool = true, /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` #[lints(expect_used)] - allow_expect_in_tests: bool = false, + allow_expect_in_tests("allow-expect-in-tests"): bool = false, /// Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]` #[lints(indexing_slicing)] - allow_indexing_slicing_in_tests: bool = false, + allow_indexing_slicing_in_tests("allow-indexing-slicing-in-tests"): bool = false, /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` #[lints(uninlined_format_args)] - allow_mixed_uninlined_format_args: bool = true, + allow_mixed_uninlined_format_args("allow-mixed-uninlined-format-args"): bool = true, /// Whether to allow `r#""#` when `r""` can be used #[lints(needless_raw_string_hashes)] - allow_one_hash_in_raw_strings: bool = false, + allow_one_hash_in_raw_strings("allow-one-hash-in-raw-strings"): bool = false, /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` #[lints(panic)] - allow_panic_in_tests: bool = false, + allow_panic_in_tests("allow-panic-in-tests"): bool = false, /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` #[lints(print_stderr, print_stdout)] - allow_print_in_tests: bool = false, + allow_print_in_tests("allow-print-in-tests"): bool = false, /// Whether to allow module inception if it's not public. #[lints(module_inception)] - allow_private_module_inception: bool = false, + allow_private_module_inception("allow-private-module-inception"): bool = false, /// List of trait paths to ignore when checking renamed function parameters. /// /// #### Example @@ -398,29 +261,27 @@ define_Conf! { /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. #[lints(renamed_function_params)] - allow_renamed_params_for: Vec = - DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(), + allow_renamed_params_for("allow-renamed-params-for"): Vec = DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, /// Whether `unwrap` should be allowed in code always evaluated at compile time #[lints(unwrap_used)] - allow_unwrap_in_consts: bool = true, + allow_unwrap_in_consts("allow-unwrap-in-consts"): bool = true, /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` #[lints(unwrap_used)] - allow_unwrap_in_tests: bool = false, + allow_unwrap_in_tests("allow-unwrap-in-tests"): bool = false, /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` #[lints(useless_vec)] - allow_useless_vec_in_tests: bool = false, + allow_useless_vec_in_tests("allow-useless-vec-in-tests"): bool = false, /// Additional dotfiles (files or directories starting with a dot) to allow #[lints(path_ends_with_ext)] - allowed_dotfiles: Vec = Vec::default(), + allowed_dotfiles("allowed-dotfiles"): Vec, /// A list of crate names to allow duplicates of #[lints(multiple_crate_versions)] - allowed_duplicate_crates: Vec = Vec::new(), + allowed_duplicate_crates("allowed-duplicate-crates"): FxHashSet, /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of /// the list to indicate, that the configured values should be appended to the default /// configuration of Clippy. By default, any configuration will replace the default value. #[lints(min_ident_chars)] - allowed_idents_below_min_chars: Vec = - DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(), + allowed_idents_below_min_chars("allowed-idents-below-min-chars"): FxHashSet = DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS, /// List of prefixes to allow when determining whether an item's name ends with the module's name. /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`), /// then don't emit a warning. @@ -439,10 +300,10 @@ define_Conf! { /// - Use `".."` as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value #[lints(module_name_repetitions)] - allowed_prefixes: Vec = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect(), + allowed_prefixes("allowed-prefixes"): Vec = DEFAULT_ALLOWED_PREFIXES, /// The list of unicode scripts allowed to be used in the scope. #[lints(disallowed_script_idents)] - allowed_scripts: Vec = vec!["Latin".to_string()], + allowed_scripts("allowed-scripts"): Vec = DEFAULT_ALLOWED_SCRIPTS, /// List of path segments allowed to have wildcard imports. /// /// #### Example @@ -457,7 +318,7 @@ define_Conf! { /// 2. Paths with any segment that containing the word 'prelude' /// are already allowed by default. #[lints(wildcard_imports)] - allowed_wildcard_imports: Vec = Vec::new(), + allowed_wildcard_imports("allowed-wildcard-imports"): FxHashSet, /// Suppress checking of the passed type names in all types of operations. /// /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. @@ -473,7 +334,7 @@ define_Conf! { /// A type, say `SomeType`, listed in this configuration has the same behavior of /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. #[lints(arithmetic_side_effects)] - arithmetic_side_effects_allowed: Vec = <_>::default(), + arithmetic_side_effects_allowed("arithmetic-side-effects-allowed"): Vec, /// Suppress checking of the passed type pair names in binary operations like addition or /// multiplication. /// @@ -489,7 +350,7 @@ define_Conf! { /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] /// ``` #[lints(arithmetic_side_effects)] - arithmetic_side_effects_allowed_binary: Vec<(String, String)> = <_>::default(), + arithmetic_side_effects_allowed_binary("arithmetic-side-effects-allowed-binary"): Vec<[String; 2]>, /// Suppress checking of the passed type names in unary operations like "negation" (`-`). /// /// #### Example @@ -498,10 +359,10 @@ define_Conf! { /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] /// ``` #[lints(arithmetic_side_effects)] - arithmetic_side_effects_allowed_unary: Vec = <_>::default(), + arithmetic_side_effects_allowed_unary("arithmetic-side-effects-allowed-unary"): Vec, /// The maximum allowed size for arrays on the stack #[lints(large_const_arrays, large_stack_arrays)] - array_size_threshold: u64 = 16 * 1024, + array_size_threshold("array-size-threshold"): u64 = 16 * 1024, /// Suppress lints whenever the suggested change would cause breakage for other crates. #[lints( box_collection, @@ -524,22 +385,18 @@ define_Conf! { vec_box, wrong_self_convention, )] - avoid_breaking_exported_api: bool = true, + avoid_breaking_exported_api("avoid-breaking-exported-api"): bool = true, /// The list of types which may not be held across an await point. - #[disallowed_paths_allow_replacements = false] #[lints(await_holding_invalid_type)] - await_holding_invalid_types: Vec = Vec::new(), - /// DEPRECATED LINT: BLACKLISTED_NAME. - /// - /// Use the Disallowed Names lint instead - #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)] - blacklisted_names: Vec = Vec::new(), + await_holding_invalid_types("await-holding-invalid-types"): Vec, + #[rename = disallowed_names] + blacklisted_names("blacklisted-names"), /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. #[lints(cargo_common_metadata)] - cargo_ignore_publish: bool = false, + cargo_ignore_publish("cargo-ignore-publish"): bool = false, /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] - check_incompatible_msrv_in_tests: bool = false, + check_incompatible_msrv_in_tests("check-incompatible-msrv-in-tests"): bool = false, /// Whether to suggest reordering constructor fields when initializers are present. /// /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the @@ -559,45 +416,39 @@ define_Conf! { /// /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 #[lints(inconsistent_struct_constructor)] - check_inconsistent_struct_field_initializers: bool = false, + check_inconsistent_struct_field_initializers("check-inconsistent-struct-field-initializers"): bool = false, /// Whether to also run the listed lints on private items. #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] - check_private_items: bool = false, + check_private_items("check-private-items"): bool = false, /// The maximum cognitive complexity a function can have #[lints(cognitive_complexity)] - cognitive_complexity_threshold: u64 = 25, - /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. - /// - /// Use the Cognitive Complexity lint instead. - #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] - cyclomatic_complexity_threshold: u64 = 25, + cognitive_complexity_threshold("cognitive-complexity-threshold"): u64 = 25, + #[rename = cognitive_complexity_threshold] + cyclomatic_complexity_threshold("cyclomatic-complexity-threshold"), /// The list of disallowed macros, written as fully qualified paths. - #[disallowed_paths_allow_replacements = true] #[lints(disallowed_macros)] - disallowed_macros: Vec = Vec::new(), + disallowed_macros("disallowed-macros"): Vec, /// The list of disallowed methods, written as fully qualified paths. - #[disallowed_paths_allow_replacements = true] #[lints(disallowed_methods)] - disallowed_methods: Vec = Vec::new(), + disallowed_methods("disallowed-methods"): Vec, /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value /// `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. #[lints(disallowed_names)] - disallowed_names: Vec = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), + disallowed_names("disallowed-names"): Vec = DEFAULT_DISALLOWED_NAMES, /// The list of disallowed types, written as fully qualified paths. - #[disallowed_paths_allow_replacements = true] #[lints(disallowed_types)] - disallowed_types: Vec = Vec::new(), + disallowed_types("disallowed-types"): Vec, /// The list of words this lint should not consider as identifiers needing ticks. The value /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. For example: /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. #[lints(doc_markdown)] - doc_valid_idents: Vec = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(), + doc_valid_idents("doc-valid-idents"): FxHashSet = DEFAULT_DOC_VALID_IDENTS, /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. #[lints(non_send_fields_in_send_ty)] - enable_raw_pointer_heuristic_for_send: bool = true, + enable_raw_pointer_heuristic_for_send("enable-raw-pointer-heuristic-for-send"): bool = true, /// Whether to recommend using implicit into iter for reborrowed values. /// /// #### Example @@ -616,79 +467,74 @@ define_Conf! { /// for _ in &mut *rmvec {} /// ``` #[lints(explicit_iter_loop)] - enforce_iter_loop_reborrow: bool = false, + enforce_iter_loop_reborrow("enforce-iter-loop-reborrow"): bool = false, /// The list of imports to always rename, a fully qualified path followed by the rename. #[lints(missing_enforced_import_renames)] - enforced_import_renames: Vec = Vec::new(), + enforced_import_renames("enforced-import-renames"): Vec, /// The minimum number of enum variants for the lints about variant names to trigger #[lints(enum_variant_names)] - enum_variant_name_threshold: u64 = 3, + enum_variant_name_threshold("enum-variant-name-threshold"): u64 = 3, /// The maximum size of an enum's variant to avoid box suggestion #[lints(large_enum_variant)] - enum_variant_size_threshold: u64 = 200, + enum_variant_size_threshold("enum-variant-size-threshold"): u64 = 200, /// The maximum amount of nesting a block can reside in #[lints(excessive_nesting)] - excessive_nesting_threshold: u64 = 0, + excessive_nesting_threshold("excessive-nesting-threshold"): u64 = 0, /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint #[lints(large_futures)] - future_size_threshold: u64 = 16 * 1024, + future_size_threshold("future-size-threshold"): u64 = 16 * 1024, /// A list of paths to types that should be treated as if they do not contain interior mutability #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)] - ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()]), + ignore_interior_mutability("ignore-interior-mutability"): Vec = DEFAULT_IGNORE_INTERIOR_MUTABILITY, /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] - large_error_threshold: u64 = 128, + large_error_threshold("large-error-threshold"): u64 = 128, /// Whether collapsible `if` chains are linted if they contain comments inside the parts /// that would be collapsed. #[lints(collapsible_if)] - lint_commented_code: bool = false, - /// Whether to suggest reordering constructor fields when initializers are present. - /// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers - /// - /// Use the `check-inconsistent-struct-field-initializers` configuration instead. - #[conf_deprecated("Please use `check-inconsistent-struct-field-initializers` instead", check_inconsistent_struct_field_initializers)] - lint_inconsistent_struct_field_initializers: bool = false, + lint_commented_code("lint-commented-code"): bool = false, + #[rename = check_inconsistent_struct_field_initializers] + lint_inconsistent_struct_field_initializers("lint-inconsistent-struct-field-initializers"), /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] - literal_representation_threshold: u64 = 16384, + literal_representation_threshold("literal-representation-threshold"): u64 = 16384, /// Whether the matches should be considered by the lint, and whether there should /// be filtering for common types. #[lints(manual_let_else)] - matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes, + matches_for_let_else("matches-for-let-else"): MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes, /// The maximum number of bool parameters a function can have #[lints(fn_params_excessive_bools)] - max_fn_params_bools: u64 = 3, + max_fn_params_bools("max-fn-params-bools"): u64 = 3, /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes #[lints(large_include_file)] - max_include_file_size: u64 = 1_000_000, + max_include_file_size("max-include-file-size"): u64 = 1_000_000, /// The maximum number of bool fields a struct can have #[lints(struct_excessive_bools)] - max_struct_bools: u64 = 3, + max_struct_bools("max-struct-bools"): u64 = 3, /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. #[lints(index_refutable_slice)] - max_suggested_slice_pattern_length: u64 = 3, + max_suggested_slice_pattern_length("max-suggested-slice-pattern-length"): u64 = 3, /// The maximum number of bounds a trait can have to be linted #[lints(type_repetition_in_bounds)] - max_trait_bounds: u64 = 3, + max_trait_bounds("max-trait-bounds"): u64 = 3, /// Minimum chars an ident can have, anything below or equal to this will be linted. #[lints(min_ident_chars)] - min_ident_chars_threshold: u64 = 1, + min_ident_chars_threshold("min-ident-chars-threshold"): u64 = 1, /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. #[lints(missing_docs_in_private_items)] - missing_docs_in_crate_items: bool = false, + missing_docs_in_crate_items("missing-docs-in-crate-items"): bool = false, /// The named groupings of different source item kinds within modules. #[lints(arbitrary_source_item_ordering)] - module_item_order_groupings: SourceItemOrderingModuleItemGroupings = DEFAULT_MODULE_ITEM_ORDERING_GROUPS.into(), + module_item_order_groupings("module-item-order-groupings"): SourceItemOrderingModuleItemGroupings, /// Whether the items within module groups should be ordered alphabetically or not. /// /// This option can be configured to "all", "none", or a list of specific grouping names that should be checked /// (e.g. only "enums"). #[lints(arbitrary_source_item_ordering)] - module_items_ordered_within_groupings: SourceItemOrderingWithinModuleItemGroupings = - SourceItemOrderingWithinModuleItemGroupings::None, + module_items_ordered_within_groupings("module-items-ordered-within-groupings"): SourceItemOrderingWithinModuleItemGroupings, /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = "current version"] #[lints( @@ -766,410 +612,235 @@ define_Conf! { unused_trait_names, use_self, )] - msrv: Msrv = Msrv::default(), + msrv("msrv"): Option, /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. #[lints(large_types_passed_by_value)] - pass_by_value_size_limit: u64 = 256, + pass_by_value_size_limit("pass-by-value-size-limit"): u64 = 256, /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] - pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + pub_underscore_fields_behavior("pub-underscore-fields-behavior"): PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, /// Whether to lint only if it's multiline. #[lints(semicolon_inside_block)] - semicolon_inside_block_ignore_singleline: bool = false, + semicolon_inside_block_ignore_singleline("semicolon-inside-block-ignore-singleline"): bool = false, /// Whether to lint only if it's singleline. #[lints(semicolon_outside_block)] - semicolon_outside_block_ignore_multiline: bool = false, + semicolon_outside_block_ignore_multiline("semicolon-outside-block-ignore-multiline"): bool = false, /// The maximum number of single char bindings a scope may have #[lints(many_single_char_names)] - single_char_binding_names_threshold: u64 = 4, + single_char_binding_names_threshold("single-char-binding-names-threshold"): u64 = 4, /// Which kind of elements should be ordered internally, possible values being `enum`, `impl`, `module`, `struct`, `trait`. #[lints(arbitrary_source_item_ordering)] - source_item_ordering: SourceItemOrdering = DEFAULT_SOURCE_ITEM_ORDERING.into(), + source_item_ordering("source-item-ordering"): SourceItemOrdering, /// The maximum allowed stack size for functions in bytes #[lints(large_stack_frames)] - stack_size_threshold: u64 = 512_000, + stack_size_threshold("stack-size-threshold"): u64 = 512_000, /// Enforce the named macros always use the braces specified. /// /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro /// 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. #[lints(nonstandard_macro_braces)] - standard_macro_braces: Vec = Vec::new(), + standard_macro_braces("standard-macro-braces"): Vec, /// The minimum number of struct fields for the lints about field names to trigger #[lints(struct_field_names)] - struct_field_name_threshold: u64 = 3, + struct_field_name_threshold("struct-field-name-threshold"): u64 = 3, /// Whether to suppress a restriction lint in constant code. In same /// cases the restructured operation might not be unavoidable, as the /// suggested counterparts are unavailable in constant code. This /// configuration will cause restriction lints to trigger even /// if no suggestion can be made. #[lints(indexing_slicing)] - suppress_restriction_lint_in_const: bool = false, + suppress_restriction_lint_in_const("suppress-restriction-lint-in-const"): bool = false, /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap #[lints(boxed_local, useless_vec)] - too_large_for_stack: u64 = 200, + too_large_for_stack("too-large-for-stack"): u64 = 200, /// The maximum number of argument a function or method can have #[lints(too_many_arguments)] - too_many_arguments_threshold: u64 = 7, + too_many_arguments_threshold("too-many-arguments-threshold"): u64 = 7, /// The maximum number of lines a function or method can have #[lints(too_many_lines)] - too_many_lines_threshold: u64 = 100, + too_many_lines_threshold("too-many-lines-threshold"): u64 = 100, /// The order of associated items in traits. #[lints(arbitrary_source_item_ordering)] - trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(), + trait_assoc_item_kinds_order("trait-assoc-item-kinds-order"): SourceItemOrderingTraitAssocItemKinds, /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by /// reference. #[default_text = "target_pointer_width * 2"] #[lints(trivially_copy_pass_by_ref)] - trivial_copy_size_limit: Option = None, + trivial_copy_size_limit("trivial-copy-size-limit"): Option, /// The maximum complexity a type can have #[lints(type_complexity)] - type_complexity_threshold: u64 = 250, + type_complexity_threshold("type-complexity-threshold"): u64 = 250, /// The byte size a `T` in `Box` can have, below which it triggers the `clippy::unnecessary_box` lint #[lints(unnecessary_box_returns)] - unnecessary_box_size: u64 = 128, + unnecessary_box_size("unnecessary-box-size"): u64 = 128, /// Should the fraction of a decimal be linted to include separators. #[lints(unreadable_literal)] - unreadable_literal_lint_fractions: bool = true, + unreadable_literal_lint_fractions("unreadable-literal-lint-fractions"): bool = true, /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other #[lints(upper_case_acronyms)] - upper_case_acronyms_aggressive: bool = false, + upper_case_acronyms_aggressive("upper-case-acronyms-aggressive"): bool = false, /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed #[lints(vec_box)] - vec_box_size_threshold: u64 = 4096, + vec_box_size_threshold("vec-box-size-threshold"): u64 = 4096, /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' #[lints(verbose_bit_mask)] - verbose_bit_mask_threshold: u64 = 1, + verbose_bit_mask_threshold("verbose-bit-mask-threshold"): u64 = 1, /// Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests, /// or for `pub use` reexports. #[lints(wildcard_imports)] - warn_on_all_wildcard_imports: bool = false, + warn_on_all_wildcard_imports("warn-on-all-wildcard-imports"): bool = false, /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. #[lints(macro_metavars_in_unsafe)] - warn_unsafe_macro_metavars_in_private_macros: bool = false, + warn_unsafe_macro_metavars_in_private_macros("warn-unsafe-macro-metavars-in-private-macros"): bool = false, +} + +// Remove code tags and code behind '# 's, as they are not needed for the lint docs and --explain +pub fn sanitize_explanation(raw_docs: &str) -> String { + // Remove tags and hidden code: + let mut explanation = String::with_capacity(128); + let mut in_code = false; + for line in raw_docs.lines() { + let line = line.strip_prefix(' ').unwrap_or(line); + + if let Some(lang) = line.strip_prefix("```") { + let tag = lang.split_once(',').map_or(lang, |(left, _)| left); + if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") { + explanation += "```rust\n"; + } else { + explanation += line; + explanation.push('\n'); + } + in_code = !in_code; + } else if !(in_code && line.starts_with("# ")) { + explanation += line; + explanation.push('\n'); + } + } + + explanation } -/// Search for the configuration file. +/// Searches for and loads the config file into the source map. /// /// # Errors /// /// Returns any unexpected filesystem error encountered when searching for the config file -pub fn lookup_conf_file() -> io::Result<(Option, Vec)> { +fn load_conf_file(sess: &Session) -> Option> { /// Possible filename to search for. const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR. // If neither of those exist, use ".". (Update documentation if this priority changes) - let mut current = env::var_os("CLIPPY_CONF_DIR") - .or_else(|| env::var_os("CARGO_MANIFEST_DIR")) - .map_or_else(|| PathBuf::from("."), PathBuf::from) - .canonicalize()?; - - let mut found_config: Option = None; - let mut warnings = vec![]; + const CONFIG_VARS: [(&str, &str); 2] = [ + ("CLIPPY_CONF_DIR", "failed to read `CLIPPY_CONF_DIR` as a directory"), + ( + "CARGO_MANIFEST_DIR", + "failed to read `CARGO_MANIFEST_DIR` as a directory", + ), + ]; + let (current, msg) = CONFIG_VARS + .into_iter() + .find_map(|(var, msg)| env::var_os(var).map(|p| (PathBuf::from(p), msg))) + .unwrap_or_else(|| (PathBuf::from("."), "failed to get the current directory")); + let mut current = match current.canonicalize() { + Ok(x) => x, + Err(e) => { + sess.dcx().err(format!("{msg}: {e}")); + return None; + }, + }; + let mut loaded_config: Option<(PathBuf, Arc)> = None; loop { - for config_file_name in &CONFIG_FILE_NAMES { - if let Ok(config_file) = current.join(config_file_name).canonicalize() { - match fs::metadata(&config_file) { - Err(e) if e.kind() == io::ErrorKind::NotFound => {}, - Err(e) => return Err(e), - Ok(md) if md.is_dir() => {}, - Ok(_) => { - // warn if we happen to find two config files #8323 - if let Some(ref found_config) = found_config { - warnings.push(format!( - "using config file `{}`, `{}` will be ignored", - found_config.display(), - config_file.display() - )); - } else { - found_config = Some(config_file); - } - }, + for config_file_name in CONFIG_FILE_NAMES { + if let Ok(config_path) = current.join(config_file_name).canonicalize() { + if let Some((loaded_path, _)) = &loaded_config { + if fs::metadata(loaded_path).is_ok_and(|x| x.is_file()) { + // Warn if `.clippy.toml` and `clippy.toml` exist + sess.dcx().warn(format!( + "using config file `{}`, `{}` will be ignored", + loaded_path.display(), + config_path.display(), + )); + } + } else { + match sess.source_map().load_file(&config_path) { + Ok(src) => loaded_config = Some((config_path, src)), + Err(e) + if matches!( + e.kind(), + io::ErrorKind::NotFound | io::ErrorKind::IsADirectory | io::ErrorKind::NotADirectory + ) => {}, + Err(e) => { + sess.dcx() + .err(format!("error reading `{}`: {e}", config_path.display())); + return None; + }, + } } } } - if found_config.is_some() { - return Ok((found_config, warnings)); + // Don't mention config files in parent directories. + if let Some((_, src)) = loaded_config { + return Some(src); } // If the current directory has no parent, we're done searching. if !current.pop() { - return Ok((None, warnings)); + return None; } } } -fn deserialize(file: &SourceFile) -> TryConf { - match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(file)) { - Ok(mut conf) => { - extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); - extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES); - extend_vec_if_indicator_present( - &mut conf.conf.allow_renamed_params_for, - DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, - ); - - // Confirms that the user has not accidentally configured ordering requirements for groups that - // aren't configured. - if let SourceItemOrderingWithinModuleItemGroupings::Custom(groupings) = - &conf.conf.module_items_ordered_within_groupings - { - for grouping in groupings { - if !conf.conf.module_item_order_groupings.is_grouping(grouping) { - // Since this isn't fixable by rustfix, don't emit a `Suggestion`. This just adds some useful - // info for the user instead. - - let names = conf.conf.module_item_order_groupings.grouping_names(); - let suggestion = suggest_candidate(grouping, names.iter().map(String::as_str)) - .map(|s| format!(" perhaps you meant `{s}`?")) - .unwrap_or_default(); - let names = names.iter().map(|s| format!("`{s}`")).join(", "); - let message = format!( - "unknown ordering group: `{grouping}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}" - ); - - let span = conf - .value_spans - .get("module_item_order_groupings") - .cloned() - .unwrap_or_default(); - conf.errors.push(ConfError::spanned(file, message, None, span)); - } - } - } - - // TODO: THIS SHOULD BE TESTED, this comment will be gone soon - if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") { - conf.conf - .allowed_idents_below_min_chars - .extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string)); - } - if conf.conf.doc_valid_idents.iter().any(|e| e == "..") { - conf.conf - .doc_valid_idents - .extend(DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string)); - } - - conf - }, - Err(e) => TryConf::from_toml_error(file, &e), - } -} - -fn extend_vec_if_indicator_present(vec: &mut Vec, default: &[&str]) { - if vec.contains(&"..".to_string()) { - vec.extend(default.iter().map(ToString::to_string)); - } -} - impl Conf { - pub fn read(sess: &Session, path: &io::Result<(Option, Vec)>) -> &'static Conf { + pub fn load(sess: &Session) -> &'static Conf { static CONF: OnceLock = OnceLock::new(); - CONF.get_or_init(|| Conf::read_inner(sess, path)) + CONF.get_or_init(|| Conf::load_inner(sess)) } - fn read_inner(sess: &Session, path: &io::Result<(Option, Vec)>) -> Conf { - match path { - Ok((_, warnings)) => { - for warning in warnings { - sess.dcx().warn(warning.clone()); - } - }, - Err(error) => { - sess.dcx() - .err(format!("error finding Clippy's configuration file: {error}")); - }, - } - - let TryConf { - mut conf, - value_spans: _, - errors, - warnings, - } = match path { - Ok((Some(path), _)) => match sess.source_map().load_file(path) { - Ok(file) => deserialize(&file), - Err(error) => { - sess.dcx().err(format!("failed to read `{}`: {error}", path.display())); - TryConf::default() - }, - }, - _ => TryConf::default(), + fn load_inner(sess: &Session) -> Conf { + let mut conf = if let Some(src) = load_conf_file(sess) + && let dcx = DiagCtxt::new(sess, src.start_pos.to_usize()) + && let src = src.src.as_ref().unwrap() + && let Ok(toml) = toml::ImDocument::parse(src.as_str()) + .inspect_err(|e| dcx.span_err(e.span(), format!("error reading `clippy.toml`: {}", e.message()))) + { + Conf::deserialize(&dcx, toml.as_table()) + } else { + Conf::default() }; - conf.msrv.read_cargo(sess); - - // all conf errors are non-fatal, we just use the default conf in case of error - for error in errors { - let mut diag = sess.dcx().struct_span_err( - error.span, - format!("error reading Clippy's configuration file: {}", error.message), - ); - - if let Some(sugg) = error.suggestion { - diag.span_suggestion(error.span, sugg.message, sugg.suggestion, Applicability::MaybeIncorrect); - } - - diag.emit(); - } - - for warning in warnings { - sess.dcx().span_warn( - warning.span, - format!("error reading Clippy's configuration file: {}", warning.message), - ); - } - - conf - } -} - -const SEPARATOR_WIDTH: usize = 4; - -#[derive(Debug)] -struct FieldError { - error: String, - suggestion: Option, -} - -#[derive(Debug)] -struct Suggestion { - message: &'static str, - suggestion: &'static str, -} - -impl std::error::Error for FieldError {} - -impl Display for FieldError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.pad(&self.error) - } -} - -impl serde::de::Error for FieldError { - fn custom(msg: T) -> Self { - Self { - error: msg.to_string(), - suggestion: None, - } - } - - fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { - // List the available fields sorted and at least one per line, more if `CLIPPY_TERMINAL_WIDTH` is - // set and allows it. - use fmt::Write; - - let metadata = get_configuration_metadata(); - let deprecated = metadata - .iter() - .filter_map(|conf| { - if conf.deprecation_reason.is_some() { - Some(conf.name.as_str()) - } else { - None + let cargo_msrv = env::var("CARGO_PKG_RUST_VERSION") + .ok() + .and_then(|v| parse_version(Symbol::intern(&v))); + match (&conf.msrv, cargo_msrv) { + (None, Some(cargo_msrv)) => conf.msrv = Some(cargo_msrv), + (Some(clippy_msrv), Some(cargo_msrv)) => { + if *clippy_msrv != cargo_msrv { + sess.dcx().warn(format!( + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" + )); } - }) - .collect::>(); - - let mut expected = expected - .iter() - .copied() - .filter(|name| !deprecated.contains(name)) - .collect::>(); - expected.sort_unstable(); - - let (rows, column_widths) = calculate_dimensions(&expected); - - let mut msg = format!("unknown field `{field}`, expected one of"); - for row in 0..rows { - writeln!(msg).unwrap(); - for (column, column_width) in column_widths.iter().copied().enumerate() { - let index = column * rows + row; - let field = expected.get(index).copied().unwrap_or_default(); - write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap(); - } + }, + (_, None) => {}, } - let suggestion = suggest_candidate(field, expected).map(|suggestion| Suggestion { - message: "perhaps you meant", - suggestion, - }); - - Self { error: msg, suggestion } + conf } } -fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { - let columns = env::var("CLIPPY_TERMINAL_WIDTH") - .ok() - .and_then(|s| ::from_str(&s).ok()) - .map_or(1, |terminal_width| { - let max_field_width = fields.iter().map(|field| field.len()).max().unwrap(); - cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width)) - }); - - let rows = fields.len().div_ceil(columns); - - let column_widths = (0..columns) - .map(|column| { - if column < columns - 1 { - (0..rows) - .map(|row| { - let index = column * rows + row; - let field = fields.get(index).copied().unwrap_or_default(); - field.len() - }) - .max() - .unwrap() - } else { - // Avoid adding extra space to the last column. - 0 - } - }) - .collect::>(); - - (rows, column_widths) -} - -/// Given a user-provided value that couldn't be matched to a known option, finds the most likely -/// candidate among candidates that the user might have meant. -fn suggest_candidate<'a, I>(value: &str, candidates: I) -> Option<&'a str> -where - I: IntoIterator, -{ - candidates - .into_iter() - .filter_map(|expected| { - let dist = edit_distance(value, expected, 4)?; - Some((dist, expected)) - }) - .min_by_key(|&(dist, _)| dist) - .map(|(_, suggestion)| suggestion) -} - #[cfg(test)] mod tests { - use serde::de::IgnoredAny; - use std::collections::{HashMap, HashSet}; + use rustc_data_structures::fx::FxHashSet; use std::fs; use walkdir::WalkDir; #[test] fn configs_are_tested() { - let mut names: HashSet = crate::get_configuration_metadata() - .into_iter() - .filter_map(|meta| { - if meta.deprecation_reason.is_none() { - Some(meta.name.replace('_', "-")) - } else { - None - } - }) - .collect(); + let mut names: FxHashSet<_> = super::Conf::get_metadata().into_iter().map(|meta| meta.name).collect(); let toml_files = WalkDir::new("../tests") .into_iter() @@ -1178,10 +849,9 @@ mod tests { for entry in toml_files { let file = fs::read_to_string(entry.path()).unwrap(); - #[allow(clippy::zero_sized_map_values)] - if let Ok(map) = toml::from_str::>(&file) { - for name in map.keys() { - names.remove(name.as_str()); + if let Ok(toml) = toml_edit::ImDocument::parse(&*file) { + for (key, _) in toml.as_table() { + names.remove(key); } } } diff --git a/clippy_config/src/de.rs b/clippy_config/src/de.rs new file mode 100644 index 000000000000..d5ab9d540911 --- /dev/null +++ b/clippy_config/src/de.rs @@ -0,0 +1,645 @@ +use arrayvec::ArrayVec; +use itertools::Itertools; +use rustc_attr_parsing::{RustcVersion, parse_version}; +use rustc_errors::{DiagCtxtHandle, DiagMessage}; +use rustc_session::Session; +use rustc_span::edit_distance::edit_distance; +use rustc_span::source_map::Spanned; +use rustc_span::{BytePos, DUMMY_SP, Pos, Span, Symbol}; +use std::collections::{HashMap, HashSet}; +use std::fmt::{self, Display, Write}; +use std::hash::{BuildHasher, Hash}; +use std::marker::PhantomData; +use std::ops::{ControlFlow, Range}; +use toml_edit as toml; + +#[derive(Clone, Copy)] +pub enum Item<'a> { + None, + Value(&'a toml::Value), + Table(&'a toml::Table), + ArrayOfTables(&'a toml::ArrayOfTables), +} + +impl<'a> Item<'a> { + pub fn span(self) -> Option> { + match self { + Self::None => None, + Self::Value(x) => x.span(), + Self::Table(x) => x.span(), + Self::ArrayOfTables(x) => x.span(), + } + } + + pub fn as_bool(self) -> Option { + match self { + Self::Value(x) => x.as_bool(), + _ => None, + } + } + + pub fn as_integer(self) -> Option { + match self { + Self::Value(x) => x.as_integer(), + _ => None, + } + } + + pub fn as_float(self) -> Option { + match self { + Self::Value(x) => x.as_float(), + _ => None, + } + } + + pub fn as_str(self) -> Option<&'a str> { + match self { + Self::Value(x) => x.as_str(), + _ => None, + } + } + + pub fn as_inline_array(self) -> Option<&'a toml::Array> { + match self { + Self::Value(x) => x.as_array(), + _ => None, + } + } + + pub fn as_array(self) -> Option<(usize, Box>>)> { + match self { + Self::Value(toml::Value::Array(x)) => Some((x.len(), Box::new(x.iter().map(Item::Value)))), + Self::ArrayOfTables(x) => Some((x.len(), Box::new(x.iter().map(Item::Table)))), + _ => None, + } + } + + pub fn as_table_like(self) -> Option<(Option>, &'a dyn toml::TableLike)> { + match self { + Self::Value(toml::Value::InlineTable(x)) => Some((x.span(), x)), + Self::Table(x) => Some((x.span(), x)), + _ => None, + } + } +} + +impl<'a> From<&'a toml::Item> for Item<'a> { + fn from(x: &'a toml::Item) -> Self { + match x { + toml::Item::None => Self::None, + toml::Item::Value(x) => Self::Value(x), + toml::Item::Table(x) => Self::Table(x), + toml::Item::ArrayOfTables(x) => Self::ArrayOfTables(x), + } + } +} + +impl<'a> From<&'a toml::Value> for Item<'a> { + fn from(x: &'a toml::Value) -> Self { + Self::Value(x) + } +} + +pub struct DiagCtxt<'a> { + pub inner: DiagCtxtHandle<'a>, + pub width: usize, + offset: usize, +} +impl<'a> DiagCtxt<'a> { + pub fn new(sess: &'a Session, offset: usize) -> Self { + Self { + inner: sess.dcx(), + width: sess.diagnostic_width(), + offset, + } + } + + pub fn make_sp(&self, range: Option>) -> Span { + if let Some(range) = range { + Span::with_root_ctxt( + BytePos::from_usize(self.offset + range.start), + BytePos::from_usize(self.offset + range.end), + ) + } else { + // All values read from a toml file have a span. + // If somehow there isn't one, crashing is worse than having no span. + DUMMY_SP + } + } + + pub fn span_err(&self, range: Option>, msg: impl Into) { + self.inner.span_err(self.make_sp(range), msg); + } + + pub fn span_warn(&self, range: Option>, msg: impl Into) { + self.inner.span_warn(self.make_sp(range), msg); + } +} + +/// Attempts to find the closest matching string from the list. Returns `None` +/// if the edit distance is too large. +pub fn find_closest_match<'a>(s: &str, options: &[&'a str]) -> Option<&'a str> { + // Don't treat `_` to `-` and case conversions as an edit. + let mut s = s.replace('_', "-"); + s.make_ascii_lowercase(); + options + .iter() + .filter_map(|&option| edit_distance(&s, &option.to_ascii_lowercase(), 4).map(|x| (x, option))) + .min_by_key(|&(dist, _)| dist) + .map(|(_, x)| x) +} + +/// Creates a message listing all possible values suitable for use in `Diag::note`. +pub fn create_value_list_msg(diag_width: usize, values: &[&str]) -> String { + const NOTE_WITH_MSG_LEN: usize = " = note: possible values: ".len(); + const NOTE_LEN: usize = " = note: ".len(); + const TBL_SEP: &str = " "; + const INLINE_SEP: &str = ", "; + + // Print everything on one line if it will fit and there aren't too many values. + // e.g. "note: possible values: `value1`, `value2`, `value3`" + // + // If there are too many values of the note would exceed the terminal width lay the + // values out into columns. e.g. + // possible values: + // value1 value5 + // value2 value6 + // value3 value7 + // value4 + if values.len() <= 8 + && NOTE_WITH_MSG_LEN + values.iter().map(|x| x.len() + INLINE_SEP.len() + 2).sum::() <= diag_width + { + format!( + "possible values: {}", + values.iter().format_with(INLINE_SEP, |x, f| f(&format_args!("`{x}`"))), + ) + } else { + // The minimum width a column could possibly have. + let min_width = values.iter().map(|x| x.len()).min().unwrap_or(0); + // The number of columns that fit using the minimum width. + let max_col = diag_width.saturating_sub(NOTE_WITH_MSG_LEN) / (min_width + TBL_SEP.len()); + + // Determine the starting dimensions of the search. + let start_size = (2..=max_col).try_fold(values.len(), |row_count, col_count| { + let needed_rows = values.len().div_ceil(col_count); + // Only add a new column if it will remove several rows. + if needed_rows + 3 <= row_count { + ControlFlow::Continue(needed_rows) + } else { + ControlFlow::Break((row_count, col_count - 1)) + } + }); + let (mut row_count, init_col_count) = match start_size { + // Also handles the case where `max_col < 2` + ControlFlow::Continue(x) => (x, max_col), + ControlFlow::Break(x) => x, + }; + + // The current total width required. + let mut total_width = init_col_count * (min_width + TBL_SEP.len()) + NOTE_LEN; + // The current width of each column without the prefix. + let mut col_widths = vec![min_width; init_col_count]; + + // Determine the required width of each column. + 'outer: loop { + // Also handles the case where `max_col` is zero. + if col_widths.len() <= 1 { + return format!( + "possible values:\n{}", + values.iter().format_with("\n", |x, f| f(&format_args!("{x}"))), + ); + } + for (col_values, col_width) in values.chunks(row_count).zip(col_widths.iter_mut()) { + for value in col_values { + if value.len() > *col_width { + let delta = value.len() - *col_width; + *col_width += delta; + total_width += delta; + if total_width > diag_width { + // Remove a column and reset the metrics then retry. + col_widths.pop(); + row_count = values.len().div_ceil(col_widths.len()); + col_widths.fill(min_width); + total_width = col_widths.len() * (min_width + TBL_SEP.len()) + NOTE_LEN; + continue 'outer; + } + } + } + } + break; + } + + format!( + "possible values:\n{}", + (0..row_count).format_with("\n", |row, f| { + f(&(row..values.len()) + .step_by(row_count) + .zip(&col_widths) + .format_with(TBL_SEP, |(i, &width), f| { + // Don't print trailing whitespace on the right edge. + let width = if i + row_count < values.len() { width } else { 0 }; + f(&format_args!("{:width$}", values[i])) + })) + }), + ) + } +} + +/// A type which can be constructed from a default value. +pub trait FromDefault: Sized { + /// Creates this value from a default value. + fn from_default(default: T) -> Self; + /// Writes the default value to a string. + fn display_default(default: T) -> impl Display; +} + +impl FromDefault for bool { + fn from_default(default: bool) -> Self { + default + } + fn display_default(default: bool) -> impl Display { + default + } +} + +impl FromDefault for i64 { + fn from_default(default: i64) -> Self { + default + } + fn display_default(default: i64) -> impl Display { + default + } +} + +impl FromDefault for u64 { + fn from_default(default: u64) -> Self { + default + } + fn display_default(default: u64) -> impl Display { + default + } +} + +impl FromDefault for f64 { + fn from_default(default: f64) -> Self { + default + } + fn display_default(default: f64) -> impl Display { + default + } +} + +impl FromDefault<()> for Option { + fn from_default((): ()) -> Self { + None + } + fn display_default((): ()) -> impl Display { + // will cause an error in the metadata collector + "" + } +} + +struct DisplayStr(&'static str); +impl Display for DisplayStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0, f) + } +} + +impl FromDefault<&'static str> for String { + fn from_default(default: &'static str) -> Self { + default.into() + } + fn display_default(default: &'static str) -> impl Display { + DisplayStr(default) + } +} + +impl FromDefault<()> for Vec { + fn from_default((): ()) -> Self { + Vec::new() + } + fn display_default((): ()) -> impl Display { + "[]" + } +} + +impl FromDefault<()> for HashSet { + fn from_default((): ()) -> Self { + HashSet::default() + } + fn display_default((): ()) -> impl Display { + "[]" + } +} + +struct DisplaySlice(&'static [T], PhantomData); +impl Display for DisplaySlice +where + T: 'static + Copy, + U: FromDefault, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_char('[')?; + if let Some((head, tail)) = self.0.split_first() { + U::display_default(*head).fmt(f)?; + for x in tail { + write!(f, ", {}", U::display_default(*x))?; + } + } + f.write_char(']') + } +} + +impl FromDefault<&'static [U]> for Vec +where + T: FromDefault, + U: 'static + Copy, +{ + fn from_default(default: &'static [U]) -> Self { + default.iter().map(|&x| T::from_default(x)).collect() + } + fn display_default(default: &'static [U]) -> impl Display { + DisplaySlice::<_, T>(default, PhantomData) + } +} + +impl FromDefault<&'static [U]> for HashSet +where + T: FromDefault + Eq + Hash, + U: 'static + Copy, + S: Default + BuildHasher, +{ + fn from_default(default: &'static [U]) -> Self { + default.iter().map(|&x| T::from_default(x)).collect() + } + fn display_default(default: &'static [U]) -> impl Display { + DisplaySlice::<_, T>(default, PhantomData) + } +} + +/// A type which can be deserialized from a toml value. +pub trait Deserialize: Sized { + /// Attempt to deserialize the value. Returns `None` and raises an error on failure. + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option; +} + +impl Deserialize for bool { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + match value.as_bool() { + None => { + dcx.span_err(value.span(), "expected a boolean"); + None + }, + x => x, + } + } +} + +impl Deserialize for i64 { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + match value.as_integer() { + None => { + dcx.span_err(value.span(), "expected an integer"); + None + }, + x => x, + } + } +} + +impl Deserialize for u64 { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + match value.as_integer() { + Some(x) if let Ok(x) = u64::try_from(x) => Some(x), + _ => { + dcx.span_err(value.span(), "expected a non-negative integer"); + None + }, + } + } +} + +impl Deserialize for f64 { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + match value.as_float() { + None => { + dcx.span_err(value.span(), "expected a floating-point number"); + None + }, + x => x, + } + } +} + +impl Deserialize for String { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some(x) = value.as_str() { + Some(x.into()) + } else { + dcx.span_err(value.span(), "expected a string"); + None + } + } +} + +impl Deserialize for Symbol { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some(x) = value.as_str() { + Some(Symbol::intern(x)) + } else { + dcx.span_err(value.span(), "expected a string"); + None + } + } +} + +impl Deserialize for RustcVersion { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some(x) = value.as_str() { + if let Some(x) = parse_version(Symbol::intern(x)) { + Some(x) + } else { + dcx.span_err(value.span(), "failed to parse rust version"); + None + } + } else { + dcx.span_err(value.span(), "expected a version string"); + None + } + } +} + +impl Deserialize for [T; N] { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some((len, values)) = value.as_array() + && len == N + { + let values = values + .filter_map(|x| T::deserialize(dcx, x)) + .collect::>(); + // A value's deserialize impl will have already given an error + values.into_inner().ok() + } else { + dcx.span_err(value.span(), "expected an array of length `{N}`"); + None + } + } +} + +impl Deserialize for Vec { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some((_, values)) = value.as_array() { + Some(values.filter_map(|x| T::deserialize(dcx, x)).collect()) + } else { + dcx.span_err(value.span(), "expected an array"); + None + } + } +} + +impl Deserialize for HashSet +where + T: Deserialize + Eq + Hash, + S: Default + BuildHasher, +{ + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some((_, values)) = value.as_array() { + Some(values.filter_map(|x| T::deserialize(dcx, x)).collect()) + } else { + dcx.span_err(value.span(), "expected an array"); + None + } + } +} + +impl Deserialize for HashMap +where + T: Deserialize + Eq + Hash, + S: Default + BuildHasher, +{ + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some((_, values)) = value.as_array() { + Some( + values + .filter_map(|x| T::deserialize(dcx, x).map(|value| (value, dcx.make_sp(x.span())))) + .collect(), + ) + } else { + dcx.span_err(value.span(), "expected an array"); + None + } + } +} + +impl Deserialize for Spanned { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + T::deserialize(dcx, value).map(|x| Spanned { + node: x, + span: dcx.make_sp(value.span()), + }) + } +} + +/// A type which can be deserialized from a toml value with a fallback to a default value. +pub trait DeserializeOrDefault: Sized { + /// Attempt to deserialize the value. Returns the default value and raises an error on failure. + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: T) -> Self; +} + +impl> DeserializeOrDefault for T { + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: T) -> Self { + T::deserialize(dcx, value).unwrap_or_else(|| T::from_default(default)) + } +} + +impl DeserializeOrDefault<()> for T { + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, (): ()) -> Self { + T::deserialize(dcx, value).unwrap_or_default() + } +} + +impl DeserializeOrDefault<()> for Option { + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, (): ()) -> Self { + T::deserialize(dcx, value) + } +} + +pub fn deserialize_array(dcx: &DiagCtxt<'_>, value: Item<'_>, default: &'static [U]) -> C +where + T: Deserialize + FromDefault, + U: Copy, + C: FromIterator + Extend, +{ + let default_iter = default.iter().map(|&x| T::from_default(x)); + if let Some((_, values)) = value.as_array() { + let mut has_default = false; + let mut res: C = values + .filter(|x| { + if x.as_str().is_some_and(|x| x == "..") { + if has_default { + dcx.span_warn(value.span(), "duplicate `..` item"); + } + has_default = true; + false + } else { + true + } + }) + .filter_map(|x| T::deserialize(dcx, x)) + .collect(); + if has_default { + res.extend(default_iter); + } + res + } else { + dcx.span_err(value.span(), "expected an array"); + default_iter.collect() + } +} + +impl DeserializeOrDefault<&'static [U]> for Vec +where + T: Deserialize + FromDefault, + U: Copy, +{ + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: &'static [U]) -> Self { + deserialize_array(dcx, value, default) + } +} + +impl DeserializeOrDefault<&'static [U]> for HashSet +where + T: Deserialize + FromDefault + Eq + Hash, + U: Copy, + S: Default + BuildHasher, +{ + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: &'static [U]) -> Self { + deserialize_array(dcx, value, default) + } +} + +macro_rules! deserialize_table { + ($dcx:ident, $table:ident, $($name:ident($name_str:literal): $ty:ty,)+) => { + $(let mut $name: Option<$ty> = None;)+ + + for (key, value) in $table.iter() { + match key { + $($name_str => { + // Duplicate keys are handled by the toml parser + $name = <$ty as crate::de::Deserialize>::deserialize($dcx, value.into()); + },)+ + _ => { + const NAMES: &[&str] = &[$($name_str),*]; + let sp = $dcx.make_sp($table.get_key_value(key).unwrap().0.span()); + let mut diag = $dcx.inner.struct_span_err(sp, "unknown key"); + if let Some(sugg) = crate::de::find_closest_match(key, NAMES) { + diag.span_suggestion(sp, "did you mean", sugg, Applicability::MaybeIncorrect); + } + diag.note(crate::de::create_value_list_msg($dcx.width, NAMES)); + diag.emit(); + }, + } + } + } +} diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index c227b8900b74..a88f312cfba2 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, array_windows, let_chains)] +#![feature(rustc_private, array_try_map, array_windows, if_let_guard, io_error_more, let_chains)] #![warn( trivial_casts, trivial_numeric_casts, @@ -13,6 +13,7 @@ rustc::untranslatable_diagnostic )] +extern crate rustc_attr_parsing; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; @@ -20,9 +21,11 @@ extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; +#[macro_use] +mod de; mod conf; mod metadata; pub mod types; -pub use conf::{Conf, get_configuration_metadata, lookup_conf_file, sanitize_explanation}; -pub use metadata::ClippyConfiguration; +pub use conf::{Conf, sanitize_explanation}; +pub use metadata::ConfMetadata; diff --git a/clippy_config/src/metadata.rs b/clippy_config/src/metadata.rs index 7cbd92a9b710..6bff90815794 100644 --- a/clippy_config/src/metadata.rs +++ b/clippy_config/src/metadata.rs @@ -1,16 +1,15 @@ use itertools::Itertools; -use std::fmt; +use std::fmt::{self, Display}; -#[derive(Debug, Clone, Default)] -pub struct ClippyConfiguration { - pub name: String, +pub struct ConfMetadata { + pub name: &'static str, pub default: String, pub lints: &'static [&'static str], pub doc: &'static str, - pub deprecation_reason: Option<&'static str>, + pub renamed_to: Option<&'static str>, } -impl fmt::Display for ClippyConfiguration { +impl Display for ConfMetadata { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "- `{}`: {}", self.name, self.doc)?; if !self.default.is_empty() { @@ -20,21 +19,40 @@ impl fmt::Display for ClippyConfiguration { } } -impl ClippyConfiguration { - pub fn to_markdown_paragraph(&self) -> String { - format!( - "## `{}`\n{}\n\n**Default Value:** `{}`\n\n---\n**Affected lints:**\n{}\n\n", - self.name, - self.doc.lines().map(|x| x.strip_prefix(' ').unwrap_or(x)).join("\n"), - self.default, - self.lints.iter().format_with("\n", |name, f| f(&format_args!( - "* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})" - ))), - ) +impl ConfMetadata { + pub fn display_markdown_paragraph(&self) -> impl '_ + Display { + struct S<'a>(&'a ConfMetadata); + impl Display for S<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "## `{}`\n{}\n\n**Default Value:** `{}`\n\n---\n**Affected lints:**\n{}\n\n", + self.0.name, + self.0 + .doc + .lines() + .format_with("\n", |doc, f| f(&doc.strip_prefix(" ").unwrap_or(doc))), + self.0.default, + self.0.lints.iter().format_with("\n", |name, f| f(&format_args!( + "* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})" + ))), + ) + } + } + S(self) } - pub fn to_markdown_link(&self) -> String { - const BOOK_CONFIGS_PATH: &str = "https://doc.rust-lang.org/clippy/lint_configuration.html"; - format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name) + pub fn display_markdown_link(&self) -> impl '_ + Display { + struct S<'a>(&'a ConfMetadata); + impl Display for S<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "[`{}`]: https://doc.rust-lang.org/clippy/lint_configuration.html#{}", + self.0.name, self.0.name, + ) + } + } + S(self) } } diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 5949eaca7bc1..42b3d8af8aa8 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,24 +1,130 @@ -use rustc_data_structures::fx::FxHashMap; +use crate::de::{ + Deserialize, DeserializeOrDefault, DiagCtxt, FromDefault, Item, create_value_list_msg, find_closest_match, +}; +use core::fmt::{self, Display}; +use itertools::Itertools; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{Applicability, Diag}; use rustc_hir::PrimTy; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; +use rustc_session::Session; use rustc_span::Span; -use serde::de::{self, Deserializer, Visitor}; -use serde::{Deserialize, Serialize, ser}; +use rustc_span::source_map::Spanned; use std::collections::HashMap; -use std::fmt; +use toml_edit as toml; + +macro_rules! concat_expr { + ($($e:expr)*) => { + concat!($($e),*) + } +} + +macro_rules! name_or_lit { + ($name:ident) => { + stringify!($name) + }; + ($name:ident $lit:literal) => { + $lit + }; +} + +macro_rules! conf_enum { + ( + $(#[$attrs:meta])* + $vis:vis $name:ident {$( + $(#[$var_attrs:meta])* + $var_name:ident $(($var_lit:literal))?, + )*} + ) => { + $(#[$attrs])* + #[derive(Clone, Copy)] + $vis enum $name {$( + $(#[$var_attrs])* + $var_name, + )*} + impl $name { + const NAMES: &[&'static str] = &[$(name_or_lit!($var_name $($var_lit)?)),*]; + #[allow(dead_code)] + const COUNT: usize = { + enum __ITEMS__ { $($var_name,)* __COUNT__ } + __ITEMS__::__COUNT__ as usize + }; -#[derive(Debug, Deserialize)] + pub fn name(self) -> &'static str { + Self::NAMES[self as usize] + } + #[allow(clippy::should_implement_trait)] + pub fn from_str(s: &str) -> Option { + match s { + $(name_or_lit!($var_name $($var_lit)?) => Some(Self::$var_name),)* + _ => None, + } + } + } + impl FromDefault<$name> for $name { + fn from_default(default: $name) -> Self { + default + } + fn display_default(default: $name) -> impl Display { + String::display_default(default.name()) + } + } + impl Deserialize for $name { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + let Some(s) = value.as_str() else { + dcx.span_err(value.span(), "expected a string"); + return None; + }; + let x = Self::from_str(s); + if x.is_none() { + let sp = dcx.make_sp(value.span()); + let mut diag = dcx.inner.struct_span_err( + sp, + concat_expr!("expected one of: " $("`" name_or_lit!($var_name $($var_lit)?) "`")", "*), + ); + if let Some(sugg) = find_closest_match(s, Self::NAMES) { + diag.span_suggestion(sp, "did you mean", sugg, Applicability::MaybeIncorrect); + } + diag.note(create_value_list_msg(dcx.width, Self::NAMES)); + diag.emit(); + } + x + } + } + }; +} pub struct Rename { pub path: String, pub rename: String, } +impl Deserialize for Rename { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some((span, table)) = value.as_table_like() { + deserialize_table!(dcx, table, + path("path"): String, + rename("rename"): String, + ); + let Some(path) = path else { + dcx.span_err(span.clone(), "missing required field `path`"); + return None; + }; + let Some(rename) = rename else { + dcx.span_err(span.clone(), "missing required field `rename`"); + return None; + }; + Some(Rename { path, rename }) + } else { + dcx.span_err(value.span(), "expected an inline table"); + None + } + } +} + pub type DisallowedPathWithoutReplacement = DisallowedPath; -#[derive(Debug, Serialize)] pub struct DisallowedPath { path: String, reason: Option, @@ -32,44 +138,9 @@ pub struct DisallowedPath { /// The span of the `DisallowedPath`. /// /// Used for diagnostics. - #[serde(skip_serializing)] span: Span, } -impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let enum_ = DisallowedPathEnum::deserialize(deserializer)?; - if !REPLACEMENT_ALLOWED && enum_.replacement().is_some() { - return Err(de::Error::custom("replacement not allowed for this configuration")); - } - Ok(Self { - path: enum_.path().to_owned(), - reason: enum_.reason().map(ToOwned::to_owned), - replacement: enum_.replacement().map(ToOwned::to_owned), - allow_invalid: enum_.allow_invalid(), - span: Span::default(), - }) - } -} - -// `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just -// above. `DisallowedPathEnum` is not meant to be used outside of this file. -#[derive(Debug, Deserialize, Serialize)] -#[serde(untagged)] -enum DisallowedPathEnum { - Simple(String), - WithReason { - path: String, - reason: Option, - replacement: Option, - #[serde(rename = "allow-invalid")] - allow_invalid: Option, - }, -} - impl DisallowedPath { pub fn path(&self) -> &str { &self.path @@ -99,43 +170,84 @@ impl DisallowedPath { } } -impl DisallowedPathEnum { - pub fn path(&self) -> &str { - let (Self::Simple(path) | Self::WithReason { path, .. }) = self; - - path - } - - fn reason(&self) -> Option<&str> { - match &self { - Self::WithReason { reason, .. } => reason.as_deref(), - Self::Simple(_) => None, - } - } - - fn replacement(&self) -> Option<&str> { - match &self { - Self::WithReason { replacement, .. } => replacement.as_deref(), - Self::Simple(_) => None, +impl Deserialize for DisallowedPath { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some(s) = value.as_str() { + Some(DisallowedPath { + path: s.into(), + reason: None, + replacement: None, + allow_invalid: false, + span: dcx.make_sp(value.span()), + }) + } else if let Some((span, table)) = value.as_table_like() { + deserialize_table!(dcx, table, + path("path"): String, + reason("reason"): String, + allow_invalid("allow-invalid"): bool, + ); + let Some(path) = path else { + dcx.span_err(span, "missing required field `path`"); + return None; + }; + Some(DisallowedPath { + path, + reason, + replacement: None, + allow_invalid: allow_invalid.unwrap_or(false), + span: dcx.make_sp(value.span()), + }) + } else { + dcx.span_err(value.span(), "expected either a string or an inline table"); + None } } +} - fn allow_invalid(&self) -> bool { - match &self { - Self::WithReason { allow_invalid, .. } => allow_invalid.unwrap_or_default(), - Self::Simple(_) => false, +impl Deserialize for DisallowedPath { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some(s) = value.as_str() { + Some(DisallowedPath { + path: s.into(), + reason: None, + replacement: None, + allow_invalid: false, + span: dcx.make_sp(value.span()), + }) + } else if let Some((span, table)) = value.as_table_like() { + deserialize_table!(dcx, table, + path("path"): String, + reason("reason"): String, + replacement("replacement"): String, + allow_invalid("allow-invalid"): bool, + ); + let Some(path) = path else { + dcx.span_err(span, "missing required field `path`"); + return None; + }; + Some(DisallowedPath { + path, + reason, + replacement, + allow_invalid: allow_invalid.unwrap_or(false), + span: dcx.make_sp(value.span()), + }) + } else { + dcx.span_err(value.span(), "expected either a string or an inline table"); + None } } } /// Creates a map of disallowed items to the reason they were disallowed. #[allow(clippy::type_complexity)] -pub fn create_disallowed_map( - tcx: TyCtxt<'_>, +pub fn create_disallowed_map<'tcx, const REPLACEMENT_ALLOWED: bool>( + tcx: TyCtxt<'tcx>, disallowed_paths: &'static [DisallowedPath], def_kind_predicate: impl Fn(DefKind) -> bool, predicate_description: &str, allow_prim_tys: bool, + resolve_path: fn(TyCtxt<'tcx>, &[&str]) -> Vec, ) -> ( DefIdMap<(&'static str, &'static DisallowedPath)>, FxHashMap)>, @@ -145,7 +257,7 @@ pub fn create_disallowed_map( FxHashMap::default(); for disallowed_path in disallowed_paths { let path = disallowed_path.path(); - let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::>()); + let mut resolutions = resolve_path(tcx, &path.split("::").collect::>()); let mut found_def_id = None; let mut found_prim_ty = false; @@ -202,87 +314,92 @@ pub fn create_disallowed_map( (def_ids, prim_tys) } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum MatchLintBehaviour { - AllTypes, - WellKnownTypes, - Never, +conf_enum! { + #[derive(PartialEq, Eq)] + pub MatchLintBehaviour { + AllTypes, + WellKnownTypes, + Never, + } +} + +enum BraceKind { + Brace, + Bracket, + Paren, +} + +impl Deserialize for BraceKind { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + let msg = if let Some(s) = value.as_str() { + match s { + "{" | "{}" => return Some(BraceKind::Brace), + "[" | "[]" => return Some(BraceKind::Bracket), + "(" | "()" => return Some(BraceKind::Paren), + _ => "unknown value", + } + } else { + "expected a string" + }; + let mut diag = dcx.inner.struct_span_err(dcx.make_sp(value.span()), msg); + diag.note("possible values: `()`, `[]`, `{}`"); + diag.emit(); + None + } } -#[derive(Debug)] pub struct MacroMatcher { pub name: String, pub braces: (char, char), } -impl<'de> Deserialize<'de> for MacroMatcher { - fn deserialize(deser: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] - enum Field { - Name, - Brace, - } - struct MacVisitor; - impl<'de> Visitor<'de> for MacVisitor { - type Value = MacroMatcher; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("struct MacroMatcher") - } - - fn visit_map(self, mut map: V) -> Result - where - V: de::MapAccess<'de>, - { - let mut name = None; - let mut brace: Option = None; - while let Some(key) = map.next_key()? { - match key { - Field::Name => { - if name.is_some() { - return Err(de::Error::duplicate_field("name")); - } - name = Some(map.next_value()?); - }, - Field::Brace => { - if brace.is_some() { - return Err(de::Error::duplicate_field("brace")); - } - brace = Some(map.next_value()?); - }, - } - } - let name = name.ok_or_else(|| de::Error::missing_field("name"))?; - let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?; - Ok(MacroMatcher { - name, - braces: [('(', ')'), ('{', '}'), ('[', ']')] - .into_iter() - .find(|b| b.0 == brace) - .map(|(o, c)| (o.to_owned(), c.to_owned())) - .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, - }) - } +impl Deserialize for MacroMatcher { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + if let Some((span, table)) = value.as_table_like() { + deserialize_table!(dcx, table, + name("name"): String, + brace("brace"): BraceKind, + ); + let Some(name) = name else { + dcx.span_err(span, "missing required field `name`"); + return None; + }; + let Some(brace) = brace else { + dcx.span_err(span, "missing required field `brace`"); + return None; + }; + Some(MacroMatcher { + name, + braces: match brace { + BraceKind::Brace => ('{', '}'), + BraceKind::Bracket => ('[', ']'), + BraceKind::Paren => ('(', ')'), + }, + }) + } else { + dcx.span_err(value.span(), "expected an inline table"); + None } + } +} - const FIELDS: &[&str] = &["name", "brace"]; - deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor) +conf_enum! { + pub PubUnderscoreFieldsBehaviour { + PubliclyExported, + AllPubFields, } } -/// Represents the item categories that can be ordered by the source ordering lint. -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum SourceItemOrderingCategory { - Enum, - Impl, - Module, - Struct, - Trait, +conf_enum! { + /// Represents the item categories that can be ordered by the source ordering lint. + #[derive(Debug, PartialEq, Eq, Hash)] + pub SourceItemOrderingCategory { + Enum("enum"), + Impl("impl"), + Module("module"), + Struct("struct"), + Trait("trait"), + } } /// Represents which item categories are enabled for ordering. @@ -292,76 +409,78 @@ pub enum SourceItemOrderingCategory { pub struct SourceItemOrdering(Vec); impl SourceItemOrdering { - pub fn contains(&self, category: &SourceItemOrderingCategory) -> bool { - self.0.contains(category) - } -} - -impl From for SourceItemOrdering -where - T: Into>, -{ - fn from(value: T) -> Self { - Self(value.into()) + pub fn contains(&self, category: SourceItemOrderingCategory) -> bool { + self.0.contains(&category) } } -impl core::fmt::Debug for SourceItemOrdering { +impl fmt::Debug for SourceItemOrdering { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } -impl<'de> Deserialize<'de> for SourceItemOrdering { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let items = Vec::::deserialize(deserializer)?; - let mut items_set = std::collections::HashSet::new(); +impl Deserialize for SourceItemOrdering { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + let items = Vec::::deserialize(dcx, value)?; + let mut items_set = FxHashSet::default(); for item in &items { if items_set.contains(item) { - return Err(de::Error::custom(format!( - "The category \"{item:?}\" was enabled more than once in the source ordering configuration." - ))); + dcx.span_err( + value.span(), + format!( + "The category \"{}\" was enabled more than once in the source ordering configuration.", + item.name() + ), + ); + return None; } items_set.insert(item); } - - Ok(Self(items)) + Some(SourceItemOrdering(items)) } } - -impl Serialize for SourceItemOrdering { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - self.0.serialize(serializer) +impl FromDefault<()> for SourceItemOrdering { + fn from_default((): ()) -> Self { + Self(vec![ + SourceItemOrderingCategory::Enum, + SourceItemOrderingCategory::Impl, + SourceItemOrderingCategory::Module, + SourceItemOrderingCategory::Struct, + SourceItemOrderingCategory::Trait, + ]) + } + fn display_default((): ()) -> impl Display { + r#"["enum", "impl", "module", "struct", "trait"]"# + } +} +impl DeserializeOrDefault<()> for SourceItemOrdering { + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: ()) -> Self { + Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default)) } } -/// Represents the items that can occur within a module. -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum SourceItemOrderingModuleItemKind { - ExternCrate, - Mod, - ForeignMod, - Use, - Macro, - GlobalAsm, - Static, - Const, - TyAlias, - Enum, - Struct, - Union, - Trait, - TraitAlias, - Impl, - Fn, +conf_enum! { + #[derive(Debug, PartialEq, Eq, Hash)] + pub SourceItemOrderingModuleItemKind { + ExternCrate("extern_crate"), + Mod("mod"), + ForeignMod("foreign_mod"), + Use("use"), + Macro("macro"), + GlobalAsm("global_asm"), + Static("static"), + Const("const"), + TyAlias("ty_alias"), + Enum("enum"), + Struct("struct"), + Union("union"), + Trait("trait"), + TraitAlias("trait_alias"), + Impl("impl"), + Fn("fn"), + } } impl SourceItemOrderingModuleItemKind { @@ -400,14 +519,20 @@ pub struct SourceItemOrderingModuleItemGroupings { back_lut: HashMap, } +impl fmt::Debug for SourceItemOrderingModuleItemGroupings { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.groups.fmt(f) + } +} + impl SourceItemOrderingModuleItemGroupings { fn build_lut( groups: &[(String, Vec)], ) -> HashMap { let mut lut = HashMap::new(); for (group_index, (_, items)) in groups.iter().enumerate() { - for item in items { - lut.insert(item.clone(), group_index); + for &item in items { + lut.insert(item, group_index); } } lut @@ -418,15 +543,15 @@ impl SourceItemOrderingModuleItemGroupings { ) -> HashMap { let mut lut = HashMap::new(); for (group_name, items) in groups { - for item in items { - lut.insert(item.clone(), group_name.clone()); + for &item in items { + lut.insert(item, group_name.clone()); } } lut } - pub fn grouping_name_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<&String> { - self.back_lut.get(item) + pub fn grouping_name_of(&self, item: SourceItemOrderingModuleItemKind) -> Option<&String> { + self.back_lut.get(&item) } pub fn grouping_names(&self) -> Vec { @@ -437,33 +562,33 @@ impl SourceItemOrderingModuleItemGroupings { self.groups.iter().any(|(g, _)| g == grouping) } - pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option { - self.lut.get(item).copied() - } -} - -impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrderingModuleItemGroupings { - fn from(value: &[(&str, &[SourceItemOrderingModuleItemKind])]) -> Self { - let groups: Vec<(String, Vec)> = - value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect(); - let lut = Self::build_lut(&groups); - let back_lut = Self::build_back_lut(&groups); - Self { groups, lut, back_lut } + pub fn module_level_order_of(&self, item: SourceItemOrderingModuleItemKind) -> Option { + self.lut.get(&item).copied() } } -impl core::fmt::Debug for SourceItemOrderingModuleItemGroupings { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.groups.fmt(f) - } -} +impl Deserialize for SourceItemOrderingModuleItemGroupings { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + let Some(values) = value.as_inline_array() else { + dcx.span_err(value.span(), "expected an array"); + return None; + }; + let mut groups = Vec::with_capacity(values.len()); + for value in values { + let toml::Value::Array(values) = value else { + dcx.span_err(value.span(), "expected an array of length two"); + return None; + }; + if values.len() != 2 { + dcx.span_err(value.span(), "expected an array of length two"); + return None; + } + groups.push(( + String::deserialize(dcx, Item::Value(values.get(0).unwrap()))?, + Vec::::deserialize(dcx, Item::Value(values.get(1).unwrap()))?, + )); + } -impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let groups = Vec::<(String, Vec)>::deserialize(deserializer)?; let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum(); let lut = Self::build_lut(&groups); let back_lut = Self::build_back_lut(&groups); @@ -476,51 +601,134 @@ impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { let all_items = SourceItemOrderingModuleItemKind::all_variants(); if expected_items.is_empty() && items_total == all_items.len() { let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else { - return Err(de::Error::custom("Error in internal LUT.")); + dcx.span_err(value.span(), "Error in internal LUT."); + return None; }; let Some((_, use_group_items)) = groups.get(*use_group_index) else { - return Err(de::Error::custom("Error in internal LUT.")); + dcx.span_err(value.span(), "Error in internal LUT."); + return None; }; if use_group_items.len() > 1 { - return Err(de::Error::custom( + dcx.span_err( + value.span(), "The group containing the \"use\" item kind may not contain any other item kinds. \ The \"use\" items will (generally) be sorted by rustfmt already. \ Therefore it makes no sense to implement linting rules that may conflict with rustfmt.", - )); + ); + return None; } - - Ok(Self { groups, lut, back_lut }) + Some(Self { groups, lut, back_lut }) } else if items_total != all_items.len() { - Err(de::Error::custom(format!( - "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \ - The module item kinds are: {all_items:?}" - ))) + dcx.span_err(value.span(), + format!( + "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \ + The module item kinds are: {all_items:?}" + ) + ); + None } else { - Err(de::Error::custom(format!( - "Not all module item kinds were part of the configured source ordering rule. \ - All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ - The module item kinds are: {all_items:?}" - ))) + dcx.span_err(value.span(), + format!( + "Not all module item kinds were part of the configured source ordering rule. \ + All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ + The module item kinds are: {all_items:?}" + ) + ); + None } } } - -impl Serialize for SourceItemOrderingModuleItemGroupings { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - self.groups.serialize(serializer) +impl FromDefault<()> for SourceItemOrderingModuleItemGroupings { + fn from_default((): ()) -> Self { + Self { + groups: vec![ + ( + "modules".into(), + vec![ + SourceItemOrderingModuleItemKind::ExternCrate, + SourceItemOrderingModuleItemKind::Mod, + SourceItemOrderingModuleItemKind::ForeignMod, + ], + ), + ("use".into(), vec![SourceItemOrderingModuleItemKind::Use]), + ("macros".into(), vec![SourceItemOrderingModuleItemKind::Macro]), + ("global_asm".into(), vec![SourceItemOrderingModuleItemKind::GlobalAsm]), + ( + "UPPER_SNAKE_CASE".into(), + vec![ + SourceItemOrderingModuleItemKind::Static, + SourceItemOrderingModuleItemKind::Const, + ], + ), + ( + "PascalCase".into(), + vec![ + SourceItemOrderingModuleItemKind::TyAlias, + SourceItemOrderingModuleItemKind::Enum, + SourceItemOrderingModuleItemKind::Struct, + SourceItemOrderingModuleItemKind::Union, + SourceItemOrderingModuleItemKind::Trait, + SourceItemOrderingModuleItemKind::TraitAlias, + SourceItemOrderingModuleItemKind::Impl, + ], + ), + ("lower_snake_case".into(), vec![SourceItemOrderingModuleItemKind::Fn]), + ], + lut: HashMap::from_iter([ + (SourceItemOrderingModuleItemKind::ExternCrate, 0), + (SourceItemOrderingModuleItemKind::Mod, 0), + (SourceItemOrderingModuleItemKind::ForeignMod, 0), + (SourceItemOrderingModuleItemKind::Use, 1), + (SourceItemOrderingModuleItemKind::Macro, 2), + (SourceItemOrderingModuleItemKind::GlobalAsm, 3), + (SourceItemOrderingModuleItemKind::Static, 4), + (SourceItemOrderingModuleItemKind::Const, 4), + (SourceItemOrderingModuleItemKind::TyAlias, 5), + (SourceItemOrderingModuleItemKind::Enum, 5), + (SourceItemOrderingModuleItemKind::Struct, 5), + (SourceItemOrderingModuleItemKind::Union, 5), + (SourceItemOrderingModuleItemKind::Trait, 5), + (SourceItemOrderingModuleItemKind::TraitAlias, 5), + (SourceItemOrderingModuleItemKind::Impl, 5), + (SourceItemOrderingModuleItemKind::Fn, 6), + ]), + back_lut: HashMap::from_iter([ + (SourceItemOrderingModuleItemKind::ExternCrate, "modules".into()), + (SourceItemOrderingModuleItemKind::Mod, "modules".into()), + (SourceItemOrderingModuleItemKind::ForeignMod, "modules".into()), + (SourceItemOrderingModuleItemKind::Use, "use".into()), + (SourceItemOrderingModuleItemKind::Macro, "macros".into()), + (SourceItemOrderingModuleItemKind::GlobalAsm, "global_asm".into()), + (SourceItemOrderingModuleItemKind::Static, "UPPER_SNAKE_CASE".into()), + (SourceItemOrderingModuleItemKind::Const, "UPPER_SNAKE_CASE".into()), + (SourceItemOrderingModuleItemKind::TyAlias, "PascalCase".into()), + (SourceItemOrderingModuleItemKind::Enum, "PascalCase".into()), + (SourceItemOrderingModuleItemKind::Struct, "PascalCase".into()), + (SourceItemOrderingModuleItemKind::Union, "PascalCase".into()), + (SourceItemOrderingModuleItemKind::Trait, "PascalCase".into()), + (SourceItemOrderingModuleItemKind::TraitAlias, "PascalCase".into()), + (SourceItemOrderingModuleItemKind::Impl, "PascalCase".into()), + (SourceItemOrderingModuleItemKind::Fn, "lower_snake_case".into()), + ]), + } + } + fn display_default((): ()) -> impl Display { + r#"[["modules", ["extern_crate", "mod", "foreign_mod"]], ["use", ["use"]], ["macros", ["macro"]], ["global_asm", ["global_asm"]], ["UPPER_SNAKE_CASE", ["static", "const"]], ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]]]"# + } +} +impl DeserializeOrDefault<()> for SourceItemOrderingModuleItemGroupings { + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: ()) -> Self { + Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default)) } } -/// Represents all kinds of trait associated items. -#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum SourceItemOrderingTraitAssocItemKind { - Const, - Fn, - Type, +conf_enum! { + #[derive(Debug, PartialEq)] + pub SourceItemOrderingTraitAssocItemKind { + Const("const"), + Fn("fn"), + Type("type"), + } } impl SourceItemOrderingTraitAssocItemKind { @@ -541,33 +749,21 @@ impl SourceItemOrderingTraitAssocItemKind { #[derive(Clone)] pub struct SourceItemOrderingTraitAssocItemKinds(Vec); -impl SourceItemOrderingTraitAssocItemKinds { - pub fn index_of(&self, item: &SourceItemOrderingTraitAssocItemKind) -> Option { - self.0.iter().position(|i| i == item) - } -} - -impl From for SourceItemOrderingTraitAssocItemKinds -where - T: Into>, -{ - fn from(value: T) -> Self { - Self(value.into()) +impl fmt::Debug for SourceItemOrderingTraitAssocItemKinds { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) } } -impl core::fmt::Debug for SourceItemOrderingTraitAssocItemKinds { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) +impl SourceItemOrderingTraitAssocItemKinds { + pub fn index_of(&self, item: SourceItemOrderingTraitAssocItemKind) -> Option { + self.0.iter().position(|&i| i == item) } } -impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let items = Vec::::deserialize(deserializer)?; +impl Deserialize for SourceItemOrderingTraitAssocItemKinds { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + let items = Vec::::deserialize(dcx, value)?; let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants(); for item in &items { @@ -576,28 +772,44 @@ impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds { let all_items = SourceItemOrderingTraitAssocItemKind::all_variants(); if expected_items.is_empty() && items.len() == all_items.len() { - Ok(Self(items)) + Some(Self(items)) } else if items.len() != all_items.len() { - Err(de::Error::custom(format!( - "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \ - The trait associated item kinds are: {all_items:?}", - ))) + dcx.span_err( + value.span(), + format!( + "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \ + The trait associated item kinds are: {all_items:?}", + ) + ); + None } else { - Err(de::Error::custom(format!( - "Not all trait associated item kinds were part of the configured source ordering rule. \ - All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ - The trait associated item kinds are: {all_items:?}" - ))) + dcx.span_err( + value.span(), + format!( + "Not all trait associated item kinds were part of the configured source ordering rule. \ + All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ + The trait associated item kinds are: {all_items:?}" + ) + ); + None } } } - -impl Serialize for SourceItemOrderingTraitAssocItemKinds { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - self.0.serialize(serializer) +impl FromDefault<()> for SourceItemOrderingTraitAssocItemKinds { + fn from_default((): ()) -> Self { + Self(vec![ + SourceItemOrderingTraitAssocItemKind::Const, + SourceItemOrderingTraitAssocItemKind::Type, + SourceItemOrderingTraitAssocItemKind::Fn, + ]) + } + fn display_default((): ()) -> impl Display { + r#"["const", "type", "fn"]"# + } +} +impl DeserializeOrDefault<()> for SourceItemOrderingTraitAssocItemKinds { + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: ()) -> Self { + Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default)) } } @@ -617,7 +829,7 @@ pub enum SourceItemOrderingWithinModuleItemGroupings { None, /// Only the specified groupings should have their order checked. - Custom(Vec), + Custom(Vec>), } impl SourceItemOrderingWithinModuleItemGroupings { @@ -625,83 +837,66 @@ impl SourceItemOrderingWithinModuleItemGroupings { match self { SourceItemOrderingWithinModuleItemGroupings::All => true, SourceItemOrderingWithinModuleItemGroupings::None => false, - SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => groups.contains(grouping_name), + SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => { + groups.iter().any(|x| x.node == *grouping_name) + }, } } -} -/// Helper struct for deserializing the [`SourceItemOrderingWithinModuleItemGroupings`]. -#[derive(Deserialize)] -#[serde(untagged)] -enum StringOrVecOfString { - String(String), - Vec(Vec), + pub fn check_groupings(&self, sess: &Session, module_item_order_groupings: &SourceItemOrderingModuleItemGroupings) { + if let SourceItemOrderingWithinModuleItemGroupings::Custom(groupings) = self { + for grouping in groupings { + if !module_item_order_groupings.is_grouping(&grouping.node) { + // Since this isn't fixable by rustfix, don't emit a `Suggestion`. This just adds some useful + // info for the user instead. + let names = module_item_order_groupings + .groups + .iter() + .map(|(x, _)| &**x) + .collect::>(); + let suggestion = find_closest_match(&grouping.node, &names) + .map(|s| format!(" perhaps you meant `{s}`?")) + .unwrap_or_default(); + let names = names.iter().map(|s| format!("`{s}`")).join(", "); + sess.dcx().span_err(grouping.span, format!( + "unknown ordering group: `{}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}", + grouping.node, + )); + } + } + } + } } -impl<'de> Deserialize<'de> for SourceItemOrderingWithinModuleItemGroupings { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let description = "The available options for configuring an ordering within module item groups are: \ - \"all\", \"none\", or a list of module item group names \ - (as configured with the `module-item-order-groupings` configuration option)."; - - match StringOrVecOfString::deserialize(deserializer) { - Ok(StringOrVecOfString::String(preset)) if preset == "all" => { - Ok(SourceItemOrderingWithinModuleItemGroupings::All) - }, - Ok(StringOrVecOfString::String(preset)) if preset == "none" => { - Ok(SourceItemOrderingWithinModuleItemGroupings::None) +impl Deserialize for SourceItemOrderingWithinModuleItemGroupings { + fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { + match value { + Item::Value(toml::Value::String(value)) => match &**value.value() { + "all" => Some(Self::All), + "none" => Some(Self::None), + _ => { + dcx.span_err(value.span(), "expected: `all`, `none` or a list of category names"); + None + }, }, - Ok(StringOrVecOfString::String(preset)) => Err(de::Error::custom(format!( - "Unknown configuration option: {preset}.\n{description}" - ))), - Ok(StringOrVecOfString::Vec(groupings)) => { - Ok(SourceItemOrderingWithinModuleItemGroupings::Custom(groupings)) + Item::Value(toml::Value::Array(_)) => Vec::deserialize(dcx, value).map(Self::Custom), + _ => { + dcx.span_err(value.span(), "expected a string or an array of strings"); + None }, - Err(e) => Err(de::Error::custom(format!("{e}\n{description}"))), } } } - -impl Serialize for SourceItemOrderingWithinModuleItemGroupings { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - match self { - SourceItemOrderingWithinModuleItemGroupings::All => serializer.serialize_str("all"), - SourceItemOrderingWithinModuleItemGroupings::None => serializer.serialize_str("none"), - SourceItemOrderingWithinModuleItemGroupings::Custom(vec) => vec.serialize(serializer), - } +impl FromDefault<()> for SourceItemOrderingWithinModuleItemGroupings { + fn from_default((): ()) -> Self { + SourceItemOrderingWithinModuleItemGroupings::None } -} - -// these impls are never actually called but are used by the various config options that default to -// empty lists -macro_rules! unimplemented_serialize { - ($($t:ty,)*) => { - $( - impl Serialize for $t { - fn serialize(&self, _serializer: S) -> Result - where - S: ser::Serializer, - { - Err(ser::Error::custom("unimplemented")) - } - } - )* + fn display_default((): ()) -> impl Display { + r#""none""# } } - -unimplemented_serialize! { - Rename, - MacroMatcher, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum PubUnderscoreFieldsBehaviour { - PubliclyExported, - AllPubFields, +impl DeserializeOrDefault<()> for SourceItemOrderingWithinModuleItemGroupings { + fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: Item<'_>, default: ()) -> Self { + Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default)) + } } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 96e12706c9e2..21286f18f057 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -254,13 +254,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { Pass::Early => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), Pass::Late => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), }; - let (msrv_ty, msrv_ctor, extract_msrv) = match lint.pass { - Pass::Early => ( - "MsrvStack", - "MsrvStack::new(conf.msrv)", - "\n extract_msrv_attr!();\n", - ), - Pass::Late => ("Msrv", "conf.msrv", ""), + let (msrv_ty, extract_msrv) = match lint.pass { + Pass::Early => ("MsrvStack", "\n extract_msrv_attr!();\n"), + Pass::Late => ("Msrv", ""), }; let lint_name = lint.name; @@ -304,7 +300,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { impl {name_camel} {{ pub fn new(conf: &'static Conf) -> Self {{ - Self {{ msrv: {msrv_ctor} }} + Self {{ msrv: conf.msrv.into() }} }} }} diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs index 1af6d448a93c..ac1ec4660cd9 100644 --- a/clippy_lints/src/absolute_paths.rs +++ b/clippy_lints/src/absolute_paths.rs @@ -55,19 +55,15 @@ declare_clippy_lint! { impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]); pub struct AbsolutePaths { - pub absolute_paths_max_segments: u64, - pub absolute_paths_allowed_crates: FxHashSet, + absolute_paths_max_segments: u64, + absolute_paths_allowed_crates: &'static FxHashSet, } impl AbsolutePaths { pub fn new(conf: &'static Conf) -> Self { Self { absolute_paths_max_segments: conf.absolute_paths_max_segments, - absolute_paths_allowed_crates: conf - .absolute_paths_allowed_crates - .iter() - .map(|x| Symbol::intern(x)) - .collect(), + absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates, } } } diff --git a/clippy_lints/src/almost_complete_range.rs b/clippy_lints/src/almost_complete_range.rs index 4f55968d5625..563db89f9535 100644 --- a/clippy_lints/src/almost_complete_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -35,9 +35,7 @@ pub struct AlmostCompleteRange { } impl AlmostCompleteRange { pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: MsrvStack::new(conf.msrv), - } + Self { msrv: conf.msrv.into() } } } impl EarlyLintPass for AlmostCompleteRange { diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 9ae746c13b26..dfe74dfda05a 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -69,7 +69,7 @@ pub struct ApproxConstant { impl ApproxConstant { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 272444475c0c..f30c881ed0ff 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -11,6 +11,7 @@ use rustc_hir::{ VariantData, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -172,16 +173,18 @@ pub struct ArbitrarySourceItemOrdering { } impl ArbitrarySourceItemOrdering { - pub fn new(conf: &'static Conf) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. use SourceItemOrderingCategory::*; + conf.module_items_ordered_within_groupings + .check_groupings(tcx.sess, &conf.module_item_order_groupings); Self { assoc_types_order: conf.trait_assoc_item_kinds_order.clone(), - enable_ordering_for_enum: conf.source_item_ordering.contains(&Enum), - enable_ordering_for_impl: conf.source_item_ordering.contains(&Impl), - enable_ordering_for_module: conf.source_item_ordering.contains(&Module), - enable_ordering_for_struct: conf.source_item_ordering.contains(&Struct), - enable_ordering_for_trait: conf.source_item_ordering.contains(&Trait), + enable_ordering_for_enum: conf.source_item_ordering.contains(Enum), + enable_ordering_for_impl: conf.source_item_ordering.contains(Impl), + enable_ordering_for_module: conf.source_item_ordering.contains(Module), + enable_ordering_for_struct: conf.source_item_ordering.contains(Struct), + enable_ordering_for_trait: conf.source_item_ordering.contains(Trait), module_item_order_groupings: conf.module_item_order_groupings.clone(), module_items_ordered_within_groupings: conf.module_items_ordered_within_groupings.clone(), } @@ -301,9 +304,9 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { if let Some(cur_t) = cur_t { let cur_t_kind = convert_assoc_item_kind(cur_t.kind); - let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind); + let cur_t_kind_index = self.assoc_types_order.index_of(cur_t_kind); let item_kind = convert_assoc_item_kind(item.kind); - let item_kind_index = self.assoc_types_order.index_of(&item_kind); + let item_kind_index = self.assoc_types_order.index_of(item_kind); if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() { Self::lint_member_name(cx, &item.ident, &cur_t.ident); @@ -324,9 +327,9 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { if let Some(cur_t) = cur_t { let cur_t_kind = convert_assoc_item_kind(cur_t.kind); - let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind); + let cur_t_kind_index = self.assoc_types_order.index_of(cur_t_kind); let item_kind = convert_assoc_item_kind(item.kind); - let item_kind_index = self.assoc_types_order.index_of(&item_kind); + let item_kind_index = self.assoc_types_order.index_of(item_kind); if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() { Self::lint_member_name(cx, &item.ident, &cur_t.ident); @@ -395,10 +398,10 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { } let item_kind = convert_module_item_kind(&item.kind); - let grouping_name = self.module_item_order_groupings.grouping_name_of(&item_kind); + let grouping_name = self.module_item_order_groupings.grouping_name_of(item_kind); let module_level_order = self .module_item_order_groupings - .module_level_order_of(&item_kind) + .module_level_order_of(item_kind) .unwrap_or_default(); if let Some(cur_t) = cur_t.as_ref() { diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 9acff676d4f6..e79f68e0a2d7 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -59,7 +59,7 @@ pub struct AssigningClones { impl AssigningClones { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index f7f168cb2679..d75bcf34cc32 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -485,7 +485,7 @@ impl_lint_pass!(Attributes => [ impl Attributes { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } @@ -519,9 +519,7 @@ pub struct EarlyAttributes { impl EarlyAttributes { pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: MsrvStack::new(conf.msrv), - } + Self { msrv: conf.msrv.into() } } } @@ -548,9 +546,7 @@ pub struct PostExpansionEarlyAttributes { impl PostExpansionEarlyAttributes { pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: MsrvStack::new(conf.msrv), - } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 52d1d5b4c67a..2f2085e39b05 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::{def_path_res, match_def_path, paths}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; @@ -185,6 +185,7 @@ impl AwaitHolding { crate::disallowed_types::def_kind_predicate, "type", false, + def_path_res, ); Self { def_ids } } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index bc6ba84772b3..8ea5f5c585c3 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -85,7 +85,7 @@ pub struct NonminimalBool { impl NonminimalBool { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 60371dcd7715..0de659f69f69 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -214,7 +214,7 @@ declare_clippy_lint! { } pub struct Cargo { - allowed_duplicate_crates: FxHashSet, + allowed_duplicate_crates: &'static FxHashSet, ignore_publish: bool, } @@ -230,7 +230,7 @@ impl_lint_pass!(Cargo => [ impl Cargo { pub fn new(conf: &'static Conf) -> Self { Self { - allowed_duplicate_crates: conf.allowed_duplicate_crates.iter().cloned().collect(), + allowed_duplicate_crates: &conf.allowed_duplicate_crates, ignore_publish: conf.cargo_ignore_publish, } } @@ -272,7 +272,7 @@ impl LateLintPass<'_> for Cargo { { match MetadataCommand::new().exec() { Ok(metadata) => { - multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates); + multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates); }, Err(e) => { for lint in WITH_DEPS_LINTS { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 7e4b43575672..47bf89a2b350 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -792,7 +792,7 @@ pub struct Casts { impl Casts { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 8ada608049c7..36db194152a6 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -39,7 +39,7 @@ pub struct CheckedConversions { impl CheckedConversions { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 10331b3855b8..d3326bea13bb 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -62,7 +62,7 @@ pub struct DerivableImpls { impl DerivableImpls { pub fn new(conf: &'static Conf) -> Self { - DerivableImpls { msrv: conf.msrv } + DerivableImpls { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index fa33fef23062..796c741f0e37 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::def_path_res; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; @@ -78,6 +79,7 @@ impl DisallowedMacros { |def_kind| matches!(def_kind, DefKind::Macro(_)), "macro", false, + def_path_res, ); Self { disallowed, diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 1382dafa931e..71bb1f839211 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; @@ -74,6 +75,7 @@ impl DisallowedMethods { }, "function", false, + def_path_res, ); Self { disallowed } } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 2bae82648ac7..369105f9d8ac 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; @@ -60,7 +61,14 @@ pub struct DisallowedTypes { impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true); + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + def_kind_predicate, + "type", + true, + def_path_res, + ); Self { def_ids, prim_tys } } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ab77edf1147c..1fd83b5c9728 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -609,14 +609,14 @@ declare_clippy_lint! { } pub struct Documentation { - valid_idents: FxHashSet, + valid_idents: &'static FxHashSet, check_private_items: bool, } impl Documentation { pub fn new(conf: &'static Conf) -> Self { Self { - valid_idents: conf.doc_valid_idents.iter().cloned().collect(), + valid_idents: &conf.doc_valid_idents, check_private_items: conf.check_private_items, } } @@ -650,7 +650,7 @@ impl EarlyLintPass for Documentation { impl<'tcx> LateLintPass<'tcx> for Documentation { fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { + let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else { return; }; diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 8a3f8e1c5874..1b57ae182b00 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -215,7 +215,7 @@ impl<'tcx> FormatArgs<'tcx> { let ty_msrv_map = make_ty_msrv_map(tcx); Self { format_args, - msrv: conf.msrv, + msrv: conf.msrv.into(), ignore_mixed: conf.allow_mixed_uninlined_format_args, ty_msrv_map, } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index be887b03ae4b..bcad1876b982 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -58,7 +58,7 @@ pub struct FromOverInto { impl FromOverInto { pub fn new(conf: &'static Conf) -> Self { - FromOverInto { msrv: conf.msrv } + FromOverInto { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 5f3fc5100e75..a16ff3ca2a07 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -471,7 +471,7 @@ impl Functions { .iter() .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::>())) .collect(), - msrv: conf.msrv, + msrv: conf.msrv.into(), } } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 9e94280fc074..b7fce87c297d 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -54,7 +54,7 @@ pub struct IfThenSomeElseNone { impl IfThenSomeElseNone { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 514e72a48682..318792512fb0 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -83,7 +83,7 @@ impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATU impl ImplicitSaturatingSub { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index e55edb1fcaa8..a04a3f7498a2 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -50,7 +50,7 @@ impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); impl IncompatibleMsrv { pub fn new(conf: &'static Conf) -> Self { Self { - msrv: conf.msrv, + msrv: conf.msrv.into(), is_above_msrv: FxHashMap::default(), check_in_tests: conf.check_incompatible_msrv_in_tests, } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 989997d69f7c..18ccf2c78fc4 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -62,7 +62,7 @@ impl IndexRefutableSlice { pub fn new(conf: &'static Conf) -> Self { Self { max_suggested_slice: conf.max_suggested_slice_pattern_length, - msrv: conf.msrv, + msrv: conf.msrv.into(), } } } diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 91f65d0b79ca..4a940428cfd8 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -70,7 +70,7 @@ pub struct InstantSubtraction { impl InstantSubtraction { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index 01b49403cac8..2d74b4938304 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -39,7 +39,7 @@ pub struct LegacyNumericConstants { impl LegacyNumericConstants { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5fa8f6f4bf3d..cdf929a15493 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -406,7 +406,7 @@ mod zero_sized_map_values; mod zombie_processes; // end lints modules, do not remove this comment, it’s used in `update_lints` -use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; +use clippy_config::{Conf, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; @@ -512,12 +512,11 @@ impl LintInfo { } pub fn explain(name: &str) -> i32 { - let target = format!("clippy::{}", name.to_ascii_uppercase()); - + let target = format!("clippy::{name}"); if let Some(info) = declared_lints::LINTS.iter().find(|info| info.lint.name == target) { println!("{}", sanitize_explanation(info.explanation)); // Check if the lint has configuration - let mut mdconf = get_configuration_metadata(); + let mut mdconf = Conf::get_metadata(); let name = name.to_ascii_lowercase(); mdconf.retain(|cconf| cconf.lints.contains(&&*name)); if !mdconf.is_empty() { @@ -936,7 +935,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new())); - store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(move |tcx| { + Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new( + tcx, conf, + )) + }); store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index fd6208f6b5ef..9dc105003185 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -130,7 +130,7 @@ pub struct Lifetimes { impl Lifetimes { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index d8af44233d3e..44d39b18a45b 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -15,7 +15,7 @@ pub struct LinesFilterMapOk { impl LinesFilterMapOk { pub fn new(conf: &Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 2b66827e82ee..a38864632f0f 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -791,7 +791,7 @@ pub struct Loops { impl Loops { pub fn new(conf: &'static Conf) -> Self { Self { - msrv: conf.msrv, + msrv: conf.msrv.into(), enforce_iter_loop_reborrow: conf.enforce_iter_loop_reborrow, } } diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index c515e41f242f..8b93c13cf4a1 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -50,7 +50,7 @@ pub struct ManualAbsDiff { impl ManualAbsDiff { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 40fe88532729..54c2cab35b5a 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -40,7 +40,7 @@ pub struct ManualBits { impl ManualBits { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 02afe9f0997d..33b170086088 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -99,7 +99,7 @@ pub struct ManualClamp { impl ManualClamp { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index 70db403e1039..3f0d9cbc55b6 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -49,7 +49,7 @@ pub struct ManualDivCeil { impl ManualDivCeil { #[must_use] pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index bd2785fea270..8c3a965ed3d6 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -90,7 +90,7 @@ pub struct ManualFloatMethods { impl ManualFloatMethods { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index f71264a93ca8..c8d11e061ecd 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -53,7 +53,7 @@ pub struct ManualHashOne { impl ManualHashOne { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 8ab49bd2ea8e..39466ccda13e 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -64,7 +64,7 @@ pub struct ManualIsAsciiCheck { impl ManualIsAsciiCheck { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs index b4cd988329d3..509d827acdc0 100644 --- a/clippy_lints/src/manual_is_power_of_two.rs +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -42,7 +42,7 @@ impl_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); impl ManualIsPowerOfTwo { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } fn build_sugg(&self, cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index f54ccf2c87b0..2acd1f909e24 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -39,7 +39,7 @@ pub struct ManualMainSeparatorStr { impl ManualMainSeparatorStr { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 067b92cd46ee..f145fd070e6b 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -71,7 +71,7 @@ pub struct ManualNonExhaustive { impl ManualNonExhaustive { pub fn new(conf: &'static Conf) -> Self { Self { - msrv: conf.msrv, + msrv: conf.msrv.into(), constructed_enum_variants: FxHashSet::default(), potential_enums: Vec::new(), } diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index e4ad3953b671..0a3367174cd1 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -46,7 +46,7 @@ pub struct ManualOptionAsSlice { impl ManualOptionAsSlice { pub fn new(conf: &Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 41e07e26bff0..d77bd354e684 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -39,7 +39,7 @@ pub struct ManualRemEuclid { impl ManualRemEuclid { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 98e8b1f5cf92..589010a029c2 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -50,7 +50,7 @@ pub struct ManualRetain { impl ManualRetain { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 9e911e61f196..356f849eeced 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -56,7 +56,7 @@ pub struct ManualStrip { impl ManualStrip { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index c6ebd6144c76..5319417e20f9 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1014,7 +1014,7 @@ pub struct Matches { impl Matches { pub fn new(conf: &'static Conf) -> Self { Self { - msrv: conf.msrv, + msrv: conf.msrv.into(), infallible_destructuring_match_linted: false, } } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index a54d835b538c..b37861238550 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -293,7 +293,7 @@ pub struct MemReplace { impl MemReplace { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ad374dee516c..c58100e6c088 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4548,7 +4548,7 @@ impl Methods { Self { avoid_breaking_exported_api: conf.avoid_breaking_exported_api, - msrv: conf.msrv, + msrv: conf.msrv.into(), allow_expect_in_tests: conf.allow_expect_in_tests, allow_unwrap_in_tests: conf.allow_unwrap_in_tests, allow_expect_in_consts: conf.allow_expect_in_consts, diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 00ea9bba0d19..7ad55bb52d07 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -40,14 +40,14 @@ declare_clippy_lint! { impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]); pub struct MinIdentChars { - allowed_idents_below_min_chars: FxHashSet, + allowed_idents_below_min_chars: &'static FxHashSet, min_ident_chars_threshold: u64, } impl MinIdentChars { pub fn new(conf: &'static Conf) -> Self { Self { - allowed_idents_below_min_chars: conf.allowed_idents_below_min_chars.iter().cloned().collect(), + allowed_idents_below_min_chars: &conf.allowed_idents_below_min_chars, min_ident_chars_threshold: conf.min_ident_chars_threshold, } } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 67537a251da7..d34d143bc7ae 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -80,7 +80,7 @@ pub struct MissingConstForFn { impl MissingConstForFn { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/missing_const_for_thread_local.rs b/clippy_lints/src/missing_const_for_thread_local.rs index ea74940828a1..5f07d0a2016e 100644 --- a/clippy_lints/src/missing_const_for_thread_local.rs +++ b/clippy_lints/src/missing_const_for_thread_local.rs @@ -49,7 +49,7 @@ pub struct MissingConstForThreadLocal { impl MissingConstForThreadLocal { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index e579dd5947d7..8b8501593672 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -72,7 +72,7 @@ impl NeedlessBorrowsForGenericArgs<'_> { pub fn new(conf: &'static Conf) -> Self { Self { possible_borrowers: Vec::new(), - msrv: conf.msrv, + msrv: conf.msrv.into(), } } } diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index f6bc9428d65f..c6516000711d 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -75,7 +75,7 @@ impl NonStdLazyStatic { #[must_use] pub fn new(conf: &'static Conf) -> Self { Self { - msrv: conf.msrv, + msrv: conf.msrv.into(), lazy_static_lazy_static: Vec::new(), once_cell_crate: Vec::new(), once_cell_sync_lazy: Vec::new(), diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index a78a342d4fe3..40642dabc1bf 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -35,7 +35,7 @@ impl ArithmeticSideEffects { ("f64", FxHashSet::from_iter(["f64"])), ("std::string::String", FxHashSet::from_iter(["str"])), ]); - for (lhs, rhs) in &conf.arithmetic_side_effects_allowed_binary { + for [lhs, rhs] in &conf.arithmetic_side_effects_allowed_binary { allowed_binary.entry(lhs).or_default().insert(rhs); } for s in &conf.arithmetic_side_effects_allowed { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index d32c062cf56a..9e32cb7ea197 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -842,7 +842,7 @@ impl Operators { arithmetic_context: numeric_arithmetic::Context::default(), verbose_bit_mask_threshold: conf.verbose_bit_mask_threshold, modulo_arithmetic_allow_comparison_to_zero: conf.allow_comparison_to_zero, - msrv: conf.msrv, + msrv: conf.msrv.into(), } } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index a80e1f79bbc7..d32db1e4cb50 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -68,7 +68,7 @@ impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]); impl QuestionMark { pub fn new(conf: &'static Conf) -> Self { Self { - msrv: conf.msrv, + msrv: conf.msrv.into(), matches_behaviour: conf.matches_for_let_else, try_block_depth_stack: Vec::new(), inferred_ret_closure_stack: 0, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index d292ed86ea4c..20079365e5c2 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -166,7 +166,7 @@ pub struct Ranges { impl Ranges { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index feefe10f57d7..d644953b1319 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -41,9 +41,7 @@ pub struct RedundantFieldNames { impl RedundantFieldNames { pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: MsrvStack::new(conf.msrv), - } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index b4e1f70d1535..ddfa3850640e 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -40,9 +40,7 @@ pub struct RedundantStaticLifetimes { impl RedundantStaticLifetimes { pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: MsrvStack::new(conf.msrv), - } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index 8805687efccf..1e484a865a12 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -18,7 +18,7 @@ pub struct RepeatVecWithCapacity { impl RepeatVecWithCapacity { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index d68ac8bab128..b3a0f967db5b 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -99,7 +99,7 @@ impl StdReexports { pub fn new(conf: &'static Conf) -> Self { Self { prev_span: Span::default(), - msrv: conf.msrv, + msrv: conf.msrv.into(), } } } diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index 5c95dfe83473..41406d9b8193 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -77,7 +77,7 @@ pub struct StringPatterns { impl StringPatterns { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 8aac3a591029..f1d694ec0427 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -95,7 +95,7 @@ impl TraitBounds { pub fn new(conf: &'static Conf) -> Self { Self { max_trait_bounds: conf.max_trait_bounds, - msrv: conf.msrv, + msrv: conf.msrv.into(), } } } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index f2da8d35cb4f..85a5320beac9 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -598,7 +598,7 @@ impl_lint_pass!(Transmute => [ ]); impl Transmute { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } impl<'tcx> LateLintPass<'tcx> for Transmute { diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 95ce19975c7e..e26ad0ec8d89 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -47,7 +47,7 @@ pub struct TupleArrayConversions { } impl TupleArrayConversions { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 9ad184450de4..e794c1b4a393 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -53,9 +53,7 @@ pub struct UnnestedOrPatterns { impl UnnestedOrPatterns { pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: MsrvStack::new(conf.msrv), - } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/unused_trait_names.rs b/clippy_lints/src/unused_trait_names.rs index 14ac65cf4dfe..1e410ecf92e3 100644 --- a/clippy_lints/src/unused_trait_names.rs +++ b/clippy_lints/src/unused_trait_names.rs @@ -51,7 +51,7 @@ pub struct UnusedTraitNames { impl UnusedTraitNames { pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } + Self { msrv: conf.msrv.into() } } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 743f54ca993a..bff593233acf 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -62,7 +62,7 @@ pub struct UseSelf { impl UseSelf { pub fn new(conf: &'static Conf) -> Self { Self { - msrv: conf.msrv, + msrv: conf.msrv.into(), stack: Vec::new(), } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 3346b15dae9c..a34ba3a8fd97 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -27,7 +27,7 @@ impl UselessVec { pub fn new(conf: &'static Conf) -> Self { Self { too_large_for_stack: conf.too_large_for_stack, - msrv: conf.msrv, + msrv: conf.msrv.into(), span_to_lint_map: BTreeMap::new(), allow_in_test: conf.allow_useless_vec_in_tests, } diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 5b3f60ad6ab0..a0ba6cb46671 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -102,14 +102,14 @@ declare_clippy_lint! { pub struct WildcardImports { warn_on_all: bool, - allowed_segments: FxHashSet, + allowed_segments: &'static FxHashSet, } impl WildcardImports { pub fn new(conf: &'static Conf) -> Self { Self { warn_on_all: conf.warn_on_all_wildcard_imports, - allowed_segments: conf.allowed_wildcard_imports.iter().cloned().collect(), + allowed_segments: &conf.allowed_wildcard_imports, } } } @@ -185,7 +185,7 @@ impl WildcardImports { fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { item.span.from_expansion() || is_prelude_import(segments) - || is_allowed_via_config(segments, &self.allowed_segments) + || is_allowed_via_config(segments, self.allowed_segments) || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id())) } } diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index b98e99017503..f86d2d94e4ec 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -16,7 +16,6 @@ arrayvec = { version = "0.7", default-features = false } itertools = "0.12" # FIXME(f16_f128): remove when no longer needed for parsing rustc_apfloat = "0.2.0" -serde = { version = "1.0", features = ["derive"] } [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index c2aca5ee5390..d61e7a1f0ad6 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -5,7 +5,6 @@ use rustc_attr_parsing::{RustcVersion, parse_version}; use rustc_lint::LateContext; use rustc_session::Session; use rustc_span::{Symbol, sym}; -use serde::Deserialize; use smallvec::SmallVec; use std::iter::once; use std::sync::atomic::{AtomicBool, Ordering}; @@ -89,15 +88,10 @@ static SEEN_MSRV_ATTR: AtomicBool = AtomicBool::new(false); #[derive(Copy, Clone, Debug, Default)] pub struct Msrv(Option); -impl<'de> Deserialize<'de> for Msrv { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let v = String::deserialize(deserializer)?; - parse_version(Symbol::intern(&v)) - .map(|v| Self(Some(v))) - .ok_or_else(|| serde::de::Error::custom("not a valid Rust version")) +impl From> for Msrv { + #[inline] + fn from(value: Option) -> Self { + Self(value) } } @@ -154,13 +148,16 @@ pub struct MsrvStack { stack: SmallVec<[RustcVersion; 2]>, } -impl MsrvStack { - pub fn new(initial: Msrv) -> Self { +impl From> for MsrvStack { + #[inline] + fn from(value: Option) -> Self { Self { - stack: SmallVec::from_iter(initial.0), + stack: SmallVec::from_iter(value), } } +} +impl MsrvStack { pub fn current(&self) -> Option { self.stack.last().copied() } diff --git a/src/driver.rs b/src/driver.rs index 9e88679831f5..86ba949fac23 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -135,7 +135,6 @@ impl rustc_driver::Callbacks for ClippyCallbacks { // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` #[allow(rustc::bad_opt_access)] fn config(&mut self, config: &mut interface::Config) { - let conf_path = clippy_config::lookup_conf_file(); let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take(); config.psess_created = Some(Box::new(move |psess| { @@ -156,7 +155,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } - let conf = clippy_config::Conf::read(sess, &conf_path); + let conf = clippy_config::Conf::load(sess); clippy_lints::register_lints(lint_store, conf); #[cfg(feature = "internal")] clippy_lints_internal::register_lints(lint_store); diff --git a/src/main.rs b/src/main.rs index c9853e53f3b3..32d48802f2bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -113,12 +113,8 @@ impl ClippyCmd { .iter() .fold(String::new(), |s, arg| s + arg + "__CLIPPY_HACKERY__"); - // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages. - let terminal_width = termize::dimensions().map_or(0, |(w, _)| w); - cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path()) .env("CLIPPY_ARGS", clippy_args) - .env("CLIPPY_TERMINAL_WIDTH", terminal_width.to_string()) .arg(self.cargo_subcommand) .args(&self.args); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6d391bd622a8..87998b69ebda 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -6,7 +6,7 @@ use askama::Template; use askama::filters::Safe; use cargo_metadata::Message; use cargo_metadata::diagnostic::{Applicability, Diagnostic}; -use clippy_config::ClippyConfiguration; +use clippy_config::ConfMetadata; use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; @@ -470,7 +470,7 @@ impl DiagnosticCollector { } } - let configs = clippy_config::get_configuration_metadata(); + let configs = clippy_config::Conf::get_metadata(); let mut metadata: Vec = LINTS .iter() .map(|lint| LintMetadata::new(lint, &applicabilities, &configs)) @@ -537,7 +537,7 @@ struct LintMetadata { } impl LintMetadata { - fn new(lint: &LintInfo, applicabilities: &HashMap, configs: &[ClippyConfiguration]) -> Self { + fn new(lint: &LintInfo, applicabilities: &HashMap, configs: &[ConfMetadata]) -> Self { let name = lint.name_lower(); let applicability = applicabilities .get(&name) diff --git a/tests/config-metadata.rs b/tests/config-metadata.rs index af9fe064dc70..0812904dff8c 100644 --- a/tests/config-metadata.rs +++ b/tests/config-metadata.rs @@ -1,15 +1,17 @@ #![feature(rustc_private)] -use clippy_config::{ClippyConfiguration, get_configuration_metadata}; +extern crate rustc_driver; + +use clippy_config::{Conf, ConfMetadata}; use itertools::Itertools; use regex::Regex; use std::borrow::Cow; use std::{env, fs}; -fn metadata() -> impl Iterator { - get_configuration_metadata() +fn metadata() -> impl Iterator { + Conf::get_metadata() .into_iter() - .filter(|config| config.deprecation_reason.is_none()) + .filter(|config| config.renamed_to.is_none()) .filter(|config| !config.lints.is_empty()) } @@ -18,7 +20,9 @@ fn book() { let path = "book/src/lint_configuration.md"; let current = fs::read_to_string(path).unwrap(); - let configs = metadata().map(|conf| conf.to_markdown_paragraph()).join("\n"); + let configs = metadata() + .format_with("\n", |conf, f| f(&conf.display_markdown_paragraph())) + .to_string(); let expected = format!( r" $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml:1:32 | LL | trait-assoc-item-kinds-order = ["fn", "type", "const", "type"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr index 183f0b033191..adb87d95f9df 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type] +error: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type] --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml:1:32 | LL | trait-assoc-item-kinds-order = ["const", "type"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr index abf58dbd1101..e804ed824349 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: The category "Struct" was enabled more than once in the source ordering configuration. +error: The category "struct" was enabled more than once in the source ordering configuration. --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml:1:24 | LL | source-item-ordering = ["enum", "impl", "module", "struct", "trait", "struct"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr index c38c54ffeb0d..9f182754088e 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: data did not match any variant of untagged enum StringOrVecOfString The available options for configuring an ordering within module item groups are: "all", "none", or a list of module item group names (as configured with the `module-item-order-groupings` configuration option). +error: expected a string or an array of strings --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml:1:41 | LL | module-items-ordered-within-groupings = true diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr index 7b1dafb6d0d5..6f8563843296 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr @@ -1,4 +1,8 @@ -error: error reading Clippy's configuration file: unknown ordering group: `madules` was not specified in `module-items-ordered-within-groupings`, perhaps you meant `modules`? expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` +error: unknown ordering group: `madules` was not specified in `module-items-ordered-within-groupings`, perhaps you meant `modules`? expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml:1:42 + | +LL | module-items-ordered-within-groupings = ["madules"] + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr index 996cabeeed4a..ecbc19b62f38 100644 --- a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr @@ -1,4 +1,8 @@ -error: error reading Clippy's configuration file: unknown ordering group: `entirely garbled` was not specified in `module-items-ordered-within-groupings`, expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` +error: unknown ordering group: `entirely garbled` was not specified in `module-items-ordered-within-groupings`, expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml:1:42 + | +LL | module-items-ordered-within-groupings = ["entirely garbled"] + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr index d0fce3614a14..e0e070f54cec 100644 --- a/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr @@ -1,8 +1,10 @@ -error: error reading Clippy's configuration file: replacement not allowed for this configuration - --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:2:5 +error: unknown key + --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:2:37 | LL | { path = "std::string::String", replacement = "std::net::Ipv4Addr" }, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ + | + = note: possible values: `path`, `reason`, `allow-invalid` error: aborting due to 1 previous error diff --git a/tests/ui-toml/bad_toml/conf_bad_toml.rs b/tests/ui-toml/bad_toml/conf_bad_toml.rs index c69fcd300335..7c61d146e99c 100644 --- a/tests/ui-toml/bad_toml/conf_bad_toml.rs +++ b/tests/ui-toml/bad_toml/conf_bad_toml.rs @@ -1,3 +1,3 @@ -//@error-in-other-file: error reading Clippy's configuration file: expected `.`, `=` +//@error-in-other-file: error reading `clippy.toml`: expected `.`, `=` fn main() {} diff --git a/tests/ui-toml/bad_toml/conf_bad_toml.stderr b/tests/ui-toml/bad_toml/conf_bad_toml.stderr index eaf174b04b49..0cc6d62e8f80 100644 --- a/tests/ui-toml/bad_toml/conf_bad_toml.stderr +++ b/tests/ui-toml/bad_toml/conf_bad_toml.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: expected `.`, `=` +error: error reading `clippy.toml`: expected `.`, `=` --> $DIR/tests/ui-toml/bad_toml/clippy.toml:1:4 | LL | fn this_is_obviously(not: a, toml: file) { diff --git a/tests/ui-toml/bad_toml_type/conf_bad_type.rs b/tests/ui-toml/bad_toml_type/conf_bad_type.rs index 688c92d8717d..9aeaa55449e9 100644 --- a/tests/ui-toml/bad_toml_type/conf_bad_type.rs +++ b/tests/ui-toml/bad_toml_type/conf_bad_type.rs @@ -1,3 +1,3 @@ -//@error-in-other-file: invalid type: integer `42`, expected a sequence +//@error-in-other-file: expected an array fn main() {} diff --git a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr index 5cf22ac6a227..cebbb086577e 100644 --- a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr +++ b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: invalid type: integer `42`, expected a sequence +error: expected an array --> $DIR/tests/ui-toml/bad_toml_type/clippy.toml:1:20 | LL | disallowed-names = 42 diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index ecb43dc34a8a..e2bf23c0add6 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,3 +1,7 @@ +//@no-rustfix +//@error-in-other-file: use of a deprecated field +//@error-in-other-file: use of a deprecated field + #![allow(clippy::uninlined_format_args)] fn main() {} diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 627498dc175c..d18cba4a4cfd 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,17 +1,17 @@ -warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead +warning: use of a deprecated field --> $DIR/tests/ui-toml/conf_deprecated_key/clippy.toml:2:1 | LL | cyclomatic-complexity-threshold = 2 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use new name: `cognitive-complexity-threshold` -warning: error reading Clippy's configuration file: deprecated field `blacklisted-names`. Please use `disallowed-names` instead +warning: use of a deprecated field --> $DIR/tests/ui-toml/conf_deprecated_key/clippy.toml:3:1 | LL | blacklisted-names = [ "..", "wibble" ] - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ help: use new name: `disallowed-names` error: the function has a cognitive complexity of (3/2) - --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:6:4 + --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:10:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/duplicated_keys/duplicated_keys.stderr b/tests/ui-toml/duplicated_keys/duplicated_keys.stderr index 9f11c94ded2b..3bb2f0ce226d 100644 --- a/tests/ui-toml/duplicated_keys/duplicated_keys.stderr +++ b/tests/ui-toml/duplicated_keys/duplicated_keys.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: duplicate key `cognitive-complexity-threshold` in document root +error: error reading `clippy.toml`: duplicate key `cognitive-complexity-threshold` in document root --> $DIR/tests/ui-toml/duplicated_keys/clippy.toml:2:1 | LL | cognitive-complexity-threshold = 4 diff --git a/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs index b5a1f3def61e..5654d3ae511d 100644 --- a/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs +++ b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs @@ -1,2 +1,5 @@ -//@error-in-other-file: +//@no-rustfix +//@error-in-other-file: duplicate key in document root +//@error-in-other-file: use of a deprecated field + fn main() {} diff --git a/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr index 80e700f514b5..5a737df9fef8 100644 --- a/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr +++ b/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr @@ -1,14 +1,20 @@ -error: error reading Clippy's configuration file: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`) +warning: use of a deprecated field --> $DIR/tests/ui-toml/duplicated_keys_deprecated/clippy.toml:3:1 | LL | cyclomatic-complexity-threshold = 3 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use new name: `cognitive-complexity-threshold` -warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead +error: duplicate key in document root --> $DIR/tests/ui-toml/duplicated_keys_deprecated/clippy.toml:3:1 | LL | cyclomatic-complexity-threshold = 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: previous definition here + --> $DIR/tests/ui-toml/duplicated_keys_deprecated/clippy.toml:1:1 + | +LL | cognitive-complexity-threshold = 2 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs index b5a1f3def61e..5654d3ae511d 100644 --- a/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs +++ b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs @@ -1,2 +1,5 @@ -//@error-in-other-file: +//@no-rustfix +//@error-in-other-file: duplicate key in document root +//@error-in-other-file: use of a deprecated field + fn main() {} diff --git a/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr index 2ca1e35c7449..645f3d861afd 100644 --- a/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr +++ b/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr @@ -1,14 +1,20 @@ -error: error reading Clippy's configuration file: duplicate field `cognitive-complexity-threshold` - --> $DIR/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml:4:1 +warning: use of a deprecated field + --> $DIR/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml:2:1 | -LL | cognitive-complexity-threshold = 4 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | cyclomatic-complexity-threshold = 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use new name: `cognitive-complexity-threshold` -warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead +error: duplicate key in document root --> $DIR/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml:2:1 | LL | cyclomatic-complexity-threshold = 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: previous definition here + --> $DIR/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml:4:1 + | +LL | cognitive-complexity-threshold = 4 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs index 85e2fb8c797f..af2b645fb8a8 100644 --- a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs @@ -1,4 +1,4 @@ -//@error-in-other-file: not a valid Rust version +//@error-in-other-file: failed to parse rust version #![allow(clippy::redundant_clone)] diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr index 62df5554cced..da21b02a3b19 100644 --- a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file: not a valid Rust version +error: failed to parse rust version --> $DIR/tests/ui-toml/invalid_min_rust_version/clippy.toml:1:8 | LL | msrv = "invalid.version" diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs b/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs index 8d02924c9d93..c9d951c337e7 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs @@ -1,6 +1,6 @@ //@no-rustfix -//@error-in-other-file: unknown field -//@error-in-other-file: error reading Clippy -//@error-in-other-file: error reading Clippy +//@error-in-other-file: unknown field name +//@error-in-other-file: unknown field name +//@error-in-other-file: unknown field name fn main() {} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f2eaa66a4ae4..271c0c6b6bca 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,278 +1,67 @@ -error: error reading Clippy's configuration file: unknown field `foobar`, expected one of - absolute-paths-allowed-crates - absolute-paths-max-segments - accept-comment-above-attributes - accept-comment-above-statement - allow-comparison-to-zero - allow-dbg-in-tests - allow-expect-in-consts - allow-expect-in-tests - allow-indexing-slicing-in-tests - allow-mixed-uninlined-format-args - allow-one-hash-in-raw-strings - allow-panic-in-tests - allow-print-in-tests - allow-private-module-inception - allow-renamed-params-for - allow-unwrap-in-consts - allow-unwrap-in-tests - allow-useless-vec-in-tests - allowed-dotfiles - allowed-duplicate-crates - allowed-idents-below-min-chars - allowed-prefixes - allowed-scripts - allowed-wildcard-imports - arithmetic-side-effects-allowed - arithmetic-side-effects-allowed-binary - arithmetic-side-effects-allowed-unary - array-size-threshold - avoid-breaking-exported-api - await-holding-invalid-types - cargo-ignore-publish - check-incompatible-msrv-in-tests - check-inconsistent-struct-field-initializers - check-private-items - cognitive-complexity-threshold - disallowed-macros - disallowed-methods - disallowed-names - disallowed-types - doc-valid-idents - enable-raw-pointer-heuristic-for-send - enforce-iter-loop-reborrow - enforced-import-renames - enum-variant-name-threshold - enum-variant-size-threshold - excessive-nesting-threshold - future-size-threshold - ignore-interior-mutability - large-error-threshold - lint-commented-code - literal-representation-threshold - matches-for-let-else - max-fn-params-bools - max-include-file-size - max-struct-bools - max-suggested-slice-pattern-length - max-trait-bounds - min-ident-chars-threshold - missing-docs-in-crate-items - module-item-order-groupings - module-items-ordered-within-groupings - msrv - pass-by-value-size-limit - pub-underscore-fields-behavior - semicolon-inside-block-ignore-singleline - semicolon-outside-block-ignore-multiline - single-char-binding-names-threshold - source-item-ordering - stack-size-threshold - standard-macro-braces - struct-field-name-threshold - suppress-restriction-lint-in-const - third-party - too-large-for-stack - too-many-arguments-threshold - too-many-lines-threshold - trait-assoc-item-kinds-order - trivial-copy-size-limit - type-complexity-threshold - unnecessary-box-size - unreadable-literal-lint-fractions - upper-case-acronyms-aggressive - vec-box-size-threshold - verbose-bit-mask-threshold - warn-on-all-wildcard-imports - warn-unsafe-macro-metavars-in-private-macros +error: unknown field name --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1 | LL | foobar = 42 | ^^^^^^ - -error: error reading Clippy's configuration file: unknown field `barfoo`, expected one of - absolute-paths-allowed-crates - absolute-paths-max-segments - accept-comment-above-attributes - accept-comment-above-statement - allow-comparison-to-zero - allow-dbg-in-tests - allow-expect-in-consts - allow-expect-in-tests - allow-indexing-slicing-in-tests - allow-mixed-uninlined-format-args - allow-one-hash-in-raw-strings - allow-panic-in-tests - allow-print-in-tests - allow-private-module-inception - allow-renamed-params-for - allow-unwrap-in-consts - allow-unwrap-in-tests - allow-useless-vec-in-tests - allowed-dotfiles - allowed-duplicate-crates - allowed-idents-below-min-chars - allowed-prefixes - allowed-scripts - allowed-wildcard-imports - arithmetic-side-effects-allowed - arithmetic-side-effects-allowed-binary - arithmetic-side-effects-allowed-unary - array-size-threshold - avoid-breaking-exported-api - await-holding-invalid-types - cargo-ignore-publish - check-incompatible-msrv-in-tests - check-inconsistent-struct-field-initializers - check-private-items - cognitive-complexity-threshold - disallowed-macros - disallowed-methods - disallowed-names - disallowed-types - doc-valid-idents - enable-raw-pointer-heuristic-for-send - enforce-iter-loop-reborrow + | + = note: possible values: + absolute-paths-allowed-crates enum-variant-name-threshold + absolute-paths-max-segments enum-variant-size-threshold + accept-comment-above-attributes excessive-nesting-threshold + accept-comment-above-statement future-size-threshold + allow-comparison-to-zero ignore-interior-mutability + allow-dbg-in-tests large-error-threshold + allow-expect-in-consts lint-commented-code + allow-expect-in-tests lint-inconsistent-struct-field-initializers + allow-indexing-slicing-in-tests literal-representation-threshold + allow-mixed-uninlined-format-args matches-for-let-else + allow-one-hash-in-raw-strings max-fn-params-bools + allow-panic-in-tests max-include-file-size + allow-print-in-tests max-struct-bools + allow-private-module-inception max-suggested-slice-pattern-length + allow-renamed-params-for max-trait-bounds + allow-unwrap-in-consts min-ident-chars-threshold + allow-unwrap-in-tests missing-docs-in-crate-items + allow-useless-vec-in-tests module-item-order-groupings + allowed-dotfiles module-items-ordered-within-groupings + allowed-duplicate-crates msrv + allowed-idents-below-min-chars pass-by-value-size-limit + allowed-prefixes pub-underscore-fields-behavior + allowed-scripts semicolon-inside-block-ignore-singleline + allowed-wildcard-imports semicolon-outside-block-ignore-multiline + arithmetic-side-effects-allowed single-char-binding-names-threshold + arithmetic-side-effects-allowed-binary source-item-ordering + arithmetic-side-effects-allowed-unary stack-size-threshold + array-size-threshold standard-macro-braces + avoid-breaking-exported-api struct-field-name-threshold + await-holding-invalid-types suppress-restriction-lint-in-const + blacklisted-names too-large-for-stack + cargo-ignore-publish too-many-arguments-threshold + check-incompatible-msrv-in-tests too-many-lines-threshold + check-inconsistent-struct-field-initializers trait-assoc-item-kinds-order + check-private-items trivial-copy-size-limit + cognitive-complexity-threshold type-complexity-threshold + cyclomatic-complexity-threshold unnecessary-box-size + disallowed-macros unreadable-literal-lint-fractions + disallowed-methods upper-case-acronyms-aggressive + disallowed-names vec-box-size-threshold + disallowed-types verbose-bit-mask-threshold + doc-valid-idents warn-on-all-wildcard-imports + enable-raw-pointer-heuristic-for-send warn-unsafe-macro-metavars-in-private-macros + enforce-iter-loop-reborrow third-party enforced-import-renames - enum-variant-name-threshold - enum-variant-size-threshold - excessive-nesting-threshold - future-size-threshold - ignore-interior-mutability - large-error-threshold - lint-commented-code - literal-representation-threshold - matches-for-let-else - max-fn-params-bools - max-include-file-size - max-struct-bools - max-suggested-slice-pattern-length - max-trait-bounds - min-ident-chars-threshold - missing-docs-in-crate-items - module-item-order-groupings - module-items-ordered-within-groupings - msrv - pass-by-value-size-limit - pub-underscore-fields-behavior - semicolon-inside-block-ignore-singleline - semicolon-outside-block-ignore-multiline - single-char-binding-names-threshold - source-item-ordering - stack-size-threshold - standard-macro-braces - struct-field-name-threshold - suppress-restriction-lint-in-const - third-party - too-large-for-stack - too-many-arguments-threshold - too-many-lines-threshold - trait-assoc-item-kinds-order - trivial-copy-size-limit - type-complexity-threshold - unnecessary-box-size - unreadable-literal-lint-fractions - upper-case-acronyms-aggressive - vec-box-size-threshold - verbose-bit-mask-threshold - warn-on-all-wildcard-imports - warn-unsafe-macro-metavars-in-private-macros + +error: unknown field name --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1 | LL | barfoo = 53 | ^^^^^^ -error: error reading Clippy's configuration file: unknown field `allow_mixed_uninlined_format_args`, expected one of - absolute-paths-allowed-crates - absolute-paths-max-segments - accept-comment-above-attributes - accept-comment-above-statement - allow-comparison-to-zero - allow-dbg-in-tests - allow-expect-in-consts - allow-expect-in-tests - allow-indexing-slicing-in-tests - allow-mixed-uninlined-format-args - allow-one-hash-in-raw-strings - allow-panic-in-tests - allow-print-in-tests - allow-private-module-inception - allow-renamed-params-for - allow-unwrap-in-consts - allow-unwrap-in-tests - allow-useless-vec-in-tests - allowed-dotfiles - allowed-duplicate-crates - allowed-idents-below-min-chars - allowed-prefixes - allowed-scripts - allowed-wildcard-imports - arithmetic-side-effects-allowed - arithmetic-side-effects-allowed-binary - arithmetic-side-effects-allowed-unary - array-size-threshold - avoid-breaking-exported-api - await-holding-invalid-types - cargo-ignore-publish - check-incompatible-msrv-in-tests - check-inconsistent-struct-field-initializers - check-private-items - cognitive-complexity-threshold - disallowed-macros - disallowed-methods - disallowed-names - disallowed-types - doc-valid-idents - enable-raw-pointer-heuristic-for-send - enforce-iter-loop-reborrow - enforced-import-renames - enum-variant-name-threshold - enum-variant-size-threshold - excessive-nesting-threshold - future-size-threshold - ignore-interior-mutability - large-error-threshold - lint-commented-code - literal-representation-threshold - matches-for-let-else - max-fn-params-bools - max-include-file-size - max-struct-bools - max-suggested-slice-pattern-length - max-trait-bounds - min-ident-chars-threshold - missing-docs-in-crate-items - module-item-order-groupings - module-items-ordered-within-groupings - msrv - pass-by-value-size-limit - pub-underscore-fields-behavior - semicolon-inside-block-ignore-singleline - semicolon-outside-block-ignore-multiline - single-char-binding-names-threshold - source-item-ordering - stack-size-threshold - standard-macro-braces - struct-field-name-threshold - suppress-restriction-lint-in-const - third-party - too-large-for-stack - too-many-arguments-threshold - too-many-lines-threshold - trait-assoc-item-kinds-order - trivial-copy-size-limit - type-complexity-threshold - unnecessary-box-size - unreadable-literal-lint-fractions - upper-case-acronyms-aggressive - vec-box-size-threshold - verbose-bit-mask-threshold - warn-on-all-wildcard-imports - warn-unsafe-macro-metavars-in-private-macros +error: unknown field name --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1 | LL | allow_mixed_uninlined_format_args = true - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: perhaps you meant: `allow-mixed-uninlined-format-args` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `allow-mixed-uninlined-format-args` error: aborting due to 3 previous errors From bfb4bec0498d330e28cba1317964aaa554bb3fc6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 21 Apr 2025 11:27:25 -0400 Subject: [PATCH 2/2] Note the config location the first time a disallowed item is used. --- clippy_config/src/conf.rs | 12 +- clippy_config/src/types.rs | 252 ++++++++++-------- clippy_lints/src/await_holding_invalid.rs | 38 ++- clippy_lints/src/disallowed_macros.rs | 22 +- clippy_lints/src/disallowed_methods.rs | 25 +- clippy_lints/src/disallowed_types.rs | 47 ++-- tests/ui-internal/disallow_span_lint.rs | 28 -- tests/ui-internal/disallow_span_lint.stderr | 23 -- .../await_holding_invalid_type.stderr | 17 +- .../disallowed_macros.stderr | 103 +++++-- .../replaceable_disallowed_types.stderr | 9 +- .../conf_disallowed_methods.stderr | 92 ++++++- .../conf_disallowed_types.stderr | 100 +++++-- .../conf_invalid_path.stderr | 21 +- .../replaceable_disallowed_methods.stderr | 17 +- 15 files changed, 495 insertions(+), 311 deletions(-) delete mode 100644 tests/ui-internal/disallow_span_lint.rs delete mode 100644 tests/ui-internal/disallow_span_lint.stderr diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 737aa359f8eb..4d14743c5a25 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,8 +1,8 @@ use crate::ConfMetadata; use crate::de::{DeserializeOrDefault, DiagCtxt, FromDefault, create_value_list_msg, find_closest_match}; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, - Rename, SourceItemOrdering, SourceItemOrderingModuleItemGroupings, SourceItemOrderingTraitAssocItemKinds, + DisallowedPath, DisallowedRemappablePath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, + SourceItemOrdering, SourceItemOrderingModuleItemGroupings, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use rustc_attr_parsing::{RustcVersion, parse_version}; @@ -388,7 +388,7 @@ define_Conf! { avoid_breaking_exported_api("avoid-breaking-exported-api"): bool = true, /// The list of types which may not be held across an await point. #[lints(await_holding_invalid_type)] - await_holding_invalid_types("await-holding-invalid-types"): Vec, + await_holding_invalid_types("await-holding-invalid-types"): Vec, #[rename = disallowed_names] blacklisted_names("blacklisted-names"), /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. @@ -427,10 +427,10 @@ define_Conf! { cyclomatic_complexity_threshold("cyclomatic-complexity-threshold"), /// The list of disallowed macros, written as fully qualified paths. #[lints(disallowed_macros)] - disallowed_macros("disallowed-macros"): Vec, + disallowed_macros("disallowed-macros"): Vec, /// The list of disallowed methods, written as fully qualified paths. #[lints(disallowed_methods)] - disallowed_methods("disallowed-methods"): Vec, + disallowed_methods("disallowed-methods"): Vec, /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value /// `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. @@ -438,7 +438,7 @@ define_Conf! { disallowed_names("disallowed-names"): Vec = DEFAULT_DISALLOWED_NAMES, /// The list of disallowed types, written as fully qualified paths. #[lints(disallowed_types)] - disallowed_types("disallowed-types"): Vec, + disallowed_types("disallowed-types"): Vec, /// The list of words this lint should not consider as identifiers needing ticks. The value /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. For example: diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 42b3d8af8aa8..890e4babaec1 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -4,7 +4,7 @@ use crate::de::{ use core::fmt::{self, Display}; use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{Applicability, Diag}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_hir::PrimTy; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; @@ -123,66 +123,33 @@ impl Deserialize for Rename { } } -pub type DisallowedPathWithoutReplacement = DisallowedPath; - -pub struct DisallowedPath { - path: String, - reason: Option, - replacement: Option, - /// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing - /// definition. - /// - /// This could be useful when conditional compilation is used, or when a clippy.toml file is - /// shared among multiple projects. - allow_invalid: bool, - /// The span of the `DisallowedPath`. - /// - /// Used for diagnostics. - span: Span, +pub struct DisallowedPath { + pub path: Spanned, + pub reason: Option, + pub allow_invalid: bool, } - -impl DisallowedPath { - pub fn path(&self) -> &str { - &self.path - } - - pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) { - move |diag| { - if let Some(replacement) = &self.replacement { - diag.span_suggestion( - span, - self.reason.as_ref().map_or_else(|| String::from("use"), Clone::clone), - replacement, - Applicability::MachineApplicable, - ); - } else if let Some(reason) = &self.reason { - diag.note(reason.clone()); - } +impl DisallowedPath { + pub fn add_diagnostic(&'static self, diag: &mut Diag<'_, impl EmissionGuarantee>) { + if let Some(reason) = &self.reason { + diag.note(&**reason); } - } - - pub fn span(&self) -> Span { - self.span - } - - pub fn set_span(&mut self, span: Span) { - self.span = span; + diag.span_note_once(self.path.span, "disallowed due to config"); } } - -impl Deserialize for DisallowedPath { +impl Deserialize for DisallowedPath { fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { if let Some(s) = value.as_str() { Some(DisallowedPath { - path: s.into(), + path: Spanned { + node: s.into(), + span: dcx.make_sp(value.span()), + }, reason: None, - replacement: None, allow_invalid: false, - span: dcx.make_sp(value.span()), }) } else if let Some((span, table)) = value.as_table_like() { deserialize_table!(dcx, table, - path("path"): String, + path("path"): Spanned, reason("reason"): String, allow_invalid("allow-invalid"): bool, ); @@ -193,9 +160,7 @@ impl Deserialize for DisallowedPath { Some(DisallowedPath { path, reason, - replacement: None, allow_invalid: allow_invalid.unwrap_or(false), - span: dcx.make_sp(value.span()), }) } else { dcx.span_err(value.span(), "expected either a string or an inline table"); @@ -204,19 +169,42 @@ impl Deserialize for DisallowedPath { } } -impl Deserialize for DisallowedPath { +pub struct DisallowedRemappablePath { + pub path: Spanned, + pub reason: Option, + pub replacement: Option, + pub allow_invalid: bool, +} +impl DisallowedRemappablePath { + pub fn add_diagnostic(&'static self, sp: Span, diag: &mut Diag<'_, impl EmissionGuarantee>) { + if let Some(replacement) = &self.replacement { + diag.span_suggestion( + sp, + self.reason.as_deref().unwrap_or("use instead"), + &**replacement, + Applicability::MachineApplicable, + ); + } else if let Some(reason) = &self.reason { + diag.note(&**reason); + } + diag.span_note_once(self.path.span, "disallowed due to config"); + } +} +impl Deserialize for DisallowedRemappablePath { fn deserialize(dcx: &DiagCtxt<'_>, value: Item<'_>) -> Option { if let Some(s) = value.as_str() { - Some(DisallowedPath { - path: s.into(), + Some(DisallowedRemappablePath { + path: Spanned { + node: s.into(), + span: dcx.make_sp(value.span()), + }, reason: None, replacement: None, allow_invalid: false, - span: dcx.make_sp(value.span()), }) } else if let Some((span, table)) = value.as_table_like() { deserialize_table!(dcx, table, - path("path"): String, + path("path"): Spanned, reason("reason"): String, replacement("replacement"): String, allow_invalid("allow-invalid"): bool, @@ -225,12 +213,11 @@ impl Deserialize for DisallowedPath { dcx.span_err(span, "missing required field `path`"); return None; }; - Some(DisallowedPath { + Some(DisallowedRemappablePath { path, reason, replacement, allow_invalid: allow_invalid.unwrap_or(false), - span: dcx.make_sp(value.span()), }) } else { dcx.span_err(value.span(), "expected either a string or an inline table"); @@ -239,78 +226,111 @@ impl Deserialize for DisallowedPath { } } -/// Creates a map of disallowed items to the reason they were disallowed. -#[allow(clippy::type_complexity)] -pub fn create_disallowed_map<'tcx, const REPLACEMENT_ALLOWED: bool>( - tcx: TyCtxt<'tcx>, - disallowed_paths: &'static [DisallowedPath], - def_kind_predicate: impl Fn(DefKind) -> bool, - predicate_description: &str, +pub trait DisallowedPathLike { + fn path(&self) -> &Spanned; + fn allow_invalid(&self) -> bool; +} +impl DisallowedPathLike for DisallowedPath { + fn path(&self) -> &Spanned { + &self.path + } + fn allow_invalid(&self) -> bool { + self.allow_invalid + } +} +impl DisallowedPathLike for DisallowedRemappablePath { + fn path(&self) -> &Spanned { + &self.path + } + fn allow_invalid(&self) -> bool { + self.allow_invalid + } +} + +fn resolve_disallowed_path( + tcx: TyCtxt<'_>, + path: &'static Spanned, + resolve: fn(TyCtxt<'_>, &[&str]) -> Vec, + filter_def_kinds: fn(DefKind) -> bool, + allowed_desc: &str, allow_prim_tys: bool, - resolve_path: fn(TyCtxt<'tcx>, &[&str]) -> Vec, -) -> ( - DefIdMap<(&'static str, &'static DisallowedPath)>, - FxHashMap)>, -) { - let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath)> = DefIdMap::default(); - let mut prim_tys: FxHashMap)> = - FxHashMap::default(); - for disallowed_path in disallowed_paths { - let path = disallowed_path.path(); - let mut resolutions = resolve_path(tcx, &path.split("::").collect::>()); - - let mut found_def_id = None; - let mut found_prim_ty = false; - resolutions.retain(|res| match res { - Res::Def(def_kind, def_id) => { - found_def_id = Some(*def_id); - def_kind_predicate(*def_kind) - }, - Res::PrimTy(_) => { - found_prim_ty = true; - allow_prim_tys - }, - _ => false, - }); + allow_invalid: bool, +) -> Vec { + let mut resolutions = resolve(tcx, &path.node.split("::").collect::>()); + let mut found_def_id = None; + let mut found_prim_ty = false; + resolutions.retain(|res| match *res { + Res::Def(def_kind, def_id) => { + found_def_id = Some(def_id); + filter_def_kinds(def_kind) + }, + Res::PrimTy(_) => { + found_prim_ty = true; + allow_prim_tys + }, + _ => false, + }); + + if resolutions.is_empty() { + if let Some(def_id) = found_def_id { + tcx.sess.dcx().span_warn( + path.span, + format!( + "expected a {allowed_desc}, found {} {}", + tcx.def_descr_article(def_id), + tcx.def_descr(def_id) + ), + ); + } else if found_prim_ty { + tcx.sess + .dcx() + .span_warn(path.span, format!("expected a {allowed_desc}, found a primitive type",)); + } else if !allow_invalid { + tcx.sess.dcx().span_warn( + path.span, + format!("`{}` does not refer to an existing {allowed_desc}", path.node), + ); + } + } - if resolutions.is_empty() { - let span = disallowed_path.span(); + resolutions +} - if let Some(def_id) = found_def_id { - tcx.sess.dcx().span_warn( - span, - format!( - "expected a {predicate_description}, found {} {}", - tcx.def_descr_article(def_id), - tcx.def_descr(def_id) - ), - ); - } else if found_prim_ty { - tcx.sess.dcx().span_warn( - span, - format!("expected a {predicate_description}, found a primitive type",), - ); - } else if !disallowed_path.allow_invalid { - tcx.sess.dcx().span_warn( - span, - format!("`{path}` does not refer to an existing {predicate_description}"), - ); - } - } +/// Creates a map of disallowed items to the reason they were disallowed. +pub fn create_disallowed_map( + tcx: TyCtxt<'_>, + disallowed_paths: &'static [T], + // pass `def_path_res` as a function to avoid depending on `clippy_utils` + resolve: fn(TyCtxt<'_>, &[&str]) -> Vec, + filter_def_kinds: fn(DefKind) -> bool, + allowed_desc: &str, + allow_prim_tys: bool, +) -> (DefIdMap<&'static T>, FxHashMap) { + let mut def_ids: DefIdMap<&'static T> = DefIdMap::default(); + let mut prim_tys: FxHashMap = FxHashMap::default(); + for disallowed_path in disallowed_paths { + let resolutions = resolve_disallowed_path( + tcx, + disallowed_path.path(), + resolve, + filter_def_kinds, + allowed_desc, + allow_prim_tys, + disallowed_path.allow_invalid(), + ); for res in resolutions { match res { Res::Def(_, def_id) => { - def_ids.insert(def_id, (path, disallowed_path)); + def_ids.insert(def_id, disallowed_path); }, Res::PrimTy(ty) => { - prim_tys.insert(ty, (path, disallowed_path)); + prim_tys.insert(ty, disallowed_path); }, _ => unreachable!(), } } } - (def_ids, prim_tys) } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 2f2085e39b05..9cd810d44482 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; +use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{def_path_res, match_def_path, paths}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::CoroutineLayout; @@ -174,7 +175,7 @@ declare_clippy_lint! { impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); pub struct AwaitHolding { - def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>, + def_ids: DefIdMap<&'static DisallowedPath>, } impl AwaitHolding { @@ -182,10 +183,22 @@ impl AwaitHolding { let (def_ids, _) = create_disallowed_map( tcx, &conf.await_holding_invalid_types, - crate::disallowed_types::def_kind_predicate, + def_path_res, + |kind| { + matches!( + kind, + DefKind::AssocTy + | DefKind::Enum + | DefKind::ForeignTy + | DefKind::Struct + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::Union + ) + }, "type", false, - def_path_res, ); Self { def_ids } } @@ -252,26 +265,23 @@ impl AwaitHolding { ); }, ); - } else if let Some(&(path, disallowed_path)) = self.def_ids.get(&adt.did()) { - emit_invalid_type(cx, ty_cause.source_info.span, path, disallowed_path); + } else if let Some(disallowed_path) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.source_info.span, disallowed_path); } } } } } -fn emit_invalid_type( - cx: &LateContext<'_>, - span: Span, - path: &'static str, - disallowed_path: &'static DisallowedPathWithoutReplacement, -) { +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed_path: &'static DisallowedPath) { span_lint_and_then( cx, AWAIT_HOLDING_INVALID_TYPE, span, - format!("holding a disallowed type across an await point `{path}`"), - disallowed_path.diag_amendment(span), + "holding a disallowed type across an await point", + |diag| { + disallowed_path.add_diagnostic(diag); + }, ); } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 796c741f0e37..c531652f6d72 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_config::types::{DisallowedRemappablePath, create_disallowed_map}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; @@ -60,7 +60,7 @@ declare_clippy_lint! { } pub struct DisallowedMacros { - disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, + disallowed: DefIdMap<&'static DisallowedRemappablePath>, seen: FxHashSet, // Track the most recently seen node that can have a `derive` attribute. // Needed to use the correct lint level. @@ -76,10 +76,10 @@ impl DisallowedMacros { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_macros, - |def_kind| matches!(def_kind, DefKind::Macro(_)), + def_path_res, + |kind| matches!(kind, DefKind::Macro(..)), "macro", false, - def_path_res, ); Self { disallowed, @@ -99,9 +99,7 @@ impl DisallowedMacros { return; } - if let Some(&(path, disallowed_path)) = self.disallowed.get(&mac.def_id) { - let msg = format!("use of a disallowed macro `{path}`"); - let add_note = disallowed_path.diag_amendment(mac.span); + if let Some(disallowed_path) = self.disallowed.get(&mac.def_id) { if matches!(mac.kind, MacroKind::Derive) && let Some(derive_src) = derive_src { @@ -110,11 +108,15 @@ impl DisallowedMacros { DISALLOWED_MACROS, cx.tcx.local_def_id_to_hir_id(derive_src.def_id), mac.span, - msg, - add_note, + "use of a disallowed macro", + |diag| { + disallowed_path.add_diagnostic(mac.span, diag); + }, ); } else { - span_lint_and_then(cx, DISALLOWED_MACROS, mac.span, msg, add_note); + span_lint_and_then(cx, DISALLOWED_MACROS, mac.span, "use of a disallowed macro", |diag| { + disallowed_path.add_diagnostic(mac.span, diag); + }); } } } diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 71bb1f839211..3959586d2193 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_config::types::{DisallowedRemappablePath, create_disallowed_map}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -59,7 +59,7 @@ declare_clippy_lint! { } pub struct DisallowedMethods { - disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, + disallowed: DefIdMap<&'static DisallowedRemappablePath>, } impl DisallowedMethods { @@ -67,15 +67,10 @@ impl DisallowedMethods { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, - |def_kind| { - matches!( - def_kind, - DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn - ) - }, + def_path_res, + |kind| matches!(kind, DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn), "function", false, - def_path_res, ); Self { disallowed } } @@ -92,14 +87,10 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { - span_lint_and_then( - cx, - DISALLOWED_METHODS, - span, - format!("use of a disallowed method `{path}`"), - disallowed_path.diag_amendment(span), - ); + if let Some(disallowed_path) = self.disallowed.get(&id) { + span_lint_and_then(cx, DISALLOWED_METHODS, span, "use of a disallowed method", |diag| { + disallowed_path.add_diagnostic(span, diag); + }); } } } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 369105f9d8ac..1926153714f1 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_config::types::{DisallowedRemappablePath, create_disallowed_map}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; @@ -55,8 +55,8 @@ declare_clippy_lint! { } pub struct DisallowedTypes { - def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, - prim_tys: FxHashMap, + def_ids: DefIdMap<&'static DisallowedRemappablePath>, + prim_tys: FxHashMap, } impl DisallowedTypes { @@ -64,43 +64,38 @@ impl DisallowedTypes { let (def_ids, prim_tys) = create_disallowed_map( tcx, &conf.disallowed_types, - def_kind_predicate, + def_path_res, + |kind| { + matches!( + kind, + DefKind::AssocTy + | DefKind::Enum + | DefKind::ForeignTy + | DefKind::Struct + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::Union + ) + }, "type", true, - def_path_res, ); Self { def_ids, prim_tys } } fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { - let (path, disallowed_path) = match res { + let disallowed_path = match res { Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, _ => return, }; - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}`"), - disallowed_path.diag_amendment(span), - ); + span_lint_and_then(cx, DISALLOWED_TYPES, span, "use of a disallowed type", |diag| { + disallowed_path.add_diagnostic(span, diag); + }); } } -pub fn def_kind_predicate(def_kind: DefKind) -> bool { - matches!( - def_kind, - DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::AssocTy - ) -} - impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { diff --git a/tests/ui-internal/disallow_span_lint.rs b/tests/ui-internal/disallow_span_lint.rs deleted file mode 100644 index 36e4158f6e68..000000000000 --- a/tests/ui-internal/disallow_span_lint.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![feature(rustc_private)] -#![deny(clippy::disallowed_methods)] - -extern crate rustc_errors; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; - -use rustc_errors::{DiagMessage, MultiSpan}; -use rustc_hir::hir_id::HirId; -use rustc_lint::{Lint, LintContext}; -use rustc_middle::ty::TyCtxt; - -pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into, msg: impl Into) { - cx.span_lint(lint, span, |lint| { - //~^ disallowed_methods - lint.primary_message(msg); - }); -} - -pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into, msg: impl Into) { - tcx.node_span_lint(lint, hir_id, span, |lint| { - //~^ disallowed_methods - lint.primary_message(msg); - }); -} - -fn main() {} diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr deleted file mode 100644 index f03a745963e0..000000000000 --- a/tests/ui-internal/disallow_span_lint.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` - --> tests/ui-internal/disallow_span_lint.rs:15:8 - | -LL | cx.span_lint(lint, span, |lint| { - | ^^^^^^^^^ - | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead -note: the lint level is defined here - --> tests/ui-internal/disallow_span_lint.rs:2:9 - | -LL | #![deny(clippy::disallowed_methods)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:22:9 - | -LL | tcx.node_span_lint(lint, hir_id, span, |lint| { - | ^^^^^^^^^^^^^^ - | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead - -error: aborting due to 2 previous errors - diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index deb7f49db9e1..667f8cba5569 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -1,20 +1,31 @@ -error: holding a disallowed type across an await point `std::string::String` +error: holding a disallowed type across an await point --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9 | LL | let _x = String::from("hello"); | ^^ | = note: strings are bad +note: disallowed due to config + --> $DIR/tests/ui-toml/await_holding_invalid_type/clippy.toml:2:14 + | +LL | { path = "std::string::String", reason = "strings are bad" }, + | ^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]` -error: holding a disallowed type across an await point `std::net::Ipv4Addr` +error: holding a disallowed type across an await point --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:11:9 | LL | let x = Ipv4Addr::new(127, 0, 0, 1); | ^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/await_holding_invalid_type/clippy.toml:3:5 + | +LL | "std::net::Ipv4Addr", + | ^^^^^^^^^^^^^^^^^^^^ -error: holding a disallowed type across an await point `std::string::String` +error: holding a disallowed type across an await point --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:35:13 | LL | let _x = String::from("hi!"); diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr index 589995fa87d9..0c8a0f4cee85 100644 --- a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -1,14 +1,19 @@ -error: use of a disallowed macro `serde::Serialize` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:22:14 | LL | #[derive(Serialize)] | ^^^^^^^^^ | = note: no serializing +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:5:14 + | +LL | { path = "serde::Serialize", reason = "no serializing" }, + | ^^^^^^^^^^^^^^^^^^ = note: `-D clippy::disallowed-macros` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]` -error: use of a disallowed macro `macros::attr` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:42:1 | LL | / macros::attr! { @@ -16,86 +21,152 @@ LL | | LL | | struct S; LL | | } | |_^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:12:5 + | +LL | "macros::attr", + | ^^^^^^^^^^^^^^ -error: use of a disallowed macro `proc_macros::Derive` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:62:10 | LL | #[derive(Derive)] | ^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:13:5 + | +LL | "proc_macros::Derive", + | ^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed macro `std::println` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:13:5 | LL | println!("one"); | ^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:2:5 + | +LL | "std::println", + | ^^^^^^^^^^^^^^ -error: use of a disallowed macro `std::println` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:15:5 | LL | println!("two"); | ^^^^^^^^^^^^^^^ -error: use of a disallowed macro `std::cfg` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:17:5 | LL | cfg!(unix); | ^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:4:14 + | +LL | { path = "std::cfg" }, + | ^^^^^^^^^^ -error: use of a disallowed macro `std::vec` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:19:5 | LL | vec![1, 2, 3]; | ^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:3:5 + | +LL | "std::vec", + | ^^^^^^^^^^ -error: use of a disallowed macro `macros::expr` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:26:13 | LL | let _ = macros::expr!(); | ^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:6:5 + | +LL | "macros::expr", + | ^^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::stmt` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:28:5 | LL | macros::stmt!(); | ^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:7:5 + | +LL | "macros::stmt", + | ^^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::pat` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:30:9 | LL | let macros::pat!() = 1; | ^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:9:5 + | +LL | "macros::pat", + | ^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::ty` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:32:12 | LL | let _: macros::ty!() = ""; | ^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:8:5 + | +LL | "macros::ty", + | ^^^^^^^^^^^^ -error: use of a disallowed macro `macros::item` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:34:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:10:5 + | +LL | "macros::item", + | ^^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::binop` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:36:13 | LL | let _ = macros::binop!(1); | ^^^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/disallowed_macros/clippy.toml:11:5 + | +LL | "macros::binop", + | ^^^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::item` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:48:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::item` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:53:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::item` +error: use of a disallowed macro --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:58:5 | LL | macros::item!(); diff --git a/tests/ui-toml/replaceable_disallowed_types/replaceable_disallowed_types.stderr b/tests/ui-toml/replaceable_disallowed_types/replaceable_disallowed_types.stderr index bb63e6970a16..4e50b81ca360 100644 --- a/tests/ui-toml/replaceable_disallowed_types/replaceable_disallowed_types.stderr +++ b/tests/ui-toml/replaceable_disallowed_types/replaceable_disallowed_types.stderr @@ -1,9 +1,14 @@ -error: use of a disallowed type `std::string::String` +error: use of a disallowed type --> tests/ui-toml/replaceable_disallowed_types/replaceable_disallowed_types.rs:15:13 | LL | let _ = String::from("x"); - | ^^^^^^ help: use: `wrapper::String` + | ^^^^^^ help: use instead: `wrapper::String` | +note: disallowed due to config + --> $DIR/tests/ui-toml/replaceable_disallowed_types/clippy.toml:2:14 + | +LL | { path = "std::string::String", replacement = "wrapper::String" }, + | ^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::disallowed-types` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index f7dda81eb936..f11da8efca81 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,91 +1,155 @@ -error: use of a disallowed method `regex::Regex::new` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:33:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^ | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:10:14 + | +LL | { path = "regex::Regex::new" }, + | ^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` -error: use of a disallowed method `regex::Regex::is_match` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:35:8 | LL | re.is_match("abc"); | ^^^^^^^^ | = note: no matching allowed +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:8:14 + | +LL | { path = "regex::Regex::is_match", reason = "no matching allowed" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `std::iter::Iterator::sum` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14 | LL | a.iter().sum::(); | ^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:3:5 + | +LL | "std::iter::Iterator::sum", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `slice::sort_unstable` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:7 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:5:5 + | +LL | "slice::sort_unstable", + | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `f32::clamp` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:46:20 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:4:5 + | +LL | "f32::clamp", + | ^^^^^^^^^^^^ -error: use of a disallowed method `regex::Regex::new` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:50:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ -error: use of a disallowed method `f32::clamp` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ -error: use of a disallowed method `regex::Regex::new` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ -error: use of a disallowed method `futures::stream::select_all` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:6:5 + | +LL | "futures::stream::select_all", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `conf_disallowed_methods::local_fn` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:5 | LL | local_fn(); | ^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:12:5 + | +LL | "conf_disallowed_methods::local_fn", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `conf_disallowed_methods::local_mod::f` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:65:5 | LL | local_mod::f(); | ^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:13:5 + | +LL | "conf_disallowed_methods::local_mod::f", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `conf_disallowed_methods::Struct::method` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:7 | LL | s.method(); | ^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:14:5 + | +LL | "conf_disallowed_methods::Struct::method", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:70:7 | LL | s.provided_method(); | ^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:15:5 + | +LL | "conf_disallowed_methods::Trait::provided_method", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` +error: use of a disallowed method --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:72:7 | LL | s.implemented_method(); | ^^^^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_methods/clippy.toml:16:5 + | +LL | "conf_disallowed_methods::Trait::implemented_method", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 14 previous errors diff --git a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr index 18bc36ca1e33..8463c29faec8 100644 --- a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr +++ b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr @@ -1,129 +1,187 @@ -error: use of a disallowed type `std::sync::atomic::AtomicU32` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:7:1 | LL | use std::sync::atomic::AtomicU32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:3:5 + | +LL | "std::sync::atomic::AtomicU32", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::disallowed-types` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` -error: use of a disallowed type `std::time::Instant` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:9:1 | LL | use std::time::Instant as Sneaky; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:7:5 + | +LL | "std::time::Instant", + | ^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::time::Instant` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:14:33 | LL | fn bad_return_type() -> fn() -> Sneaky { | ^^^^^^ -error: use of a disallowed type `std::time::Instant` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:19:28 | LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {} | ^^^^^^ -error: use of a disallowed type `std::sync::atomic::AtomicU32` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:19:39 | LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {} | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::io::Read` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:23:22 | LL | fn trait_obj(_: &dyn std::io::Read) {} | ^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:8:5 + | +LL | "std::io::Read", + | ^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:33 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:9:5 + | +LL | "std::primitive::usize", + | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `bool` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:43 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:10:5 + | +LL | "bool", + | ^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:28 | LL | fn const_generics() {} | ^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:33:24 | LL | struct GenArg([u8; U]); | ^^^^^ -error: use of a disallowed type `std::net::Ipv4Addr` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:38:10 | LL | fn ip(_: std::net::Ipv4Addr) {} | ^^^^^^^^^^^^^^^^^^ | = note: no IPv4 allowed +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:12:14 + | +LL | { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, + | ^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::net::TcpListener` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:41:16 | LL | fn listener(_: std::net::TcpListener) {} | ^^^^^^^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:14:14 + | +LL | { path = "std::net::TcpListener" }, + | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::collections::HashMap` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:46:48 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:2:5 + | +LL | "std::collections::HashMap", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::collections::HashMap` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:46:12 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::time::Instant` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:49:13 | LL | let _ = Sneaky::now(); | ^^^^^^ -error: use of a disallowed type `std::sync::atomic::AtomicU32` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:51:13 | LL | let _ = foo::atomic::AtomicU32::new(0); | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::sync::atomic::AtomicU32` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:53:17 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::sync::atomic::AtomicU32` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:53:48 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `syn::TypePath` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:56:43 | LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); | ^^^^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:4:5 + | +LL | "syn::TypePath", + | ^^^^^^^^^^^^^^^ -error: use of a disallowed type `proc_macro2::Ident` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:58:13 | LL | let _ = syn::Ident::new("", todo!()); | ^^^^^^^^^^ + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_disallowed_types/clippy.toml:5:5 + | +LL | "proc_macro2::Ident", + | ^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:61:12 | LL | let _: usize = 64_usize; diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr index 82550108eba5..9ab95d77b3de 100644 --- a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr +++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr @@ -1,23 +1,20 @@ warning: expected a macro, found a primitive type - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:5:8 | -LL | / [[disallowed-macros]] -LL | | path = "bool" - | |_____________^ +LL | path = "bool" + | ^^^^^^ warning: `std::process::current_exe` does not refer to an existing function - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:8:8 | -LL | / [[disallowed-methods]] -LL | | path = "std::process::current_exe" - | |__________________________________^ +LL | path = "std::process::current_exe" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: expected a type, found a tuple variant - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:2:8 | -LL | / [[disallowed-types]] -LL | | path = "std::result::Result::Err" - | |_________________________________^ +LL | path = "std::result::Result::Err" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: 3 warnings emitted diff --git a/tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.stderr b/tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.stderr index 6f70b4ae6a9e..e1aa9c5f9df2 100644 --- a/tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.stderr +++ b/tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.stderr @@ -1,17 +1,28 @@ -error: use of a disallowed method `replaceable_disallowed_methods::bad` +error: use of a disallowed method --> tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.rs:6:5 | LL | bad(); - | ^^^ help: use: `good` + | ^^^ help: use instead: `good` | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_replaceable_disallowed_methods/clippy.toml:2:14 + | +LL | { path = "replaceable_disallowed_methods::bad", replacement = "good" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` -error: use of a disallowed method `replaceable_disallowed_methods::questionable` +error: use of a disallowed method --> tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.rs:8:5 | LL | questionable(); | ^^^^^^^^^^^^ help: a better function exists: `good` + | +note: disallowed due to config + --> $DIR/tests/ui-toml/toml_replaceable_disallowed_methods/clippy.toml:3:14 + | +LL | { path = "replaceable_disallowed_methods::questionable", replacement = "good", reason = "a better function exists" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors