about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--book/src/lint_configuration.md6
-rw-r--r--clippy_config/Cargo.toml1
-rw-r--r--clippy_config/src/conf.rs724
-rw-r--r--clippy_config/src/lib.rs2
-rw-r--r--clippy_config/src/metadata.rs103
-rw-r--r--clippy_dev/src/fmt.rs392
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/utils/internal_lints/metadata_collector.rs2
8 files changed, 671 insertions, 561 deletions
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index fb717a2c166..e3d550b1466 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -364,7 +364,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
 
 
 ## `await-holding-invalid-types`
-
+The list of types which may not be held across an await point.
 
 **Default Value:** `[]`
 
@@ -668,6 +668,8 @@ crate. For example, `pub(crate)` items.
 ## `msrv`
 The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
 
+**Default Value:** `current version`
+
 ---
 **Affected lints:**
 * [`allow_attributes`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes)
@@ -862,6 +864,8 @@ The maximum number of lines a function or method can have
 The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
 reference. By default there is no limit
 
+**Default Value:** `target_pointer_width * 2`
+
 ---
 **Affected lints:**
 * [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml
index e1b2edc8a6f..c98469e4cbb 100644
--- a/clippy_config/Cargo.toml
+++ b/clippy_config/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+itertools = "0.12"
 rustc-semver = "1.1"
 serde = { version = "1.0", features = ["derive"] }
 toml = "0.7.3"
diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs
index 63140a36875..4c2a8255d6b 100644
--- a/clippy_config/src/conf.rs
+++ b/clippy_config/src/conf.rs
@@ -123,7 +123,8 @@ macro_rules! define_Conf {
         $(#[doc = $doc:literal])+
         $(#[conf_deprecated($dep:literal, $new_conf:ident)])?
         $(#[default_text = $default_text:expr])?
-        ($name:ident: $ty:ty = $default:expr),
+        $(#[lints($($for_lints:ident),* $(,)?)])?
+        $name:ident: $ty:ty = $default:expr,
     )*) => {
         /// Clippy lint configuration
         pub struct Conf {
@@ -201,29 +202,128 @@ macro_rules! define_Conf {
         }
 
         pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
-            let mut sorted = vec![
-                $(
-                    {
-                        let deprecation_reason = wrap_option!($($dep)?);
-
-                        ClippyConfiguration::new(
-                            stringify!($name),
-                            default_text!(defaults::$name() $(, $default_text)?),
-                            concat!($($doc, '\n',)*),
-                            deprecation_reason,
-                        )
-                    },
-                )+
-            ];
-            sorted.sort_by(|a, b| a.name.cmp(&b.name));
-            sorted
+            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)?)
+                },
+            )*]
         }
     };
 }
 
 define_Conf! {
-    /// Lint: ARITHMETIC_SIDE_EFFECTS.
+    /// Which crates to allow absolute paths from
+    #[lints(absolute_paths)]
+    absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(),
+    /// 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,
+    /// 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,
+    /// 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,
+    /// Don't lint when comparing the result of a modulo operation to zero.
+    #[lints(modulo_arithmetic)]
+    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,
+    /// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
+    #[lints(expect_used)]
+    allow_expect_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,
+    /// Whether to allow `r#""#` when `r""` can be used
+    #[lints(unnecessary_raw_string_hashes)]
+    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,
+    /// 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,
+    /// Whether to allow module inception if it's not public.
+    #[lints(module_inception)]
+    allow_private_module_inception: bool = false,
+    /// List of trait paths to ignore when checking renamed function parameters.
+    ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allow-renamed-params-for = [ "std::convert::From" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
+    /// - `".."` 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<String> =
+        DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(),
+    /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
+    #[lints(unwrap_used)]
+    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,
+    /// Additional dotfiles (files or directories starting with a dot) to allow
+    #[lints(path_ends_with_ext)]
+    allowed_dotfiles: Vec<String> = Vec::default(),
+    /// A list of crate names to allow duplicates of
+    #[lints(multiple_crate_versions)]
+    allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(),
+    /// 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: FxHashSet<String> =
+        DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(),
+    /// 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.
+    ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allowed-prefixes = [ "to", "from" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
+    /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
+    ///   `TryInto` will also be included)
+    /// - 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<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect(),
+    /// The list of unicode scripts allowed to be used in the scope.
+    #[lints(disallowed_script_idents)]
+    allowed_scripts: Vec<String> = vec!["Latin".to_string()],
+    /// List of path segments allowed to have wildcard imports.
     ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allowed-wildcard-imports = [ "utils", "common" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`.
+    /// 2. Paths with any segment that containing the word 'prelude'
+    /// are already allowed by default.
+    #[lints(wildcard_imports)]
+    allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(),
     /// 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.
@@ -238,9 +338,8 @@ define_Conf! {
     ///
     /// A type, say `SomeType`, listed in this configuration has the same behavior of
     /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
-    (arithmetic_side_effects_allowed: Vec<String> = <_>::default()),
-    /// Lint: ARITHMETIC_SIDE_EFFECTS.
-    ///
+    #[lints(arithmetic_side_effects)]
+    arithmetic_side_effects_allowed: Vec<String> = <_>::default(),
     /// Suppress checking of the passed type pair names in binary operations like addition or
     /// multiplication.
     ///
@@ -255,9 +354,8 @@ define_Conf! {
     /// ```toml
     /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
     /// ```
-    (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()),
-    /// Lint: ARITHMETIC_SIDE_EFFECTS.
-    ///
+    #[lints(arithmetic_side_effects)]
+    arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(),
     /// Suppress checking of the passed type names in unary operations like "negation" (`-`).
     ///
     /// #### Example
@@ -265,298 +363,78 @@ define_Conf! {
     /// ```toml
     /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
     /// ```
-    (arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default()),
-    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT.
-    ///
+    #[lints(arithmetic_side_effects)]
+    arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default(),
+    /// The maximum allowed size for arrays on the stack
+    #[lints(large_const_arrays, large_stack_arrays)]
+    array_size_threshold: u64 = 512_000,
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
-    (avoid_breaking_exported_api: bool = true),
-    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, COLLAPSIBLE_MATCH.
-    ///
-    /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
-    #[default_text = ""]
-    (msrv: Msrv = Msrv::empty()),
+    #[lints(
+        box_collection,
+        enum_variant_names,
+        large_types_passed_by_value,
+        linkedlist,
+        needless_pass_by_ref_mut,
+        option_option,
+        rc_buffer,
+        rc_mutex,
+        redundant_allocation,
+        single_call_fn,
+        trivially_copy_pass_by_ref,
+        unnecessary_box_returns,
+        unnecessary_wraps,
+        unused_self,
+        upper_case_acronyms,
+        vec_box,
+        wrong_self_convention,
+    )]
+    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: Vec<DisallowedPath> = Vec::new(),
     /// DEPRECATED LINT: BLACKLISTED_NAME.
     ///
     /// Use the Disallowed Names lint instead
     #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)]
-    (blacklisted_names: Vec<String> = Vec::new()),
-    /// Lint: COGNITIVE_COMPLEXITY.
-    ///
+    blacklisted_names: Vec<String> = Vec::new(),
+    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
+    #[lints(cargo_common_metadata)]
+    cargo_ignore_publish: 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,
     /// The maximum cognitive complexity a function can have
-    (cognitive_complexity_threshold: u64 = 25),
-    /// Lint: EXCESSIVE_NESTING.
-    ///
-    /// The maximum amount of nesting a block can reside in
-    (excessive_nesting_threshold: u64 = 0),
+    #[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),
-    /// Lint: DISALLOWED_NAMES.
-    ///
+    cyclomatic_complexity_threshold: u64 = 25,
+    /// The list of disallowed macros, written as fully qualified paths.
+    #[lints(disallowed_macros)]
+    disallowed_macros: Vec<DisallowedPath> = Vec::new(),
+    /// The list of disallowed methods, written as fully qualified paths.
+    #[lints(disallowed_methods)]
+    disallowed_methods: Vec<DisallowedPath> = Vec::new(),
     /// 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.
-    (disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
-    /// Lint: SEMICOLON_INSIDE_BLOCK.
-    ///
-    /// Whether to lint only if it's multiline.
-    (semicolon_inside_block_ignore_singleline: bool = false),
-    /// Lint: SEMICOLON_OUTSIDE_BLOCK.
-    ///
-    /// Whether to lint only if it's singleline.
-    (semicolon_outside_block_ignore_multiline: bool = false),
-    /// Lint: DOC_MARKDOWN.
-    ///
+    #[lints(disallowed_names)]
+    disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(),
+    /// The list of disallowed types, written as fully qualified paths.
+    #[lints(disallowed_types)]
+    disallowed_types: Vec<DisallowedPath> = Vec::new(),
     /// 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.
-    (doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
-    /// Lint: TOO_MANY_ARGUMENTS.
-    ///
-    /// The maximum number of argument a function or method can have
-    (too_many_arguments_threshold: u64 = 7),
-    /// Lint: TYPE_COMPLEXITY.
-    ///
-    /// The maximum complexity a type can have
-    (type_complexity_threshold: u64 = 250),
-    /// Lint: MANY_SINGLE_CHAR_NAMES.
-    ///
-    /// The maximum number of single char bindings a scope may have
-    (single_char_binding_names_threshold: u64 = 4),
-    /// Lint: BOXED_LOCAL, USELESS_VEC.
-    ///
-    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
-    (too_large_for_stack: u64 = 200),
-    /// Lint: ENUM_VARIANT_NAMES.
-    ///
-    /// The minimum number of enum variants for the lints about variant names to trigger
-    (enum_variant_name_threshold: u64 = 3),
-    /// Lint: STRUCT_FIELD_NAMES.
-    ///
-    /// The minimum number of struct fields for the lints about field names to trigger
-    (struct_field_name_threshold: u64 = 3),
-    /// Lint: LARGE_ENUM_VARIANT.
-    ///
-    /// The maximum size of an enum's variant to avoid box suggestion
-    (enum_variant_size_threshold: u64 = 200),
-    /// Lint: VERBOSE_BIT_MASK.
-    ///
-    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
-    (verbose_bit_mask_threshold: u64 = 1),
-    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
-    ///
-    /// The lower bound for linting decimal literals
-    (literal_representation_threshold: u64 = 16384),
-    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
-    ///
-    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
-    /// reference. By default there is no limit
-    #[default_text = ""]
-    (trivial_copy_size_limit: Option<u64> = None),
-    /// Lint: LARGE_TYPES_PASSED_BY_VALUE.
-    ///
-    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
-    (pass_by_value_size_limit: u64 = 256),
-    /// Lint: TOO_MANY_LINES.
-    ///
-    /// The maximum number of lines a function or method can have
-    (too_many_lines_threshold: u64 = 100),
-    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
-    ///
-    /// The maximum allowed size for arrays on the stack
-    (array_size_threshold: u64 = 512_000),
-    /// Lint: LARGE_STACK_FRAMES.
-    ///
-    /// The maximum allowed stack size for functions in bytes
-    (stack_size_threshold: u64 = 512_000),
-    /// Lint: VEC_BOX.
-    ///
-    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
-    (vec_box_size_threshold: u64 = 4096),
-    /// Lint: TYPE_REPETITION_IN_BOUNDS.
-    ///
-    /// The maximum number of bounds a trait can have to be linted
-    (max_trait_bounds: u64 = 3),
-    /// Lint: STRUCT_EXCESSIVE_BOOLS.
-    ///
-    /// The maximum number of bool fields a struct can have
-    (max_struct_bools: u64 = 3),
-    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
-    ///
-    /// The maximum number of bool parameters a function can have
-    (max_fn_params_bools: u64 = 3),
-    /// Lint: WILDCARD_IMPORTS.
-    ///
-    /// Whether to allow certain wildcard imports (prelude, super in tests).
-    (warn_on_all_wildcard_imports: bool = false),
-    /// Lint: DISALLOWED_MACROS.
-    ///
-    /// The list of disallowed macros, written as fully qualified paths.
-    (disallowed_macros: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: DISALLOWED_METHODS.
-    ///
-    /// The list of disallowed methods, written as fully qualified paths.
-    (disallowed_methods: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: DISALLOWED_TYPES.
-    ///
-    /// The list of disallowed types, written as fully qualified paths.
-    (disallowed_types: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: UNREADABLE_LITERAL.
-    ///
-    /// Should the fraction of a decimal be linted to include separators.
-    (unreadable_literal_lint_fractions: bool = true),
-    /// Lint: UPPER_CASE_ACRONYMS.
-    ///
-    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
-    (upper_case_acronyms_aggressive: bool = false),
-    /// Lint: MANUAL_LET_ELSE.
-    ///
-    /// Whether the matches should be considered by the lint, and whether there should
-    /// be filtering for common types.
-    (matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes),
-    /// Lint: CARGO_COMMON_METADATA.
-    ///
-    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
-    (cargo_ignore_publish: bool = false),
-    /// Lint: NONSTANDARD_MACRO_BRACES.
-    ///
-    /// 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.
-    (standard_macro_braces: Vec<MacroMatcher> = Vec::new()),
-    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
-    ///
-    /// The list of imports to always rename, a fully qualified path followed by the rename.
-    (enforced_import_renames: Vec<Rename> = Vec::new()),
-    /// Lint: DISALLOWED_SCRIPT_IDENTS.
-    ///
-    /// The list of unicode scripts allowed to be used in the scope.
-    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
-    /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
-    ///
+    #[lints(doc_markdown)]
+    doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
-    (enable_raw_pointer_heuristic_for_send: bool = true),
-    /// Lint: INDEX_REFUTABLE_SLICE.
-    ///
-    /// 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.
-    (max_suggested_slice_pattern_length: u64 = 3),
-    /// Lint: AWAIT_HOLDING_INVALID_TYPE.
-    (await_holding_invalid_types: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: LARGE_INCLUDE_FILE.
-    ///
-    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
-    (max_include_file_size: u64 = 1_000_000),
-    /// Lint: EXPECT_USED.
-    ///
-    /// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
-    (allow_expect_in_tests: bool = false),
-    /// Lint: UNWRAP_USED.
-    ///
-    /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
-    (allow_unwrap_in_tests: bool = false),
-    /// Lint: PANIC.
-    ///
-    /// Whether `panic` should be allowed in test functions or `#[cfg(test)]`
-    (allow_panic_in_tests: bool = false),
-    /// Lint: DBG_MACRO.
-    ///
-    /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
-    (allow_dbg_in_tests: bool = false),
-    /// Lint: PRINT_STDOUT, PRINT_STDERR.
-    ///
-    /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
-    (allow_print_in_tests: bool = false),
-    /// Lint: USELESS_VEC.
-    ///
-    /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]`
-    (allow_useless_vec_in_tests: bool = false),
-    /// Lint: RESULT_LARGE_ERR.
-    ///
-    /// The maximum size of the `Err`-variant in a `Result` returned from a function
-    (large_error_threshold: u64 = 128),
-    /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST.
-    ///
-    /// A list of paths to types that should be treated as if they do not contain interior mutability
-    (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
-    /// Lint: UNINLINED_FORMAT_ARGS.
-    ///
-    /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
-    (allow_mixed_uninlined_format_args: bool = true),
-    /// Lint: INDEXING_SLICING.
-    ///
-    /// 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.
-    (suppress_restriction_lint_in_const: bool = false),
-    /// Lint: MISSING_DOCS_IN_PRIVATE_ITEMS.
-    ///
-    /// Whether to **only** check for missing documentation in items visible within the current
-    /// crate. For example, `pub(crate)` items.
-    (missing_docs_in_crate_items: bool = false),
-    /// Lint: LARGE_FUTURES.
-    ///
-    /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
-    (future_size_threshold: u64 = 16 * 1024),
-    /// Lint: UNNECESSARY_BOX_RETURNS.
-    ///
-    /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
-    (unnecessary_box_size: u64 = 128),
-    /// Lint: MODULE_INCEPTION.
-    ///
-    /// Whether to allow module inception if it's not public.
-    (allow_private_module_inception: bool = false),
-    /// Lint: MIN_IDENT_CHARS.
-    ///
-    /// 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.
-    (allowed_idents_below_min_chars: FxHashSet<String> =
-        DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()),
-    /// Lint: MIN_IDENT_CHARS.
-    ///
-    /// Minimum chars an ident can have, anything below or equal to this will be linted.
-    (min_ident_chars_threshold: u64 = 1),
-    /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
-    ///
-    /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
-    (accept_comment_above_statement: bool = true),
-    /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
-    ///
-    /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
-    (accept_comment_above_attributes: bool = true),
-    /// Lint: UNNECESSARY_RAW_STRING_HASHES.
-    ///
-    /// Whether to allow `r#""#` when `r""` can be used
-    (allow_one_hash_in_raw_strings: bool = false),
-    /// Lint: ABSOLUTE_PATHS.
-    ///
-    /// The maximum number of segments a path can have before being linted, anything above this will
-    /// be linted.
-    (absolute_paths_max_segments: u64 = 2),
-    /// Lint: ABSOLUTE_PATHS.
-    ///
-    /// Which crates to allow absolute paths from
-    (absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default()),
-    /// Lint: PATH_ENDS_WITH_EXT.
-    ///
-    /// Additional dotfiles (files or directories starting with a dot) to allow
-    (allowed_dotfiles: Vec<String> = Vec::default()),
-    /// Lint: MULTIPLE_CRATE_VERSIONS.
-    ///
-    /// A list of crate names to allow duplicates of
-    (allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default()),
-    /// Lint: EXPLICIT_ITER_LOOP.
-    ///
+    #[lints(non_send_fields_in_send_ty)]
+    enable_raw_pointer_heuristic_for_send: bool = true,
     /// Whether to recommend using implicit into iter for reborrowed values.
     ///
     /// #### Example
@@ -574,77 +452,195 @@ define_Conf! {
     /// for _ in &*rmvec {}
     /// for _ in &mut *rmvec {}
     /// ```
-    (enforce_iter_loop_reborrow: bool = false),
-    /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC.
-    ///
-    /// Whether to also run the listed lints on private items.
-    (check_private_items: bool = false),
-    /// Lint: PUB_UNDERSCORE_FIELDS.
-    ///
+    #[lints(explicit_iter_loop)]
+    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<Rename> = Vec::new(),
+    /// The minimum number of enum variants for the lints about variant names to trigger
+    #[lints(enum_variant_names)]
+    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,
+    /// The maximum amount of nesting a block can reside in
+    #[lints(excessive_nesting)]
+    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,
+    /// 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<String> = Vec::from(["bytes::Bytes".into()]),
+    /// The maximum size of the `Err`-variant in a `Result` returned from a function
+    #[lints(result_large_err)]
+    large_error_threshold: u64 = 128,
+    /// The lower bound for linting decimal literals
+    #[lints(decimal_literal_representation)]
+    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,
+    /// The maximum number of bool parameters a function can have
+    #[lints(fn_params_excessive_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,
+    /// The maximum number of bool fields a struct can have
+    #[lints(struct_excessive_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,
+    /// The maximum number of bounds a trait can have to be linted
+    #[lints(type_repetition_in_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,
+    /// 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,
+    /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
+    #[default_text = "current version"]
+    #[lints(
+        allow_attributes,
+        allow_attributes_without_reason,
+        almost_complete_range,
+        approx_constant,
+        assigning_clones,
+        borrow_as_ptr,
+        cast_abs_to_unsigned,
+        checked_conversions,
+        cloned_instead_of_copied,
+        collapsible_match,
+        collapsible_str_replace,
+        deprecated_cfg_attr,
+        derivable_impls,
+        err_expect,
+        filter_map_next,
+        from_over_into,
+        if_then_some_else_none,
+        index_refutable_slice,
+        iter_kv_map,
+        legacy_numeric_constants,
+        manual_bits,
+        manual_c_str_literals,
+        manual_clamp,
+        manual_hash_one,
+        manual_is_ascii_check,
+        manual_let_else,
+        manual_non_exhaustive,
+        manual_pattern_char_comparison,
+        manual_range_contains,
+        manual_rem_euclid,
+        manual_retain,
+        manual_split_once,
+        manual_str_repeat,
+        manual_strip,
+        manual_try_fold,
+        map_clone,
+        map_unwrap_or,
+        match_like_matches_macro,
+        mem_replace_with_default,
+        missing_const_for_fn,
+        needless_borrow,
+        option_as_ref_deref,
+        option_map_unwrap_or,
+        ptr_as_ptr,
+        redundant_field_names,
+        redundant_static_lifetimes,
+        seek_from_current,
+        seek_rewind,
+        transmute_ptr_to_ref,
+        tuple_array_conversions,
+        type_repetition_in_bounds,
+        unchecked_duration_subtraction,
+        uninlined_format_args,
+        unnecessary_lazy_evaluations,
+        unnested_or_patterns,
+        use_self,
+    )]
+    msrv: Msrv = Msrv::empty(),
+    /// 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,
     /// Lint "public" fields in a struct that are prefixed with an underscore based on their
     /// exported visibility, or whether they are marked as "pub".
-    (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported),
-    /// Lint: MODULO_ARITHMETIC.
-    ///
-    /// Don't lint when comparing the result of a modulo operation to zero.
-    (allow_comparison_to_zero: bool = true),
-    /// Lint: WILDCARD_IMPORTS.
-    ///
-    /// List of path segments allowed to have wildcard imports.
-    ///
-    /// #### Example
-    ///
-    /// ```toml
-    /// allowed-wildcard-imports = [ "utils", "common" ]
-    /// ```
-    ///
-    /// #### Noteworthy
-    ///
-    /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`.
-    /// 2. Paths with any segment that containing the word 'prelude'
-    /// are already allowed by default.
-    (allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default()),
-    /// Lint: MODULE_NAME_REPETITIONS.
-    ///
-    /// 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.
-    ///
-    /// #### Example
-    ///
-    /// ```toml
-    /// allowed-prefixes = [ "to", "from" ]
-    /// ```
-    ///
-    /// #### Noteworthy
-    ///
-    /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
-    /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
-    ///   `TryInto` will also be included)
-    /// - 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
-    (allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
-    /// Lint: RENAMED_FUNCTION_PARAMS.
-    ///
-    /// List of trait paths to ignore when checking renamed function parameters.
-    ///
-    /// #### Example
-    ///
-    /// ```toml
-    /// allow-renamed-params-for = [ "std::convert::From" ]
-    /// ```
-    ///
-    /// #### Noteworthy
-    ///
-    /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
-    /// - `".."` 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.
-    (allow_renamed_params_for: Vec<String> =
-        DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()),
-    /// Lint: MACRO_METAVARS_IN_UNSAFE.
+    #[lints(pub_underscore_fields)]
+    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,
+    /// Whether to lint only if it's singleline.
+    #[lints(semicolon_outside_block)]
+    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,
+    /// The maximum allowed stack size for functions in bytes
+    #[lints(large_stack_frames)]
+    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<MacroMatcher> = Vec::new(),
+    /// The minimum number of struct fields for the lints about field names to trigger
+    #[lints(struct_field_names)]
+    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,
+    /// 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,
+    /// The maximum number of argument a function or method can have
+    #[lints(too_many_arguments)]
+    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,
+    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
+    /// reference. By default there is no limit
+    #[default_text = "target_pointer_width * 2"]
+    #[lints(trivially_copy_pass_by_ref)]
+    trivial_copy_size_limit: Option<u64> = None,
+    /// The maximum complexity a type can have
+    #[lints(type_complexity)]
+    type_complexity_threshold: u64 = 250,
+    /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
+    #[lints(unnecessary_box_returns)]
+    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,
+    /// 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,
+    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
+    #[lints(vec_box)]
+    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,
+    /// Whether to allow certain wildcard imports (prelude, super in tests).
+    #[lints(wildcard_imports)]
+    warn_on_all_wildcard_imports: bool = false,
     /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
-    (warn_unsafe_macro_metavars_in_private_macros: bool = false),
+    #[lints(macro_metavars_in_unsafe)]
+    warn_unsafe_macro_metavars_in_private_macros: bool = false,
 }
 
 /// Search for the configuration file.
diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs
index ff7fa7241cb..c099585e62a 100644
--- a/clippy_config/src/lib.rs
+++ b/clippy_config/src/lib.rs
@@ -1,4 +1,4 @@
-#![feature(rustc_private, let_chains)]
+#![feature(rustc_private, array_windows, let_chains)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(
     trivial_casts,
diff --git a/clippy_config/src/metadata.rs b/clippy_config/src/metadata.rs
index 8e78aee9d2e..9261fca8870 100644
--- a/clippy_config/src/metadata.rs
+++ b/clippy_config/src/metadata.rs
@@ -1,11 +1,12 @@
-use std::fmt::{self, Write};
+use itertools::Itertools;
+use std::fmt;
 
 #[derive(Debug, Clone, Default)]
 pub struct ClippyConfiguration {
     pub name: String,
     pub default: String,
-    pub lints: Vec<String>,
-    pub doc: String,
+    pub lints: &'static [&'static str],
+    pub doc: &'static str,
     pub deprecation_reason: Option<&'static str>,
 }
 
@@ -20,54 +21,16 @@ impl fmt::Display for ClippyConfiguration {
 }
 
 impl ClippyConfiguration {
-    pub fn new(
-        name: &'static str,
-        default: String,
-        doc_comment: &'static str,
-        deprecation_reason: Option<&'static str>,
-    ) -> Self {
-        let (mut lints, doc) = parse_config_field_doc(doc_comment)
-            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
-
-        lints.sort();
-
-        Self {
-            name: to_kebab(name),
-            lints,
-            doc,
-            default,
-            deprecation_reason,
-        }
-    }
-
     pub fn to_markdown_paragraph(&self) -> String {
-        let mut out = format!(
-            "## `{}`\n{}\n\n",
+        format!(
+            "## `{}`\n{}\n\n**Default Value:** `{}`\n\n---\n**Affected lints:**\n{}\n\n",
             self.name,
-            self.doc
-                .lines()
-                .map(|line| line.strip_prefix("    ").unwrap_or(line))
-                .collect::<Vec<_>>()
-                .join("\n"),
-        );
-
-        if !self.default.is_empty() {
-            write!(out, "**Default Value:** `{}`\n\n", self.default).unwrap();
-        }
-
-        write!(
-            out,
-            "---\n**Affected lints:**\n{}\n\n",
-            self.lints
-                .iter()
-                .map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
-                .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
-                .collect::<Vec<_>>()
-                .join("\n"),
+            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})"
+            ))),
         )
-        .unwrap();
-
-        out
     }
 
     pub fn to_markdown_link(&self) -> String {
@@ -75,47 +38,3 @@ impl ClippyConfiguration {
         format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name)
     }
 }
-
-/// This parses the field documentation of the config struct.
-///
-/// ```rust, ignore
-/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
-/// ```
-///
-/// Would yield:
-/// ```rust, ignore
-/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
-/// ```
-fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
-    const DOC_START: &str = " Lint: ";
-    if doc_comment.starts_with(DOC_START)
-        && let Some(split_pos) = doc_comment.find('.')
-    {
-        let mut doc_comment = doc_comment.to_string();
-        let mut documentation = doc_comment.split_off(split_pos);
-
-        // Extract lints
-        doc_comment.make_ascii_lowercase();
-        let lints: Vec<String> = doc_comment
-            .split_off(DOC_START.len())
-            .lines()
-            .next()
-            .unwrap()
-            .split(", ")
-            .map(str::to_string)
-            .collect();
-
-        // Format documentation correctly
-        // split off leading `.` from lint name list and indent for correct formatting
-        documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
-
-        Some((lints, documentation))
-    } else {
-        None
-    }
-}
-
-/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
-fn to_kebab(config_name: &str) -> String {
-    config_name.replace('_', "-")
-}
diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs
index 25623144181..5fc4365c6e7 100644
--- a/clippy_dev/src/fmt.rs
+++ b/clippy_dev/src/fmt.rs
@@ -1,30 +1,65 @@
 use crate::clippy_project_root;
 use itertools::Itertools;
+use rustc_lexer::{tokenize, TokenKind};
 use shell_escape::escape;
 use std::ffi::{OsStr, OsString};
-use std::path::Path;
+use std::ops::ControlFlow;
+use std::path::{Path, PathBuf};
 use std::process::{self, Command, Stdio};
 use std::{fs, io};
 use walkdir::WalkDir;
 
-#[derive(Debug)]
-pub enum CliError {
+pub enum Error {
     CommandFailed(String, String),
-    IoError(io::Error),
+    Io(io::Error),
     RustfmtNotInstalled,
-    WalkDirError(walkdir::Error),
+    WalkDir(walkdir::Error),
     IntellijSetupActive,
+    Parse(PathBuf, usize, String),
+    CheckFailed,
 }
 
-impl From<io::Error> for CliError {
+impl From<io::Error> for Error {
     fn from(error: io::Error) -> Self {
-        Self::IoError(error)
+        Self::Io(error)
     }
 }
 
-impl From<walkdir::Error> for CliError {
+impl From<walkdir::Error> for Error {
     fn from(error: walkdir::Error) -> Self {
-        Self::WalkDirError(error)
+        Self::WalkDir(error)
+    }
+}
+
+impl Error {
+    fn display(&self) {
+        match self {
+            Self::CheckFailed => {
+                eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
+            },
+            Self::CommandFailed(command, stderr) => {
+                eprintln!("error: command `{command}` failed!\nstderr: {stderr}");
+            },
+            Self::Io(err) => {
+                eprintln!("error: {err}");
+            },
+            Self::RustfmtNotInstalled => {
+                eprintln!("error: rustfmt nightly is not installed.");
+            },
+            Self::WalkDir(err) => {
+                eprintln!("error: {err}");
+            },
+            Self::IntellijSetupActive => {
+                eprintln!(
+                    "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\
+                    Not formatting because that would format the local repo as well!\n\
+                    Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`."
+                );
+            },
+            Self::Parse(path, line, msg) => {
+                eprintln!("error parsing `{}:{line}`: {msg}", path.display());
+            },
+        }
     }
 }
 
@@ -34,75 +69,244 @@ struct FmtContext {
     rustfmt_path: String,
 }
 
-// the "main" function of cargo dev fmt
-pub fn run(check: bool, verbose: bool) {
-    fn try_run(context: &FmtContext) -> Result<bool, CliError> {
-        let mut success = true;
-
-        let project_root = clippy_project_root();
-
-        // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
-        // format because rustfmt would also format the entire rustc repo as it is a local
-        // dependency
-        if fs::read_to_string(project_root.join("Cargo.toml"))
-            .expect("Failed to read clippy Cargo.toml")
-            .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
-        {
-            return Err(CliError::IntellijSetupActive);
-        }
-
-        rustfmt_test(context)?;
+struct ClippyConf<'a> {
+    name: &'a str,
+    attrs: &'a str,
+    lints: Vec<&'a str>,
+    field: &'a str,
+}
 
-        success &= cargo_fmt(context, project_root.as_path())?;
-        success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
-        success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
-        success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
+fn offset_to_line(text: &str, offset: usize) -> usize {
+    match text.split('\n').try_fold((1usize, 0usize), |(line, pos), s| {
+        let pos = pos + s.len() + 1;
+        if pos > offset {
+            ControlFlow::Break(line)
+        } else {
+            ControlFlow::Continue((line + 1, pos))
+        }
+    }) {
+        ControlFlow::Break(x) | ControlFlow::Continue((x, _)) => x,
+    }
+}
 
-        let chunks = WalkDir::new(project_root.join("tests"))
-            .into_iter()
-            .filter_map(|entry| {
-                let entry = entry.expect("failed to find tests");
-                let path = entry.path();
+/// Formats the configuration list in `clippy_config/src/conf.rs`
+#[expect(clippy::too_many_lines)]
+fn fmt_conf(check: bool) -> Result<(), Error> {
+    #[derive(Clone, Copy)]
+    enum State {
+        Start,
+        Docs,
+        Pound,
+        OpenBracket,
+        Attr(u32),
+        Lints,
+        EndLints,
+        Field,
+    }
 
-                if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
-                    None
-                } else {
-                    Some(entry.into_path().into_os_string())
-                }
-            })
-            .chunks(250);
+    let path: PathBuf = [
+        clippy_project_root().as_path(),
+        "clippy_config".as_ref(),
+        "src".as_ref(),
+        "conf.rs".as_ref(),
+    ]
+    .into_iter()
+    .collect();
+    let text = fs::read_to_string(&path)?;
 
-        for chunk in &chunks {
-            success &= rustfmt(context, chunk)?;
-        }
+    let (pre, conf) = text
+        .split_once("define_Conf! {\n")
+        .expect("can't find config definition");
+    let (conf, post) = conf.split_once("\n}\n").expect("can't find config definition");
+    let conf_offset = pre.len() + 15;
 
-        Ok(success)
-    }
+    let mut pos = 0u32;
+    let mut attrs_start = 0;
+    let mut attrs_end = 0;
+    let mut field_start = 0;
+    let mut lints = Vec::new();
+    let mut name = "";
+    let mut fields = Vec::new();
+    let mut state = State::Start;
 
-    fn output_err(err: CliError) {
-        match err {
-            CliError::CommandFailed(command, stderr) => {
-                eprintln!("error: A command failed! `{command}`\nstderr: {stderr}");
+    for (i, t) in tokenize(conf)
+        .map(|x| {
+            let start = pos;
+            pos += x.len;
+            (start as usize, x)
+        })
+        .filter(|(_, t)| !matches!(t.kind, TokenKind::Whitespace))
+    {
+        match (state, t.kind) {
+            (State::Start, TokenKind::LineComment { doc_style: Some(_) }) => {
+                attrs_start = i;
+                attrs_end = i + t.len as usize;
+                state = State::Docs;
             },
-            CliError::IoError(err) => {
-                eprintln!("error: {err}");
+            (State::Start, TokenKind::Pound) => {
+                attrs_start = i;
+                attrs_end = i;
+                state = State::Pound;
             },
-            CliError::RustfmtNotInstalled => {
-                eprintln!("error: rustfmt nightly is not installed.");
+            (State::Docs, TokenKind::LineComment { doc_style: Some(_) }) => attrs_end = i + t.len as usize,
+            (State::Docs, TokenKind::Pound) => state = State::Pound,
+            (State::Pound, TokenKind::OpenBracket) => state = State::OpenBracket,
+            (State::OpenBracket, TokenKind::Ident) => {
+                state = if conf[i..i + t.len as usize] == *"lints" {
+                    State::Lints
+                } else {
+                    State::Attr(0)
+                };
             },
-            CliError::WalkDirError(err) => {
-                eprintln!("error: {err}");
+            (State::Attr(0), TokenKind::CloseBracket) => {
+                attrs_end = i + 1;
+                state = State::Docs;
             },
-            CliError::IntellijSetupActive => {
-                eprintln!(
-                    "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
-Not formatting because that would format the local repo as well!
-Please revert the changes to Cargo.tomls with `cargo dev remove intellij`."
-                );
+            (State::Attr(x), TokenKind::OpenParen | TokenKind::OpenBracket | TokenKind::OpenBrace) => {
+                state = State::Attr(x + 1);
+            },
+            (State::Attr(x), TokenKind::CloseParen | TokenKind::CloseBracket | TokenKind::CloseBrace) => {
+                state = State::Attr(x - 1);
+            },
+            (State::Lints, TokenKind::Ident) => lints.push(&conf[i..i + t.len as usize]),
+            (State::Lints, TokenKind::CloseBracket) => state = State::EndLints,
+            (State::EndLints | State::Docs, TokenKind::Ident) => {
+                field_start = i;
+                name = &conf[i..i + t.len as usize];
+                state = State::Field;
+            },
+            (State::Field, TokenKind::LineComment { doc_style: Some(_) }) => {
+                #[expect(clippy::drain_collect)]
+                fields.push(ClippyConf {
+                    name,
+                    lints: lints.drain(..).collect(),
+                    attrs: &conf[attrs_start..attrs_end],
+                    field: conf[field_start..i].trim_end(),
+                });
+                attrs_start = i;
+                attrs_end = i + t.len as usize;
+                state = State::Docs;
+            },
+            (State::Field, TokenKind::Pound) => {
+                #[expect(clippy::drain_collect)]
+                fields.push(ClippyConf {
+                    name,
+                    lints: lints.drain(..).collect(),
+                    attrs: &conf[attrs_start..attrs_end],
+                    field: conf[field_start..i].trim_end(),
+                });
+                attrs_start = i;
+                attrs_end = i;
+                state = State::Pound;
+            },
+            (State::Field | State::Attr(_), _)
+            | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {},
+            _ => {
+                return Err(Error::Parse(
+                    path,
+                    offset_to_line(&text, conf_offset + i),
+                    format!("unexpected token `{}`", &conf[i..i + t.len as usize]),
+                ));
             },
         }
     }
 
+    if !matches!(state, State::Field) {
+        return Err(Error::Parse(
+            path,
+            offset_to_line(&text, conf_offset + conf.len()),
+            "incomplete field".into(),
+        ));
+    }
+    fields.push(ClippyConf {
+        name,
+        lints,
+        attrs: &conf[attrs_start..attrs_end],
+        field: conf[field_start..].trim_end(),
+    });
+
+    for field in &mut fields {
+        field.lints.sort_unstable();
+    }
+    fields.sort_by_key(|x| x.name);
+
+    let new_text = format!(
+        "{pre}define_Conf! {{\n{}}}\n{post}",
+        fields.iter().format_with("", |field, f| {
+            if field.lints.is_empty() {
+                f(&format_args!("    {}\n    {}\n", field.attrs, field.field))
+            } else if field.lints.iter().map(|x| x.len() + 2).sum::<usize>() < 120 - 14 {
+                f(&format_args!(
+                    "    {}\n    #[lints({})]\n    {}\n",
+                    field.attrs,
+                    field.lints.iter().join(", "),
+                    field.field,
+                ))
+            } else {
+                f(&format_args!(
+                    "    {}\n    #[lints({}\n    )]\n    {}\n",
+                    field.attrs,
+                    field
+                        .lints
+                        .iter()
+                        .format_with("", |x, f| f(&format_args!("\n        {x},"))),
+                    field.field,
+                ))
+            }
+        })
+    );
+
+    if text != new_text {
+        if check {
+            return Err(Error::CheckFailed);
+        }
+        fs::write(&path, new_text.as_bytes())?;
+    }
+    Ok(())
+}
+
+fn run_rustfmt(context: &FmtContext) -> Result<(), Error> {
+    let project_root = clippy_project_root();
+
+    // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
+    // format because rustfmt would also format the entire rustc repo as it is a local
+    // dependency
+    if fs::read_to_string(project_root.join("Cargo.toml"))
+        .expect("Failed to read clippy Cargo.toml")
+        .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
+    {
+        return Err(Error::IntellijSetupActive);
+    }
+
+    check_for_rustfmt(context)?;
+
+    cargo_fmt(context, project_root.as_path())?;
+    cargo_fmt(context, &project_root.join("clippy_dev"))?;
+    cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
+    cargo_fmt(context, &project_root.join("lintcheck"))?;
+
+    let chunks = WalkDir::new(project_root.join("tests"))
+        .into_iter()
+        .filter_map(|entry| {
+            let entry = entry.expect("failed to find tests");
+            let path = entry.path();
+
+            if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
+                None
+            } else {
+                Some(entry.into_path().into_os_string())
+            }
+        })
+        .chunks(250);
+
+    for chunk in &chunks {
+        rustfmt(context, chunk)?;
+    }
+    Ok(())
+}
+
+// the "main" function of cargo dev fmt
+pub fn run(check: bool, verbose: bool) {
     let output = Command::new("rustup")
         .args(["which", "rustfmt"])
         .stderr(Stdio::inherit())
@@ -120,21 +324,10 @@ Please revert the changes to Cargo.tomls with `cargo dev remove intellij`."
         verbose,
         rustfmt_path,
     };
-    let result = try_run(&context);
-    let code = match result {
-        Ok(true) => 0,
-        Ok(false) => {
-            eprintln!();
-            eprintln!("Formatting check failed.");
-            eprintln!("Run `cargo dev fmt` to update formatting.");
-            1
-        },
-        Err(err) => {
-            output_err(err);
-            1
-        },
-    };
-    process::exit(code);
+    if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) {
+        e.display();
+        process::exit(1);
+    }
 }
 
 fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
@@ -148,12 +341,12 @@ fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[imp
     )
 }
 
-fn exec(
+fn exec_fmt_command(
     context: &FmtContext,
     program: impl AsRef<OsStr>,
     dir: impl AsRef<Path>,
     args: &[impl AsRef<OsStr>],
-) -> Result<bool, CliError> {
+) -> Result<(), Error> {
     if context.verbose {
         println!("{}", format_command(&program, &dir, args));
     }
@@ -166,28 +359,28 @@ fn exec(
         .unwrap();
     let success = output.status.success();
 
-    if !context.check && !success {
-        let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
-        return Err(CliError::CommandFailed(
-            format_command(&program, &dir, args),
-            String::from(stderr),
-        ));
+    match (context.check, success) {
+        (_, true) => Ok(()),
+        (true, false) => Err(Error::CheckFailed),
+        (false, false) => {
+            let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
+            Err(Error::CommandFailed(
+                format_command(&program, &dir, args),
+                String::from(stderr),
+            ))
+        },
     }
-
-    Ok(success)
 }
 
-fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
+fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> {
     let mut args = vec!["fmt", "--all"];
     if context.check {
         args.push("--check");
     }
-    let success = exec(context, "cargo", path, &args)?;
-
-    Ok(success)
+    exec_fmt_command(context, "cargo", path, &args)
 }
 
-fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
+fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> {
     let program = "rustfmt";
     let dir = std::env::current_dir()?;
     let args = &["--version"];
@@ -204,23 +397,20 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
         .unwrap_or("")
         .starts_with("error: 'rustfmt' is not installed")
     {
-        Err(CliError::RustfmtNotInstalled)
+        Err(Error::RustfmtNotInstalled)
     } else {
-        Err(CliError::CommandFailed(
+        Err(Error::CommandFailed(
             format_command(program, &dir, args),
             std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
         ))
     }
 }
 
-fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> {
+fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<(), Error> {
     let mut args = Vec::new();
     if context.check {
         args.push(OsString::from("--check"));
     }
     args.extend(paths);
-
-    let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?;
-
-    Ok(success)
+    exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args)
 }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 9c035bfca39..0ca66063ef2 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -496,7 +496,7 @@ pub fn explain(name: &str) -> i32 {
         // Check if the lint has configuration
         let mut mdconf = get_configuration_metadata();
         let name = name.to_ascii_lowercase();
-        mdconf.retain(|cconf| cconf.lints.contains(&name));
+        mdconf.retain(|cconf| cconf.lints.contains(&&*name));
         if !mdconf.is_empty() {
             println!("### Configuration for {}:\n", info.lint.name_lower());
             for conf in mdconf {
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 54cab716fbd..b5a772e7a81 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -165,7 +165,7 @@ impl MetadataCollector {
     fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
         self.config
             .iter()
-            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
+            .filter(|config| config.lints.iter().any(|&lint| lint == lint_name))
             .map(ToString::to_string)
             .reduce(|acc, x| acc + "\n\n" + &x)
             .map(|configurations| {