Rollup merge of #128837 - flip1995:clippy-subtree-update, r=Manishearth

Clippy subtree update

r? ``@Manishearth``

Updates Cargo.lock due to uitest bump
This commit is contained in:
Matthias Krüger 2024-08-09 00:03:38 +02:00 committed by GitHub
commit 58c6497e4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
298 changed files with 5630 additions and 4069 deletions

View File

@ -564,7 +564,7 @@ dependencies = [
"termize",
"tokio",
"toml 0.7.8",
"ui_test 0.24.0",
"ui_test 0.25.0",
"walkdir",
]
@ -572,6 +572,7 @@ dependencies = [
name = "clippy_config"
version = "0.1.82"
dependencies = [
"itertools",
"rustc-semver",
"serde",
"toml 0.7.8",
@ -4936,9 +4937,9 @@ dependencies = [
[[package]]
name = "spanned"
version = "0.2.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed14ba8b4b82241bd5daba2c49185d4a0581a0058355fe96537338f002b8605d"
checksum = "86af297923fbcfd107c20a189a6e9c872160df71a7190ae4a7a6c5dce4b2feb6"
dependencies = [
"bstr",
"color-eyre",
@ -5562,9 +5563,9 @@ dependencies = [
[[package]]
name = "ui_test"
version = "0.24.0"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc1c6c78d55482388711c8d417b8e547263046a607512278fed274c54633bbe4"
checksum = "f7e4f339f62edc873975c47115f9e71c5454ddaa37c1142b42fc0b2672c8dacb"
dependencies = [
"annotate-snippets 0.11.4",
"anyhow",

View File

@ -13,6 +13,13 @@ target-dir = "target"
[unstable]
binary-dep-depinfo = true
profile-rustflags = true
[profile.dev]
split-debuginfo = "unpacked"
# Add back the containing directory of the packages we have to refer to using --manifest-path
[profile.dev.package.clippy_dev]
rustflags = ["--remap-path-prefix", "=clippy_dev"]
[profile.dev.package.lintcheck]
rustflags = ["--remap-path-prefix", "=lintcheck"]

View File

@ -25,6 +25,7 @@ env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
CARGO_INCREMENTAL: 0
RUSTFLAGS: -D warnings
concurrency:
# For a given workflow, if we push to the same PR, cancel all previous builds on that PR.
@ -47,25 +48,25 @@ jobs:
# Run
- name: Build
run: cargo build --tests --features deny-warnings,internal
run: cargo build --tests --features internal
- name: Test
run: cargo test --features deny-warnings,internal
run: cargo test --features internal
- name: Test clippy_lints
run: cargo test --features deny-warnings,internal
run: cargo test --features internal
working-directory: clippy_lints
- name: Test clippy_utils
run: cargo test --features deny-warnings
run: cargo test
working-directory: clippy_utils
- name: Test rustc_tools_util
run: cargo test --features deny-warnings
run: cargo test
working-directory: rustc_tools_util
- name: Test clippy_dev
run: cargo test --features deny-warnings
run: cargo test
working-directory: clippy_dev
- name: Test clippy-driver

View File

@ -11,6 +11,7 @@ env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
CARGO_INCREMENTAL: 0
RUSTFLAGS: -D warnings
concurrency:
# For a given workflow, if we push to the same branch, cancel all previous builds on that branch.
@ -85,34 +86,34 @@ jobs:
# Run
- name: Build
run: cargo build --tests --features deny-warnings,internal
run: cargo build --tests --features internal
- name: Test
if: matrix.host == 'x86_64-unknown-linux-gnu'
run: cargo test --features deny-warnings,internal
run: cargo test --features internal
- name: Test
if: matrix.host != 'x86_64-unknown-linux-gnu'
run: cargo test --features deny-warnings,internal -- --skip dogfood
run: cargo test --features internal -- --skip dogfood
- name: Test clippy_lints
run: cargo test --features deny-warnings,internal
run: cargo test --features internal
working-directory: clippy_lints
- name: Test clippy_utils
run: cargo test --features deny-warnings
run: cargo test
working-directory: clippy_utils
- name: Test clippy_config
run: cargo test --features deny-warnings
run: cargo test
working-directory: clippy_config
- name: Test rustc_tools_util
run: cargo test --features deny-warnings
run: cargo test
working-directory: rustc_tools_util
- name: Test clippy_dev
run: cargo test --features deny-warnings
run: cargo test
working-directory: clippy_dev
- name: Test clippy-driver

View File

@ -16,6 +16,7 @@ on:
env:
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
RUSTFLAGS: -D warnings
jobs:
clippy_dev:
@ -28,7 +29,7 @@ jobs:
# Run
- name: Build
run: cargo build --features deny-warnings
run: cargo build
working-directory: clippy_dev
- name: Test update_lints
@ -38,6 +39,8 @@ jobs:
run: cargo dev fmt --check
- name: Test cargo dev new lint
env:
RUSTFLAGS: -A unused-imports
run: |
cargo dev new_lint --name new_early_pass --pass early
cargo dev new_lint --name new_late_pass --pass late

View File

@ -58,7 +58,7 @@ jobs:
- name: Run lintcheck
if: steps.cache-json.outputs.cache-hit != 'true'
run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml
run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
- name: Upload base JSON
uses: actions/upload-artifact@v4
@ -86,7 +86,7 @@ jobs:
run: cargo build --manifest-path=lintcheck/Cargo.toml
- name: Run lintcheck
run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml
run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
- name: Upload head JSON
uses: actions/upload-artifact@v4

View File

@ -5830,6 +5830,7 @@ Released 2018-09-13
[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
@ -5998,6 +5999,7 @@ Released 2018-09-13
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit

View File

@ -30,7 +30,7 @@ color-print = "0.3.4"
anstream = "0.6.0"
[dev-dependencies]
ui_test = "0.24"
ui_test = "0.25"
regex = "1.5.5"
toml = "0.7.3"
walkdir = "2.3"
@ -51,7 +51,6 @@ tokio = { version = "1", features = ["io-util"] }
rustc_tools_util = "0.3.0"
[features]
deny-warnings = ["clippy_lints/deny-warnings"]
integration = ["tempfile"]
internal = ["clippy_lints/internal", "tempfile"]

View File

@ -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)

View File

@ -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"
@ -13,9 +14,6 @@ toml = "0.7.3"
[dev-dependencies]
walkdir = "2.3"
[features]
deny-warnings = []
[package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)]
rustc_private = true

View File

@ -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,411 +202,91 @@ 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.
///
/// 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.
/// 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
/// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"]
/// allow-renamed-params-for = [ "std::convert::From" ]
/// ```
///
/// #### Noteworthy
///
/// 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.
///
/// Suppress checking of the passed type pair names in binary operations like addition or
/// multiplication.
///
/// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless
/// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`.
///
/// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as
/// `["AnotherType", "SomeType"]`.
///
/// #### Example
///
/// ```toml
/// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
/// ```
(arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()),
/// Lint: ARITHMETIC_SIDE_EFFECTS.
///
/// Suppress checking of the passed type names in unary operations like "negation" (`-`).
///
/// #### Example
///
/// ```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.
///
/// 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()),
/// 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.
///
/// 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),
/// 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.
///
/// 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
/// - 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.
(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.
///
/// 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.
///
/// 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.
///
#[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)]`
(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.
///
#[lints(unwrap_used)]
allow_unwrap_in_tests: bool = false,
/// 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.
///
#[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.
(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.
///
/// Whether to recommend using implicit into iter for reborrowed values.
///
/// #### Example
/// ```no_run
/// let mut vec = vec![1, 2, 3];
/// let rmvec = &mut vec;
/// for _ in rmvec.iter() {}
/// for _ in rmvec.iter_mut() {}
/// ```
///
/// Use instead:
/// ```no_run
/// let mut vec = vec![1, 2, 3];
/// let rmvec = &mut vec;
/// 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.
///
/// 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.
///
#[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.
@ -623,28 +304,343 @@ define_Conf! {
/// `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.
#[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
/// allow-renamed-params-for = [ "std::convert::From" ]
/// allowed-wildcard-imports = [ "utils", "common" ]
/// ```
///
/// #### 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.
/// 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.
///
/// #### Example
///
/// ```toml
/// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"]
/// ```
///
/// #### Noteworthy
///
/// 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<String> = <_>::default(),
/// Suppress checking of the passed type pair names in binary operations like addition or
/// multiplication.
///
/// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless
/// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`.
///
/// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as
/// `["AnotherType", "SomeType"]`.
///
/// #### Example
///
/// ```toml
/// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
/// ```
#[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
///
/// ```toml
/// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
/// ```
#[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.
#[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(),
/// 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
#[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,
/// 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.
#[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.
#[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`.
#[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
/// ```no_run
/// let mut vec = vec![1, 2, 3];
/// let rmvec = &mut vec;
/// for _ in rmvec.iter() {}
/// for _ in rmvec.iter_mut() {}
/// ```
///
/// Use instead:
/// ```no_run
/// let mut vec = vec![1, 2, 3];
/// let rmvec = &mut vec;
/// for _ in &*rmvec {}
/// for _ in &mut *rmvec {}
/// ```
#[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".
#[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.

View File

@ -1,5 +1,4 @@
#![feature(rustc_private, let_chains)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![feature(rustc_private, array_windows, let_chains)]
#![warn(
trivial_casts,
trivial_numeric_casts,

View File

@ -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>,
}
@ -13,61 +14,23 @@ impl fmt::Display for ClippyConfiguration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "- `{}`: {}", self.name, self.doc)?;
if !self.default.is_empty() {
write!(f, " (default: `{}`)", self.default)?;
write!(f, "\n\n (default: `{}`)", self.default)?;
}
Ok(())
}
}
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('_', "-")
}

View File

@ -13,9 +13,6 @@ opener = "0.6"
shell-escape = "0.1"
walkdir = "2.3"
[features]
deny-warnings = []
[package.metadata.rust-analyzer]
# This package uses #[feature(rustc_private)]
rustc_private = true

View File

@ -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,
}
struct ClippyConf<'a> {
name: &'a str,
attrs: &'a str,
lints: Vec<&'a str>,
field: &'a str,
}
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,
}
}
/// 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,
}
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)?;
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;
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;
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;
},
(State::Start, TokenKind::Pound) => {
attrs_start = i;
attrs_end = i;
state = State::Pound;
},
(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)
};
},
(State::Attr(0), TokenKind::CloseBracket) => {
attrs_end = i + 1;
state = State::Docs;
},
(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) {
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)?;
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"))?;
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 {
success &= rustfmt(context, chunk)?;
}
Ok(success)
}
fn output_err(err: CliError) {
match err {
CliError::CommandFailed(command, stderr) => {
eprintln!("error: A command failed! `{command}`\nstderr: {stderr}");
},
CliError::IoError(err) => {
eprintln!("error: {err}");
},
CliError::RustfmtNotInstalled => {
eprintln!("error: rustfmt nightly is not installed.");
},
CliError::WalkDirError(err) => {
eprintln!("error: {err}");
},
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`."
);
},
}
}
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)
}

View File

@ -1,6 +1,5 @@
#![feature(let_chains)]
#![feature(rustc_private)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(
trivial_casts,
trivial_numeric_casts,

View File

@ -1,4 +1,3 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
@ -74,7 +73,7 @@ fn main() {
new_name,
uplift,
} => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()),
DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason),
}
}
@ -223,7 +222,7 @@ enum DevCommand {
name: String,
#[arg(long, short)]
/// The reason for deprecation
reason: Option<String>,
reason: String,
},
}

View File

@ -1,10 +1,14 @@
use std::ffi::OsStr;
use std::num::ParseIntError;
use std::path::Path;
use std::process::Command;
use std::time::{Duration, SystemTime};
use std::{env, thread};
#[cfg(windows)]
const PYTHON: &str = "python";
#[cfg(not(windows))]
const PYTHON: &str = "python3";
/// # Panics
///
/// Panics if the python commands could not be spawned
@ -25,7 +29,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
}
if let Some(url) = url.take() {
thread::spawn(move || {
Command::new("python3")
Command::new(PYTHON)
.arg("-m")
.arg("http.server")
.arg(port.to_string())
@ -58,8 +62,3 @@ fn mtime(path: impl AsRef<Path>) -> SystemTime {
.unwrap_or(SystemTime::UNIX_EPOCH)
}
}
#[allow(clippy::missing_errors_doc)]
pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> {
arg.to_string_lossy().parse::<u16>().map(|_| ())
}

View File

@ -1,13 +1,12 @@
use crate::clippy_project_root;
use aho_corasick::AhoCorasickBuilder;
use indoc::writedoc;
use itertools::Itertools;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::fmt::{self, Write};
use std::fs::{self, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write as _};
use std::io::{self, Read, Seek, Write as _};
use std::ops::Range;
use std::path::{Path, PathBuf};
use walkdir::{DirEntry, WalkDir};
@ -77,12 +76,8 @@ fn generate_lint_files(
for lint in usable_lints
.iter()
.map(|l| &*l.name)
.chain(deprecated_lints.iter().map(|l| &*l.name))
.chain(
renamed_lints
.iter()
.map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)),
)
.chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
.chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
.sorted()
{
writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
@ -108,11 +103,6 @@ fn generate_lint_files(
update_mode,
&gen_declared_lints(internal_lints.iter(), usable_lints.iter()),
);
process_file(
"clippy_lints/src/lib.deprecated.rs",
update_mode,
&gen_deprecated(deprecated_lints),
);
let content = gen_deprecated_lints_test(deprecated_lints);
process_file("tests/ui/deprecated.rs", update_mode, &content);
@ -205,7 +195,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
let ext = f.path().extension();
(ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
&& name != Some(OsStr::new("rename.rs"))
&& name != Some(OsStr::new("renamed_lints.rs"))
&& name != Some(OsStr::new("deprecated_lints.rs"))
})
{
rewrite_file(file.path(), |s| {
@ -213,6 +203,19 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
});
}
let version = crate::new_lint::get_stabilization_version();
rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| {
insert_at_marker(
s,
"// end renamed lints. used by `cargo dev rename_lint`",
&format!(
"#[clippy::version = \"{version}\"]\n \
(\"{}\", \"{}\"),\n ",
lint.old_name, lint.new_name,
),
)
});
renamed_lints.push(lint);
renamed_lints.sort_by(|lhs, rhs| {
lhs.new_name
@ -222,11 +225,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
.then_with(|| lhs.old_name.cmp(&rhs.old_name))
});
write_file(
Path::new("clippy_lints/src/renamed_lints.rs"),
&gen_renamed_lints_list(&renamed_lints),
);
if uplift {
write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
println!(
@ -293,7 +291,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
// Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
// renamed.
for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs"))
{
rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
}
@ -304,7 +303,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
println!("note: `cargo uitest` still needs to be run to update the test results");
}
const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
/// Runs the `deprecate` command
///
/// This does the following:
@ -314,33 +312,16 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
/// # Panics
///
/// If a file path could not read from or written to
pub fn deprecate(name: &str, reason: Option<&str>) {
fn finish(
(lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
name: &str,
reason: &str,
) {
deprecated_lints.push(DeprecatedLint {
name: name.to_string(),
reason: reason.to_string(),
declaration_range: Range::default(),
});
pub fn deprecate(name: &str, reason: &str) {
let prefixed_name = if name.starts_with("clippy::") {
name.to_owned()
} else {
format!("clippy::{name}")
};
let stripped_name = &prefixed_name[8..];
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
println!("info: `{name}` has successfully been deprecated");
if reason == DEFAULT_DEPRECATION_REASON {
println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
}
println!("note: you must run `cargo uitest` to update the test results");
}
let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON);
let name_lower = name.to_lowercase();
let name_upper = name.to_uppercase();
let (mut lints, deprecated_lints, renamed_lints) = gather_all();
let Some(lint) = lints.iter().find(|l| l.name == name_lower) else {
let (mut lints, mut deprecated_lints, renamed_lints) = gather_all();
let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else {
eprintln!("error: failed to find lint `{name}`");
return;
};
@ -357,13 +338,27 @@ pub fn deprecate(name: &str, reason: Option<&str>) {
let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
finish((lints, deprecated_lints, renamed_lints), name, reason);
return;
}
if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) {
let version = crate::new_lint::get_stabilization_version();
rewrite_file(deprecated_lints_path, |s| {
insert_at_marker(
s,
"// end deprecated lints. used by `cargo dev deprecate_lint`",
&format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",),
)
});
eprintln!("error: lint not found");
deprecated_lints.push(DeprecatedLint {
name: prefixed_name,
reason: reason.into(),
});
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
println!("info: `{name}` has successfully been deprecated");
println!("note: you must run `cargo uitest` to update the test results");
} else {
eprintln!("error: lint not found");
}
}
fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
@ -377,14 +372,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
// Some lints have their own directories, delete them
if path.is_dir() {
fs::remove_dir_all(path).ok();
let _ = fs::remove_dir_all(path);
return;
}
// Remove all related test files
fs::remove_file(path.with_extension("rs")).ok();
fs::remove_file(path.with_extension("stderr")).ok();
fs::remove_file(path.with_extension("fixed")).ok();
let _ = fs::remove_file(path.with_extension("rs"));
let _ = fs::remove_file(path.with_extension("stderr"));
let _ = fs::remove_file(path.with_extension("fixed"));
}
fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
@ -427,7 +422,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
lint_mod_path.set_file_name(name);
lint_mod_path.set_extension("rs");
fs::remove_file(lint_mod_path).ok();
let _ = fs::remove_file(lint_mod_path);
}
let mut content =
@ -465,37 +460,6 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
Ok(false)
}
fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
let mut file = OpenOptions::new().write(true).open(path)?;
file.seek(SeekFrom::End(0))?;
let version = crate::new_lint::get_stabilization_version();
let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
"TODO"
} else {
reason
};
writedoc!(
file,
"
declare_deprecated_lint! {{
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// {deprecation_reason}
#[clippy::version = \"{version}\"]
pub {name},
\"{reason}\"
}}
"
)
}
/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
/// were no replacements.
fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
@ -604,14 +568,12 @@ impl Lint {
struct DeprecatedLint {
name: String,
reason: String,
declaration_range: Range<usize>,
}
impl DeprecatedLint {
fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
fn new(name: &str, reason: &str) -> Self {
Self {
name: name.to_lowercase(),
name: remove_line_splices(name),
reason: remove_line_splices(reason),
declaration_range,
}
}
}
@ -629,28 +591,6 @@ impl RenamedLint {
}
}
/// Generates the `register_removed` code
#[must_use]
fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("{\n");
for lint in lints {
let _: fmt::Result = write!(
output,
concat!(
" store.register_removed(\n",
" \"clippy::{}\",\n",
" \"{}\",\n",
" );\n"
),
lint.name, lint.reason,
);
}
output.push_str("}\n");
output
}
/// Generates the code for registering lints
#[must_use]
fn gen_declared_lints<'a>(
@ -680,7 +620,7 @@ fn gen_declared_lints<'a>(
fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
let mut res: String = GENERATED_FILE_COMMENT.into();
for lint in lints {
writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
}
res.push_str("\nfn main() {}\n");
res
@ -699,27 +639,13 @@ fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
seen_lints.clear();
for lint in lints {
if seen_lints.insert(&lint.old_name) {
writeln!(res, "#![warn({})]", lint.old_name).unwrap();
writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
}
}
res.push_str("\nfn main() {}\n");
res
}
fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
const HEADER: &str = "\
// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
#[rustfmt::skip]\n\
pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
let mut res = String::from(HEADER);
for lint in lints {
writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
}
res.push_str("];\n");
res
}
/// Gathers all lints defined in `clippy_lints/src`
fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
let mut lints = Vec::with_capacity(1000);
@ -744,10 +670,10 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
module.strip_suffix(".rs").unwrap_or(&module)
};
match module {
"deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
"renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
_ => parse_contents(&contents, module, &mut lints),
if module == "deprecated_lints" {
parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints);
} else {
parse_contents(&contents, module, &mut lints);
}
}
(lints, deprecated_lints, renamed_lints)
@ -848,54 +774,37 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
}
/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
let mut offset = 0usize;
let mut iter = tokenize(contents).map(|t| {
let range = offset..offset + t.len as usize;
offset = range.end;
fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint>, renamed: &mut Vec<RenamedLint>) {
let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else {
return;
};
let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else {
return;
};
LintDeclSearchResult {
token_kind: t.kind,
content: &contents[range.clone()],
range,
}
});
for line in deprecated_src.lines() {
let mut offset = 0usize;
let mut iter = tokenize(line).map(|t| {
let range = offset..offset + t.len as usize;
offset = range.end;
while let Some(LintDeclSearchResult { range, .. }) = iter.find(
|LintDeclSearchResult {
token_kind, content, ..
}| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
) {
let start = range.start;
let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
!matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
LintDeclSearchResult {
token_kind: t.kind,
content: &line[range.clone()],
range,
}
});
let (name, reason) = match_tokens!(
iter,
// !{
Bang OpenBrace
// #[clippy::version = "version"]
Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
// pub LINT_NAME,
Ident Ident(name) Comma
// "description"
Literal{kind: LiteralKind::Str{..},..}(reason)
// ("old_name",
Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma
// "new_name"),
Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma
);
if let Some(LintDeclSearchResult {
token_kind: TokenKind::CloseBrace,
range,
..
}) = iter.next()
{
lints.push(DeprecatedLint::new(name, reason, start..range.end));
}
deprecated.push(DeprecatedLint::new(name, reason));
}
}
fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
for line in contents.lines() {
for line in renamed_src.lines() {
let mut offset = 0usize;
let mut iter = tokenize(line).map(|t| {
let range = offset..offset + t.len as usize;
@ -915,7 +824,7 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
// "new_name"),
Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
);
lints.push(RenamedLint::new(old_name, new_name));
renamed.push(RenamedLint::new(old_name, new_name));
}
}
@ -1015,6 +924,12 @@ fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
panic!("failed to {action} file `{}`: {error}", name.display())
}
fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option<String> {
let i = text.find(marker)?;
let (pre, post) = text.split_at(i);
Some([pre, new_text, post].into_iter().collect())
}
fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
let mut file = OpenOptions::new()
.write(true)
@ -1084,31 +999,6 @@ mod tests {
assert_eq!(expected, result);
}
#[test]
fn test_parse_deprecated_contents() {
static DEPRECATED_CONTENTS: &str = r#"
/// some doc comment
declare_deprecated_lint! {
#[clippy::version = "I'm a version"]
pub SHOULD_ASSERT_EQ,
"`assert!()` will be more flexible with RFC 2011"
}
"#;
let mut result = Vec::new();
parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
for r in &mut result {
r.declaration_range = Range::default();
}
let expected = vec![DeprecatedLint::new(
"should_assert_eq",
"\"`assert!()` will be more flexible with RFC 2011\"",
Range::default(),
)];
assert_eq!(expected, result);
}
#[test]
fn test_usable_lints() {
let lints = vec![
@ -1177,34 +1067,4 @@ mod tests {
);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
}
#[test]
fn test_gen_deprecated() {
let lints = vec![
DeprecatedLint::new(
"should_assert_eq",
"\"has been superseded by should_assert_eq2\"",
Range::default(),
),
DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
];
let expected = GENERATED_FILE_COMMENT.to_string()
+ &[
"{",
" store.register_removed(",
" \"clippy::should_assert_eq\",",
" \"has been superseded by should_assert_eq2\",",
" );",
" store.register_removed(",
" \"clippy::another_deprecated\",",
" \"will be removed\",",
" );",
"}",
]
.join("\n")
+ "\n";
assert_eq!(expected, gen_deprecated(&lints));
}
}

View File

@ -32,7 +32,6 @@ url = "2.2"
walkdir = "2.3"
[features]
deny-warnings = ["clippy_config/deny-warnings", "clippy_utils/deny-warnings"]
# build clippy with internal lints enabled, off by default
internal = ["serde_json", "tempfile", "regex"]

View File

@ -97,7 +97,7 @@ impl ApproxConstant {
cx,
APPROX_CONSTANT,
e.span,
format!("approximate value of `{module}::consts::{}` found", &name),
format!("approximate value of `{module}::consts::{name}` found"),
None,
"consider using the constant directly",
);

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -52,13 +52,15 @@ impl<'tcx> LateLintPass<'tcx> for AsConversions {
&& !in_external_macro(cx.sess(), expr.span)
&& !is_from_proc_macro(cx, expr)
{
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
AS_CONVERSIONS,
expr.span,
"using a potentially dangerous silent `as` conversion",
None,
"consider using a safe wrapper for this conversion",
|diag| {
diag.help("consider using a safe wrapper for this conversion");
},
);
}
}

View File

@ -1,6 +1,6 @@
use std::fmt;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
use rustc_ast::{InlineAsm, Item, ItemKind};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
@ -49,14 +49,10 @@ fn check_asm_syntax(
};
if style == check_for {
span_lint_and_help(
cx,
lint,
span,
format!("{style} x86 assembly syntax used"),
None,
format!("use {} x86 assembly syntax", !style),
);
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(cx, lint, span, format!("{style} x86 assembly syntax used"), |diag| {
diag.help(format!("use {} x86 assembly syntax", !style));
});
}
}
}
@ -98,13 +94,13 @@ declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
impl EarlyLintPass for InlineAsmX86IntelSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Intel);
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel);
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Intel);
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel);
}
}
}
@ -146,13 +142,13 @@ declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]);
impl EarlyLintPass for InlineAsmX86AttSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Att);
check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Att);
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Att);
check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, item.span, AsmStyle::Att);
}
}
}

View File

@ -1,4 +1,4 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_inside_always_const_context;
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else {
return;
};
let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else {
let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else {
return;
};

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
@ -68,39 +68,28 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
return;
}
}
let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
let mut app = Applicability::MachineApplicable;
match method_segment.ident.as_str() {
let (message, replacement) = match method_segment.ident.as_str() {
"is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => {
span_lint_and_sugg(
cx,
ASSERTIONS_ON_RESULT_STATES,
macro_call.span,
"called `assert!` with `Result::is_ok`",
"replace with",
format!(
"{}.unwrap(){semicolon}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
),
app,
);
("called `assert!` with `Result::is_ok`", "unwrap")
},
"is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => {
span_lint_and_sugg(
cx,
ASSERTIONS_ON_RESULT_STATES,
macro_call.span,
"called `assert!` with `Result::is_err`",
"replace with",
format!(
"{}.unwrap_err(){semicolon}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
),
app,
);
("called `assert!` with `Result::is_err`", "unwrap_err")
},
_ => (),
_ => return,
};
span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| {
let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
let mut app = Applicability::MachineApplicable;
diag.span_suggestion(
macro_call.span,
"replace with",
format!(
"{}.{replacement}(){semicolon}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
),
app,
);
});
}
}
}

View File

@ -227,9 +227,22 @@ fn build_sugg<'tcx>(
match call_kind {
CallKind::Method => {
let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
// `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
// If `ref_expr` is a reference, we can remove the dereference operator (`*`) to make
// the generated code a bit simpler. In other cases, we don't do this special case, to avoid
// having to deal with Deref (https://github.com/rust-lang/rust-clippy/issues/12437).
let ty = cx.typeck_results().expr_ty(ref_expr);
if ty.is_ref() {
// Apply special case, remove `*`
// `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
} else {
// Keep the original lhs
// `*lhs = self_expr.clone();` -> `(*lhs).clone_from(self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", app)
}
} else {
// Keep the original lhs
// `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", app)
}
@ -249,8 +262,16 @@ fn build_sugg<'tcx>(
},
CallKind::Ufcs => {
let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
// See special case of removing `*` in method handling above
let ty = cx.typeck_results().expr_ty(ref_expr);
if ty.is_ref() {
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", app)
} else {
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut *lhs, self_expr)`
// mut_addr_deref is used to avoid unnecessary parentheses around `*lhs`
Sugg::hir_with_applicability(cx, ref_expr, "_", app).mut_addr_deref()
}
} else {
// `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr()

View File

@ -1,5 +1,5 @@
use super::ALLOW_ATTRIBUTES;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use rustc_ast::{AttrStyle, Attribute};
use rustc_errors::Applicability;
@ -13,14 +13,14 @@ pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) {
&& let Some(ident) = attr.ident()
&& !is_from_proc_macro(cx, attr)
{
span_lint_and_sugg(
cx,
ALLOW_ATTRIBUTES,
ident.span,
"#[allow] attribute found",
"replace it with",
"expect".into(),
Applicability::MachineApplicable,
);
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(cx, ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", |diag| {
diag.span_suggestion(
ident.span,
"replace it with",
"expect",
Applicability::MachineApplicable,
);
});
}
}

View File

@ -1,5 +1,5 @@
use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_lint::{LateContext, LintContext};
@ -21,12 +21,14 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet
return;
}
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
ALLOW_ATTRIBUTES_WITHOUT_REASON,
attr.span,
format!("`{}` attribute without specifying a reason", name.as_str()),
None,
"try adding a reason at the end with `, reason = \"..\"`",
|diag| {
diag.help("try adding a reason at the end with `, reason = \"..\"`");
},
);
}

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::Sugg;
use clippy_utils::{in_constant, is_else_clause};
use clippy_utils::{is_else_clause, is_in_const_context};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
&& let Some(else_lit) = as_int_bool_lit(else_)
&& then_lit != else_lit
&& !expr.span.from_expansion()
&& !in_constant(cx, expr.hir_id)
&& !is_in_const_context(cx)
{
let ty = cx.typeck_results().expr_ty(then);
let mut applicability = Applicability::MachineApplicable;

View File

@ -477,14 +477,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
cx: self.cx,
};
if let Ok(expr) = h2q.run(e) {
if h2q.terminals.len() > 8 {
// QMC has exponentially slow behavior as the number of terminals increases
// 8 is reasonable, it takes approximately 0.2 seconds.
// See #825
let stats = terminal_stats(&expr);
if stats.ops > 7 {
// QMC has exponentially slow behavior as the number of ops increases.
// See #825, #13206
return;
}
let stats = terminal_stats(&expr);
let mut simplified = expr.simplify();
for simple in Bool::Not(Box::new(expr)).simplify() {
match simple {

View File

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::in_constant;
use clippy_utils::is_in_const_context;
use clippy_utils::source::snippet_opt;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_isize_or_usize;
@ -21,7 +21,7 @@ pub(super) fn check(
cast_to_hir: &rustc_hir::Ty<'_>,
msrv: &Msrv,
) {
if !should_lint(cx, expr, cast_from, cast_to, msrv) {
if !should_lint(cx, cast_from, cast_to, msrv) {
return;
}
@ -70,9 +70,9 @@ pub(super) fn check(
);
}
fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
if in_constant(cx, expr.hir_id) {
if is_in_const_context(cx) {
return false;
}

View File

@ -1,6 +1,6 @@
use super::CAST_NAN_TO_INT;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_note;
use rustc_hir::Expr;
use rustc_lint::LateContext;
@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
}
fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
match constant(cx, cx.typeck_results(), e) {
match ConstEvalCtxt::new(cx).eval(e) {
// FIXME(f16_f128): add these types when nan checks are available on all platforms
Some(Constant::F64(n)) => n.is_nan(),
Some(Constant::F32(n)) => n.is_nan(),

View File

@ -1,4 +1,4 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::expr_or_init;
use clippy_utils::source::snippet;
@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType;
use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) {
if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) {
Some(c)
} else {
None

View File

@ -1,7 +1,7 @@
use std::convert::Infallible;
use std::ops::ControlFlow;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
use clippy_utils::{method_chain_args, sext};
@ -88,7 +88,7 @@ fn get_const_signed_int_eval<'cx>(
) -> Option<i128> {
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)?
&& let ty::Int(ity) = *ty.kind()
{
return Some(sext(cx.tcx, n, ity));
@ -103,7 +103,7 @@ fn get_const_unsigned_int_eval<'cx>(
) -> Option<u128> {
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)?
&& let ty::Uint(_ity) = *ty.kind()
{
return Some(n);

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
@ -14,21 +14,24 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
_ => { /* continue to checks */ },
}
match cast_from.kind() {
ty::FnDef(..) | ty::FnPtr(_) => {
let mut applicability = Applicability::MaybeIncorrect;
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() {
let mut applicability = Applicability::MaybeIncorrect;
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
span_lint_and_sugg(
cx,
FN_TO_NUMERIC_CAST_ANY,
expr.span,
format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
"did you mean to invoke the function?",
format!("{from_snippet}() as {cast_to}"),
applicability,
);
},
_ => {},
span_lint_and_then(
cx,
FN_TO_NUMERIC_CAST_ANY,
expr.span,
format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
|diag| {
diag.span_suggestion_with_style(
expr.span,
"did you mean to invoke the function?",
format!("{from_snippet}() as {cast_to}"),
applicability,
SuggestionStyle::ShowAlways,
);
},
);
}
}

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::{in_constant, is_integer_literal, std_or_core};
use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
@ -10,7 +10,7 @@ use super::ZERO_PTR;
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
if let TyKind::Ptr(ref mut_ty) = to.kind
&& is_integer_literal(from, 0)
&& !in_constant(cx, from.hir_id)
&& !is_in_const_context(cx)
&& let Some(std_or_core) = std_or_core(cx)
{
let (msg, sugg_fn) = match mut_ty.mutbl {

View File

@ -4,7 +4,7 @@ use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
_ => return,
}
&& !in_external_macro(cx.sess(), item.span)
&& !in_constant(cx, item.hir_id)
&& !is_in_const_context(cx)
&& self.msrv.meets(msrvs::TRY_FROM)
&& let Some(cv) = match op2 {
// todo: check for case signed -> larger unsigned == only x >= 0

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::implements_trait;
use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq};
use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
return;
}
if in_constant(cx, expr.hir_id) {
if is_in_const_context(cx) {
return;
}

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -39,14 +39,24 @@ impl LateLintPass<'_> for CreateDir {
&& let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
{
span_lint_and_sugg(
span_lint_and_then(
cx,
CREATE_DIR,
expr.span,
"calling `std::fs::create_dir` where there may be a better way",
"consider calling `std::fs::create_dir_all` instead",
format!("create_dir_all({})", snippet(cx, arg.span, "..")),
Applicability::MaybeIncorrect,
|diag| {
let mut app = Applicability::MaybeIncorrect;
diag.span_suggestion_with_style(
expr.span,
"consider calling `std::fs::create_dir_all` instead",
format!(
"create_dir_all({})",
snippet_with_applicability(cx, arg.span, "..", &mut app)
),
app,
SuggestionStyle::ShowAlways,
);
},
);
}
}

View File

@ -1,5 +1,5 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_in_test;
use clippy_utils::macros::{macro_backtrace, MacroCall};
use clippy_utils::source::snippet_with_applicability;
@ -65,61 +65,67 @@ impl LateLintPass<'_> for DbgMacro {
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
!(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id))
{
let mut applicability = Applicability::MachineApplicable;
let (sugg_span, suggestion) = match expr.peel_drop_temps().kind {
// dbg!()
ExprKind::Block(..) => {
// If the `dbg!` macro is a "free" statement and not contained within other expressions,
// remove the whole statement.
if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
&& let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
{
(macro_call.span.to(semi_span), String::new())
} else {
(macro_call.span, String::from("()"))
}
},
// dbg!(1)
ExprKind::Match(val, ..) => (
macro_call.span,
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(),
),
// dbg!(2, 3)
ExprKind::Tup(
[
Expr {
kind: ExprKind::Match(first, ..),
..
},
..,
Expr {
kind: ExprKind::Match(last, ..),
..
},
],
) => {
let snippet = snippet_with_applicability(
cx,
first.span.source_callsite().to(last.span.source_callsite()),
"..",
&mut applicability,
);
(macro_call.span, format!("({snippet})"))
},
_ => return,
};
self.prev_ctxt = cur_syntax_ctxt;
span_lint_and_sugg(
span_lint_and_then(
cx,
DBG_MACRO,
sugg_span,
macro_call.span,
"the `dbg!` macro is intended as a debugging tool",
"remove the invocation before committing it to a version control system",
suggestion,
applicability,
|diag| {
let mut applicability = Applicability::MachineApplicable;
let (sugg_span, suggestion) = match expr.peel_drop_temps().kind {
// dbg!()
ExprKind::Block(..) => {
// If the `dbg!` macro is a "free" statement and not contained within other expressions,
// remove the whole statement.
if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
&& let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
{
(macro_call.span.to(semi_span), String::new())
} else {
(macro_call.span, String::from("()"))
}
},
// dbg!(1)
ExprKind::Match(val, ..) => (
macro_call.span,
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
.to_string(),
),
// dbg!(2, 3)
ExprKind::Tup(
[
Expr {
kind: ExprKind::Match(first, ..),
..
},
..,
Expr {
kind: ExprKind::Match(last, ..),
..
},
],
) => {
let snippet = snippet_with_applicability(
cx,
first.span.source_callsite().to(last.span.source_callsite()),
"..",
&mut applicability,
);
(macro_call.span, format!("({snippet})"))
},
_ => unreachable!(),
};
diag.span_suggestion(
sugg_span,
"remove the invocation before committing it to a version control system",
suggestion,
applicability,
);
},
);
}
}

View File

@ -14,8 +14,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
#[cfg(feature = "internal")]
crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO,
@ -741,6 +739,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::unused_async::UNUSED_ASYNC_INFO,
crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO,
crate::unused_peekable::UNUSED_PEEKABLE_INFO,
crate::unused_result_ok::UNUSED_RESULT_OK_INFO,
crate::unused_rounding::UNUSED_ROUNDING_INFO,
crate::unused_self::UNUSED_SELF_INFO,
crate::unused_unit::UNUSED_UNIT_INFO,

View File

@ -221,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ");
format!("{adt_def_ty_name}::<{}>", &tys_str)
format!("{adt_def_ty_name}::<{tys_str}>")
} else {
binding_type.to_string()
};

View File

@ -92,20 +92,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
let (suffix, is_float) = match lit_ty.kind() {
ty::Int(IntTy::I32) => ("i32", false),
ty::Float(FloatTy::F64) => ("f64", true),
// Default numeric fallback never results in other types.
_ => return,
};
let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
src
} else {
match lit.node {
LitKind::Int(src, _) => format!("{src}"),
LitKind::Float(src, _) => format!("{src}"),
_ => return,
}
};
let sugg = numeric_literal::format(&src, Some(suffix), is_float);
span_lint_hir_and_then(
self.cx,
DEFAULT_NUMERIC_FALLBACK,
@ -113,6 +101,17 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
lit.span,
"default numeric fallback might occur",
|diag| {
let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
src
} else {
match lit.node {
LitKind::Int(src, _) => format!("{src}"),
LitKind::Float(src, _) => format!("{src}"),
_ => unreachable!("Default numeric fallback never results in other types"),
}
};
let sugg = numeric_literal::format(&src, Some(suffix), is_float);
diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
},
);

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir::{HirId, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
@ -56,16 +56,18 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
&& is_union_with_two_non_zst_fields(cx, item)
&& !has_c_repr_attr(cx, item.hir_id())
{
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
DEFAULT_UNION_REPRESENTATION,
item.span,
"this union has the default representation",
None,
format!(
"consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout",
cx.tcx.def_path_str(item.owner_id)
),
|diag| {
diag.help(format!(
"consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout",
cx.tcx.def_path_str(item.owner_id)
));
},
);
}
}

View File

@ -1,243 +1,181 @@
// NOTE: Entries should be created with `cargo dev deprecate`
// This file is managed by `cargo dev rename_lint` and `cargo dev deprecate_lint`.
// Prefer to use those when possible.
/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This
/// enables the simple extraction of the metadata without changing the current deprecation
/// declaration.
pub struct ClippyDeprecatedLint {
#[allow(dead_code)]
pub desc: &'static str,
macro_rules! declare_with_version {
($name:ident($name_version:ident): &[$ty:ty] = &[$(
#[clippy::version = $version:literal]
$e:expr,
)*]) => {
pub static $name: &[$ty] = &[$($e),*];
#[allow(unused)]
pub static $name_version: &[&str] = &[$($version),*];
};
}
#[macro_export]
macro_rules! declare_deprecated_lint {
{ $(#[$attr:meta])* pub $name: ident, $reason: literal} => {
$(#[$attr])*
#[allow(dead_code)]
pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {
desc: $reason
};
}
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This used to check for `assert!(a == b)` and recommend
/// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
#[rustfmt::skip]
declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[
#[clippy::version = "pre 1.29.0"]
pub SHOULD_ASSERT_EQ,
"`assert!()` will be more flexible with RFC 2011"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This used to check for `Vec::extend`, which was slower than
/// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"),
#[clippy::version = "pre 1.29.0"]
pub EXTEND_FROM_SLICE,
"`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// `Range::step_by(0)` used to be linted since it's
/// an infinite iterator, which is better expressed by `iter::repeat`,
/// but the method has been removed for `Iterator::step_by` which panics
/// if given a zero
("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"),
#[clippy::version = "pre 1.29.0"]
pub RANGE_STEP_BY_ZERO,
"`iterator.step_by(0)` panics nowadays"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This used to check for `Vec::as_slice`, which was unstable with good
/// stable alternatives. `Vec::as_slice` has now been stabilized.
("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"),
#[clippy::version = "pre 1.29.0"]
pub UNSTABLE_AS_SLICE,
"`Vec::as_slice` has been stabilized in 1.7"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This used to check for `Vec::as_mut_slice`, which was unstable with good
/// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"),
#[clippy::version = "pre 1.29.0"]
pub UNSTABLE_AS_MUT_SLICE,
"`Vec::as_mut_slice` has been stabilized in 1.7"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint should never have applied to non-pointer types, as transmuting
/// between non-pointer types of differing alignment is well-defined behavior (it's semantically
/// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
/// cast_ptr_alignment and transmute_ptr_to_ptr.
("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"),
#[clippy::version = "pre 1.29.0"]
pub MISALIGNED_TRANSMUTE,
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint is too subjective, not having a good reason for being in clippy.
/// Additionally, compound assignment operators may be overloaded separately from their non-assigning
/// counterparts, so this lint may suggest a change in behavior or the code may not compile.
("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"),
#[clippy::version = "1.30.0"]
pub ASSIGN_OPS,
"using compound assignment operators (e.g., `+=`) is harmless"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// The original rule will only lint for `if let`. After
/// making it support to lint `match`, naming as `if let` is not suitable for it.
/// So, this lint is deprecated.
("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"),
#[clippy::version = "pre 1.29.0"]
pub IF_LET_REDUNDANT_PATTERN_MATCHING,
"this lint has been changed to redundant_pattern_matching"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint used to suggest replacing `let mut vec =
/// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
/// replacement has very different performance characteristics so the lint is
/// deprecated.
#[clippy::version = "pre 1.29.0"]
pub UNSAFE_VECTOR_INITIALIZATION,
"the replacement suggested by this lint had substantially different behavior"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint has been superseded by #[must_use] in rustc.
("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"),
#[clippy::version = "1.39.0"]
pub UNUSED_COLLECT,
"`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// Associated-constants are now preferred.
("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"),
#[clippy::version = "1.44.0"]
pub REPLACE_CONSTS,
"associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// The regex! macro does not exist anymore.
("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"),
#[clippy::version = "1.47.0"]
pub REGEX_MACRO,
"the regex! macro has been removed from the regex crate in 2018"
}
("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"),
#[clippy::version = "1.54.0"]
("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"),
#[clippy::version = "1.54.0"]
("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"),
// end deprecated lints. used by `cargo dev deprecate_lint`
]}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint has been replaced by `manual_find_map`, a
/// more specific lint.
#[rustfmt::skip]
declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[
#[clippy::version = ""]
("clippy::almost_complete_letter_range", "clippy::almost_complete_range"),
#[clippy::version = ""]
("clippy::blacklisted_name", "clippy::disallowed_names"),
#[clippy::version = ""]
("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"),
#[clippy::version = ""]
("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"),
#[clippy::version = ""]
("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"),
#[clippy::version = ""]
("clippy::box_vec", "clippy::box_collection"),
#[clippy::version = ""]
("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
#[clippy::version = ""]
("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
#[clippy::version = ""]
("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"),
#[clippy::version = ""]
("clippy::disallowed_method", "clippy::disallowed_methods"),
#[clippy::version = ""]
("clippy::disallowed_type", "clippy::disallowed_types"),
#[clippy::version = ""]
("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"),
#[clippy::version = "1.51.0"]
pub FIND_MAP,
"this lint has been replaced by `manual_find_map`, a more specific lint"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint has been replaced by `manual_filter_map`, a
/// more specific lint.
("clippy::find_map", "clippy::manual_find_map"),
#[clippy::version = "1.53.0"]
pub FILTER_MAP,
"this lint has been replaced by `manual_filter_map`, a more specific lint"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// The `avoid_breaking_exported_api` config option was added, which
/// enables the `enum_variant_names` lint for public items.
#[clippy::version = "1.54.0"]
pub PUB_ENUM_VARIANT_NAMES,
"set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// The `avoid_breaking_exported_api` config option was added, which
/// enables the `wrong_self_conversion` lint for public items.
#[clippy::version = "1.54.0"]
pub WRONG_PUB_SELF_CONVENTION,
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos.
///
/// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
("clippy::filter_map", "clippy::manual_filter_map"),
#[clippy::version = ""]
("clippy::identity_conversion", "clippy::useless_conversion"),
#[clippy::version = "pre 1.29.0"]
("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"),
#[clippy::version = ""]
("clippy::if_let_some_result", "clippy::match_result_ok"),
#[clippy::version = ""]
("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"),
#[clippy::version = ""]
("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"),
#[clippy::version = ""]
("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"),
#[clippy::version = ""]
("clippy::logic_bug", "clippy::overly_complex_bool_expr"),
#[clippy::version = ""]
("clippy::new_without_default_derive", "clippy::new_without_default"),
#[clippy::version = ""]
("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
#[clippy::version = ""]
("clippy::option_expect_used", "clippy::expect_used"),
#[clippy::version = ""]
("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
#[clippy::version = ""]
("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
#[clippy::version = ""]
("clippy::option_unwrap_used", "clippy::unwrap_used"),
#[clippy::version = ""]
("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"),
#[clippy::version = ""]
("clippy::ref_in_deref", "clippy::needless_borrow"),
#[clippy::version = ""]
("clippy::result_expect_used", "clippy::expect_used"),
#[clippy::version = ""]
("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
#[clippy::version = ""]
("clippy::result_unwrap_used", "clippy::unwrap_used"),
#[clippy::version = ""]
("clippy::single_char_push_str", "clippy::single_char_add_str"),
#[clippy::version = ""]
("clippy::stutter", "clippy::module_name_repetitions"),
#[clippy::version = ""]
("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"),
#[clippy::version = ""]
("clippy::to_string_in_display", "clippy::recursive_format_impl"),
#[clippy::version = ""]
("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"),
#[clippy::version = ""]
("clippy::zero_width_space", "clippy::invisible_characters"),
#[clippy::version = ""]
("clippy::cast_ref_to_mut", "invalid_reference_casting"),
#[clippy::version = ""]
("clippy::clone_double_ref", "suspicious_double_ref_op"),
#[clippy::version = ""]
("clippy::cmp_nan", "invalid_nan_comparisons"),
#[clippy::version = ""]
("clippy::drop_bounds", "drop_bounds"),
#[clippy::version = ""]
("clippy::drop_copy", "dropping_copy_types"),
#[clippy::version = ""]
("clippy::drop_ref", "dropping_references"),
#[clippy::version = ""]
("clippy::fn_null_check", "useless_ptr_null_checks"),
#[clippy::version = ""]
("clippy::for_loop_over_option", "for_loops_over_fallibles"),
#[clippy::version = ""]
("clippy::for_loop_over_result", "for_loops_over_fallibles"),
#[clippy::version = ""]
("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
#[clippy::version = ""]
("clippy::forget_copy", "forgetting_copy_types"),
#[clippy::version = ""]
("clippy::forget_ref", "forgetting_references"),
#[clippy::version = ""]
("clippy::into_iter_on_array", "array_into_iter"),
#[clippy::version = ""]
("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
#[clippy::version = ""]
("clippy::invalid_ref", "invalid_value"),
#[clippy::version = ""]
("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"),
#[clippy::version = ""]
("clippy::let_underscore_drop", "let_underscore_drop"),
#[clippy::version = "1.80.0"]
pub MAYBE_MISUSED_CFG,
"this lint has been replaced by `unexpected_cfgs`"
}
declare_deprecated_lint! {
/// ### What it does
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
/// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes.
///
/// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
("clippy::maybe_misused_cfg", "unexpected_cfgs"),
#[clippy::version = ""]
("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
#[clippy::version = "1.80.0"]
pub MISMATCHED_TARGET_OS,
"this lint has been replaced by `unexpected_cfgs`"
}
("clippy::mismatched_target_os", "unexpected_cfgs"),
#[clippy::version = ""]
("clippy::panic_params", "non_fmt_panics"),
#[clippy::version = ""]
("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
#[clippy::version = ""]
("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
#[clippy::version = ""]
("clippy::undropped_manually_drops", "undropped_manually_drops"),
#[clippy::version = ""]
("clippy::unknown_clippy_lints", "unknown_lints"),
#[clippy::version = ""]
("clippy::unused_label", "unused_labels"),
#[clippy::version = ""]
("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"),
#[clippy::version = ""]
("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"),
// end renamed lints. used by `cargo dev rename_lint`
]}

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs};
use clippy_utils::ty::{implements_trait, is_manually_drop};
use clippy_utils::{
expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy,
ExprUseNode,
@ -947,7 +947,7 @@ fn report<'tcx>(
let (expr_str, _expr_is_macro_call) =
snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
let ty = typeck.expr_ty(expr);
let (_, ref_count) = peel_mid_ty_refs(ty);
let (_, ref_count) = peel_middle_ty_refs(ty);
let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
// a deref call changing &T -> &U requires two deref operators the first time
// this occurs. One to remove the reference, a second to call the deref impl.

View File

@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::Visitable;
use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args};
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
use pulldown_cmark::Event::{
Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,
TaskListMarker, Text,
@ -768,7 +768,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
);
}
},
FootnoteReference(text) | Text(text) => {
Text(text) => {
paragraph_range.end = range.end;
let range_ = range.clone();
ticks_unbalanced |= text.contains('`')
@ -812,7 +812,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
}
text_to_check.push((text, range, code_level));
}
},
}
FootnoteReference(_) => {}
}
}
headers
@ -857,7 +858,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
"assert" | "assert_eq" | "assert_ne"
)
{
self.is_const = in_constant(self.cx, expr.hir_id);
self.is_const = self.cx.tcx.hir().is_inside_const_context(expr.hir_id);
self.panic_span = Some(macro_call.span);
}
}

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_must_use_func_call;
use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
@ -126,14 +126,14 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
},
_ => return,
};
span_lint_and_note(
cx,
lint,
expr.span,
msg,
note_span,
format!("argument has type `{arg_ty}`"),
);
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
let note = format!("argument has type `{arg_ty}`");
if let Some(span) = note_span {
diag.span_note(span, note);
} else {
diag.note(note);
}
});
}
}
}

View File

@ -1,6 +1,6 @@
//! Lint on if expressions with an else if, but without a final else branch.
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@ -54,13 +54,15 @@ impl EarlyLintPass for ElseIfWithoutElse {
&& let ExprKind::If(_, _, None) = els.kind
&& !in_external_macro(cx.sess(), item.span)
{
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
ELSE_IF_WITHOUT_ELSE,
els.span,
"`if` expression with an `else if`, but without a final `else`",
None,
"add an `else` block here",
|diag| {
diag.help("add an `else` block here");
},
);
}
}

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::peel_blocks;
use rustc_errors::Applicability;
use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
@ -50,15 +50,14 @@ impl LateLintPass<'_> for EmptyDrop {
&& block.stmts.is_empty()
&& block.expr.is_none()
{
span_lint_and_sugg(
cx,
EMPTY_DROP,
item.span,
"empty drop implementation",
"try removing this impl",
String::new(),
Applicability::MaybeIncorrect,
);
span_lint_and_then(cx, EMPTY_DROP, item.span, "empty drop implementation", |diag| {
diag.span_suggestion_hidden(
item.span,
"try removing this impl",
String::new(),
Applicability::MaybeIncorrect,
);
});
}
}
}

View File

@ -7,7 +7,6 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::Ty;
use rustc_session::declare_lint_pass;
use rustc_span::Symbol;
use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@ -141,52 +140,6 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix
_ => return,
};
let mut help = None;
'build_help: {
// all lints disallowed, don't give help here
if [&[lint], other_lints.as_slice()]
.concat()
.iter()
.all(|lint| !lint.allowed(cx, expr))
{
break 'build_help;
}
// ne_bytes and all other lints allowed
if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
help = Some(Cow::Borrowed("specify the desired endianness explicitly"));
break 'build_help;
}
// le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
// le_bytes is not
if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
help = Some(Cow::Borrowed("use the native endianness instead"));
break 'build_help;
}
let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
let len = allowed_lints.clone().count();
let mut help_str = "use ".to_owned();
for (i, lint) in allowed_lints.enumerate() {
let only_one = len == 1;
if !only_one {
help_str.push_str("either of ");
}
help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
if i != len && !only_one {
help_str.push_str("or ");
}
}
help = Some(Cow::Owned(help_str + "instead"));
}
span_lint_and_then(
cx,
lint.as_lint(),
@ -198,9 +151,47 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix
if prefix == Prefix::To { " method" } else { "" },
),
move |diag| {
if let Some(help) = help {
diag.help(help);
// all lints disallowed, don't give help here
if [&[lint], other_lints.as_slice()]
.concat()
.iter()
.all(|lint| !lint.allowed(cx, expr))
{
return;
}
// ne_bytes and all other lints allowed
if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
diag.help("specify the desired endianness explicitly");
return;
}
// le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
// le_bytes is not
if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
diag.help("use the native endianness instead");
return;
}
let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
let len = allowed_lints.clone().count();
let mut help_str = "use ".to_owned();
for (i, lint) in allowed_lints.enumerate() {
let only_one = len == 1;
if !only_one {
help_str.push_str("either of ");
}
help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
if i != len && !only_one {
help_str.push_str("or ");
}
}
help_str.push_str("instead");
diag.help(help_str);
},
);
}

View File

@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
.const_eval_poly(def_id.to_def_id())
.ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty));
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) {
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
if let ty::Adt(adt, _) = ty.kind() {
if adt.is_enum() {
ty = adt.repr().discr_type().to_ty(cx.tcx);

View File

@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{
self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt,
TypeVisitableExt, TypeckResults,
self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults,
};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym;
@ -74,159 +73,184 @@ declare_clippy_lint! {
declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
impl<'tcx> LateLintPass<'tcx> for EtaReduction {
#[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let body = if let ExprKind::Closure(c) = expr.kind
&& c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer))
&& matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_))
&& !expr.span.from_expansion()
{
cx.tcx.hir().body(c.body)
} else {
return;
};
if body.value.span.from_expansion() {
if body.params.is_empty() {
if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
REDUNDANT_CLOSURE,
expr.span,
"redundant closure",
"replace the closure with `Vec::new`",
"std::vec::Vec::new".into(),
Applicability::MachineApplicable,
);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind {
for arg in args {
check_clousure(cx, Some(receiver), arg);
}
// skip `foo(|| macro!())`
return;
}
let typeck = cx.typeck_results();
let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() {
closure_subs.as_closure()
} else {
return;
};
if is_adjusted(cx, body.value) {
return;
if let ExprKind::Call(func, args) = expr.kind {
check_clousure(cx, None, func);
for arg in args {
check_clousure(cx, None, arg);
}
}
}
}
match body.value.kind {
ExprKind::Call(callee, args)
if matches!(
callee.kind,
ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
) =>
#[allow(clippy::too_many_lines)]
fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) {
let body = if let ExprKind::Closure(c) = expr.kind
&& c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer))
&& matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_))
&& !expr.span.from_expansion()
{
cx.tcx.hir().body(c.body)
} else {
return;
};
if body.value.span.from_expansion() {
if body.params.is_empty() {
if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
REDUNDANT_CLOSURE,
expr.span,
"redundant closure",
"replace the closure with `Vec::new`",
"std::vec::Vec::new".into(),
Applicability::MachineApplicable,
);
}
}
// skip `foo(|| macro!())`
return;
}
if is_adjusted(cx, body.value) {
return;
}
let typeck = cx.typeck_results();
let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() {
closure_subs.as_closure()
} else {
return;
};
let closure_sig = cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder();
match body.value.kind {
ExprKind::Call(callee, args)
if matches!(
callee.kind,
ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
) =>
{
let callee_ty_raw = typeck.expr_ty(callee);
let callee_ty = callee_ty_raw.peel_refs();
if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
|| !check_inputs(typeck, body.params, None, args)
{
let callee_ty_raw = typeck.expr_ty(callee);
let callee_ty = callee_ty_raw.peel_refs();
if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
|| !check_inputs(typeck, body.params, None, args)
{
return;
}
let callee_ty_adjusted = typeck
.expr_adjustments(callee)
.last()
.map_or(callee_ty, |a| a.target.peel_refs());
return;
}
let callee_ty_adjusted = typeck
.expr_adjustments(callee)
.last()
.map_or(callee_ty, |a| a.target.peel_refs());
let sig = match callee_ty_adjusted.kind() {
ty::FnDef(def, _) => {
// Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
if cx.tcx.has_attr(*def, sym::track_caller) {
return;
}
let sig = match callee_ty_adjusted.kind() {
ty::FnDef(def, _) => {
// Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
if cx.tcx.has_attr(*def, sym::track_caller) {
return;
}
cx.tcx.fn_sig(def).skip_binder().skip_binder()
},
ty::FnPtr(sig) => sig.skip_binder(),
ty::Closure(_, subs) => cx
.tcx
.signature_unclosure(subs.as_closure().sig(), Safety::Safe)
.skip_binder(),
_ => {
if typeck.type_dependent_def_id(body.value.hir_id).is_some()
&& let subs = typeck.node_args(body.value.hir_id)
&& let output = typeck.expr_ty(body.value)
&& let ty::Tuple(tys) = *subs.type_at(1).kind()
{
cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust)
} else {
return;
}
},
};
if check_sig(cx, closure, sig)
&& let generic_args = typeck.node_args(callee.hir_id)
// Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not
// `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result
// in a type which is `'static`.
// For now ignore all callee types which reference a type parameter.
&& !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
{
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if path_to_local(callee).map_or(false, |l| {
// FIXME: Do we really need this `local_used_in` check?
// Isn't it checking something like... `callee(callee)`?
// If somehow this check is needed, add some test for it,
// 'cuz currently nothing changes after deleting this check.
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
}) {
match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait(
cx.param_env,
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
ty::PredicatePolarity::Positive,
) {
// Mutable closure is used after current expr; we cannot consume it.
Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
snippet = format!("&{snippet}");
},
_ => (),
}
cx.tcx.fn_sig(def).skip_binder().skip_binder()
},
ty::FnPtr(sig) => sig.skip_binder(),
ty::Closure(_, subs) => cx
.tcx
.signature_unclosure(subs.as_closure().sig(), Safety::Safe)
.skip_binder(),
_ => {
if typeck.type_dependent_def_id(body.value.hir_id).is_some()
&& let subs = typeck.node_args(body.value.hir_id)
&& let output = typeck.expr_ty(body.value)
&& let ty::Tuple(tys) = *subs.type_at(1).kind()
{
cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust)
} else {
return;
}
},
};
if let Some(outer) = outer_receiver
&& ty_has_static(sig.output())
&& let generic_args = typeck.node_args(outer.hir_id)
// HACK: Given a closure in `T.method(|| f())`, where `fn f() -> U where U: 'static`, `T.method(f)`
// will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when
// T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic.
// So we have a hack like this.
&& generic_args.len() > 0
{
return;
}
if check_sig(closure_sig, sig)
&& let generic_args = typeck.node_args(callee.hir_id)
// Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not
// `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result
// in a type which is `'static`.
// For now ignore all callee types which reference a type parameter.
&& !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
{
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if path_to_local(callee).map_or(false, |l| {
// FIXME: Do we really need this `local_used_in` check?
// Isn't it checking something like... `callee(callee)`?
// If somehow this check is needed, add some test for it,
// 'cuz currently nothing changes after deleting this check.
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
}) {
match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait(
cx.param_env,
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
ty::PredicatePolarity::Positive,
) {
// Mutable closure is used after current expr; we cannot consume it.
Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
snippet = format!("&{snippet}");
},
_ => (),
}
diag.span_suggestion(
expr.span,
"replace the closure with the function itself",
snippet,
Applicability::MachineApplicable,
);
}
});
}
},
ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
&& !cx.tcx.has_attr(method_def_id, sym::track_caller)
&& check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
{
span_lint_and_then(
cx,
REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
expr.span,
"redundant closure",
|diag| {
let args = typeck.node_args(body.value.hir_id);
let caller = self_.hir_id.owner.def_id;
let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args);
diag.span_suggestion(
expr.span,
"replace the closure with the method itself",
format!("{}::{}", type_name, path.ident.name),
Applicability::MachineApplicable,
);
},
);
}
},
_ => (),
}
diag.span_suggestion(
expr.span,
"replace the closure with the function itself",
snippet,
Applicability::MachineApplicable,
);
}
});
}
},
ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
&& !cx.tcx.has_attr(method_def_id, sym::track_caller)
&& check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
{
span_lint_and_then(
cx,
REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
expr.span,
"redundant closure",
|diag| {
let args = typeck.node_args(body.value.hir_id);
let caller = self_.hir_id.owner.def_id;
let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args);
diag.span_suggestion(
expr.span,
"replace the closure with the method itself",
format!("{}::{}", type_name, path.ident.name),
Applicability::MachineApplicable,
);
},
);
}
},
_ => (),
}
}
@ -251,12 +275,8 @@ fn check_inputs(
})
}
fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, call_sig: FnSig<'_>) -> bool {
call_sig.safety == Safety::Safe
&& !has_late_bound_to_non_late_bound_regions(
cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(),
call_sig,
)
fn check_sig<'tcx>(closure_sig: FnSig<'tcx>, call_sig: FnSig<'tcx>) -> bool {
call_sig.safety == Safety::Safe && !has_late_bound_to_non_late_bound_regions(closure_sig, call_sig)
}
/// This walks through both signatures and checks for any time a late-bound region is expected by an
@ -265,7 +285,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, c
/// This is needed because rustc is unable to late bind early-bound regions in a function signature.
fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool {
fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool {
matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..))
from_region.is_bound() && !to_region.is_bound()
}
fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool {
@ -318,3 +338,8 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'
.zip(to_sig.inputs_and_output)
.any(|(from_ty, to_ty)| check_ty(from_ty, to_ty))
}
fn ty_has_static(ty: Ty<'_>) -> bool {
ty.walk()
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if re.is_static()))
}

View File

@ -88,11 +88,11 @@ impl LateLintPass<'_> for ExhaustiveItems {
&& !attrs.iter().any(|a| a.has_name(sym::non_exhaustive))
&& fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public())
{
let suggestion_span = item.span.shrink_to_lo();
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
span_lint_and_then(cx, lint, item.span, msg, |diag| {
let suggestion_span = item.span.shrink_to_lo();
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
let sugg = format!("#[non_exhaustive]\n{indent}");
diag.span_suggestion(
diag.span_suggestion_verbose(
suggestion_span,
"try adding #[non_exhaustive]",
sugg,

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;
@ -62,13 +62,15 @@ impl EarlyLintPass for FieldScopedVisibilityModifiers {
// pub(self) is equivalent to not using pub at all, so we ignore it
continue;
}
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
FIELD_SCOPED_VISIBILITY_MODIFIERS,
field.vis.span,
"scoped visibility modifier on a field",
None,
"consider making the field private and adding a scoped visibility method for it",
|diag| {
diag.help("consider making the field private and adding a scoped visibility method for it");
},
);
}
}

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::numeric_literal;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_errors::Applicability;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, FloatTy};
@ -105,32 +105,43 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
if is_whole && !sym_str.contains(['e', 'E']) {
// Normalize the literal by stripping the fractional portion
if sym_str.split('.').next().unwrap() != float_str {
// If the type suffix is missing the suggestion would be
// incorrectly interpreted as an integer so adding a `.0`
// suffix to prevent that.
if type_suffix.is_none() {
float_str.push_str(".0");
}
span_lint_and_sugg(
span_lint_and_then(
cx,
LOSSY_FLOAT_LITERAL,
expr.span,
"literal cannot be represented as the underlying type without loss of precision",
"consider changing the type or replacing it with",
numeric_literal::format(&float_str, type_suffix, true),
Applicability::MachineApplicable,
|diag| {
// If the type suffix is missing the suggestion would be
// incorrectly interpreted as an integer so adding a `.0`
// suffix to prevent that.
if type_suffix.is_none() {
float_str.push_str(".0");
}
diag.span_suggestion_with_style(
expr.span,
"consider changing the type or replacing it with",
numeric_literal::format(&float_str, type_suffix, true),
Applicability::MachineApplicable,
SuggestionStyle::ShowAlways,
);
},
);
}
} else if digits > max as usize && float_str.len() < sym_str.len() {
span_lint_and_sugg(
span_lint_and_then(
cx,
EXCESSIVE_PRECISION,
expr.span,
"float has excessive precision",
"consider changing the type or truncating it to",
numeric_literal::format(&float_str, type_suffix, true),
Applicability::MachineApplicable,
|diag| {
diag.span_suggestion_with_style(
expr.span,
"consider changing the type or truncating it to",
numeric_literal::format(&float_str, type_suffix, true),
Applicability::MachineApplicable,
SuggestionStyle::ShowAlways,
);
},
);
}
}

View File

@ -1,9 +1,9 @@
use clippy_utils::consts::Constant::{Int, F32, F64};
use clippy_utils::consts::{constant, constant_simple, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{
eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal,
peel_blocks, sugg,
eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
numeric_literal, peel_blocks, sugg,
};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@ -112,7 +112,7 @@ declare_lint_pass!(FloatingPointArithmetic => [
// Returns the specialized log method for a given base if base is constant
// and is one of 2, 10 and e
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
if let Some(value) = constant(cx, cx.typeck_results(), base) {
if let Some(value) = ConstEvalCtxt::new(cx).eval(base) {
if F32(2.0) == value || F64(2.0) == value {
return Some("log2");
} else if F32(10.0) == value || F64(10.0) == value {
@ -182,10 +182,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
rhs,
) = receiver.kind
{
let recv = match (
constant(cx, cx.typeck_results(), lhs),
constant(cx, cx.typeck_results(), rhs),
) {
let ecx = ConstEvalCtxt::new(cx);
let recv = match (ecx.eval(lhs), ecx.eval(rhs)) {
(Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs,
(_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs,
_ => return,
@ -230,7 +228,7 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
// Check receiver
if let Some(value) = constant(cx, cx.typeck_results(), receiver) {
if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) {
if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
Some("exp")
} else if F32(2.0) == value || F64(2.0) == value {
@ -251,7 +249,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
}
// Check argument
if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) {
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
(
SUBOPTIMAL_FLOPS,
@ -291,7 +289,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
}
fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) {
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
if value == Int(2) {
if let Some(parent) = get_parent_expr(cx, expr) {
if let Some(grandparent) = get_parent_expr(cx, parent) {
@ -397,8 +395,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
) = &add_rhs.kind
&& lmethod_name.as_str() == "powi"
&& rmethod_name.as_str() == "powi"
&& let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1)
&& let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1)
&& let ecx = ConstEvalCtxt::new(cx)
&& let Some(lvalue) = ecx.eval(largs_1)
&& let Some(rvalue) = ecx.eval(rargs_1)
&& Int(2) == lvalue
&& Int(2) == rvalue
{
@ -438,7 +437,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
rhs,
) = expr.kind
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
&& let Some(value) = constant(cx, cx.typeck_results(), rhs)
&& let Some(value) = ConstEvalCtxt::new(cx).eval(rhs)
&& (F32(1.0) == value || F64(1.0) == value)
&& let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind
&& cx.typeck_results().expr_ty(self_arg).is_floating_point()
@ -552,7 +551,7 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
/// Returns true iff expr is some zero literal
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match constant_simple(cx, cx.typeck_results(), expr) {
match ConstEvalCtxt::new(cx).eval_simple(expr) {
Some(Int(i)) => i == 0,
Some(F32(f)) => f == 0.0,
Some(F64(f)) => f == 0.0,
@ -696,8 +695,9 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
mul_lhs,
mul_rhs,
) = &div_lhs.kind
&& let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs)
&& let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs)
&& let ecx = ConstEvalCtxt::new(cx)
&& let Some(rvalue) = ecx.eval(div_rhs)
&& let Some(lvalue) = ecx.eval(mul_rhs)
{
// TODO: also check for constant values near PI/180 or 180/PI
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)
@ -753,7 +753,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// All of these operations are currently not const and are in std.
if in_constant(cx, expr.hir_id) {
if is_in_const_context(cx) {
return;
}

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{higher, match_def_path, paths};
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
@ -81,13 +81,15 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
_ => return,
};
if is_format(cx, arg) {
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
FORMAT_PUSH_STRING,
expr.span,
"`format!(..)` appended to existing `String`",
None,
"consider using `write!` to avoid the extra allocation",
|diag| {
diag.help("consider using `write!` to avoid the extra allocation");
},
);
}
}

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::{in_constant, is_integer_literal};
use clippy_utils::{is_in_const_context, is_integer_literal};
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
@ -62,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
&& matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_))
// do not lint in constant context, because the suggestion won't work.
// NB: keep this check until a new `const_trait_impl` is available and stablized.
&& !in_constant(cx, exp.hir_id)
// NB: keep this check until a new `const_trait_impl` is available and stabilized.
&& !is_in_const_context(cx)
{
let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
let ty = cx.typeck_results().expr_ty(expr);

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{higher, SpanlessEq};
use clippy_utils::{eq_expr_value, higher};
use core::ops::ControlFlow;
use rustc_errors::Diag;
use rustc_hir::{Expr, ExprKind};
@ -51,53 +51,45 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
if_else: Some(if_else),
..
}) = higher::IfLet::hir(cx, expr)
&& let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None))
&& let Some(arm_mutex) =
for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex)))
{
let is_mutex_lock = |e: &'tcx Expr<'tcx>| {
if let Some(mutex) = is_mutex_lock_call(cx, e) {
ControlFlow::Break(mutex)
} else {
ControlFlow::Continue(())
}
let diag = |diag: &mut Diag<'_, ()>| {
diag.span_label(
op_mutex.span,
"this Mutex will remain locked for the entire `if let`-block...",
);
diag.span_label(
arm_mutex.span,
"... and is tried to lock again here, which will always deadlock.",
);
diag.help("move the lock call outside of the `if let ...` expression");
};
let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock);
if let Some(op_mutex) = op_mutex {
let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock);
if let Some(arm_mutex) = arm_mutex
&& SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)
{
let diag = |diag: &mut Diag<'_, ()>| {
diag.span_label(
op_mutex.span,
"this Mutex will remain locked for the entire `if let`-block...",
);
diag.span_label(
arm_mutex.span,
"... and is tried to lock again here, which will always deadlock.",
);
diag.help("move the lock call outside of the `if let ...` expression");
};
span_lint_and_then(
cx,
IF_LET_MUTEX,
expr.span,
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
diag,
);
}
}
span_lint_and_then(
cx,
IF_LET_MUTEX,
expr.span,
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
diag,
);
}
}
}
fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
fn mutex_lock_call<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
op_mutex: Option<&'tcx Expr<'_>>,
) -> ControlFlow<&'tcx Expr<'tcx>> {
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
&& path.ident.as_str() == "lock"
&& let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
&& is_type_diagnostic_item(cx, ty, sym::Mutex)
&& op_mutex.map_or(true, |op| eq_expr_value(cx, self_arg, op))
{
Some(self_arg)
ControlFlow::Break(self_arg)
} else {
None
ControlFlow::Continue(())
}
}

View File

@ -1,7 +1,7 @@
//! lint on if branches that could be swapped so no `!` operation is necessary
//! on the condition
use clippy_utils::consts::{constant_simple, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_else_clause;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
@ -49,7 +49,7 @@ declare_clippy_lint! {
declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
if let Some(value) = constant_simple(cx, cx.typeck_results(), expr) {
if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) {
return Constant::Int(0) == value;
}
false

View File

@ -1,10 +1,12 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
use clippy_utils::{
contains_return, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, path_res, peel_blocks,
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
@ -76,37 +78,44 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
&& !is_else_clause(cx.tcx, expr)
&& !in_constant(cx, expr.hir_id)
&& !is_in_const_context(cx)
&& !in_external_macro(cx.sess(), expr.span)
&& self.msrv.meets(msrvs::BOOL_THEN)
&& !contains_return(then_block.stmts)
{
let mut app = Applicability::Unspecified;
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
.maybe_par()
.to_string();
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
let mut method_body = if then_block.stmts.is_empty() {
arg_snip.into_owned()
} else {
format!("{{ /* snippet */ {arg_snip} }}")
};
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
"then_some"
} else {
method_body.insert_str(0, "|| ");
"then"
};
let help =
format!("consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",);
span_lint_and_help(
span_lint_and_then(
cx,
IF_THEN_SOME_ELSE_NONE,
expr.span,
format!("this could be simplified with `bool::{method_name}`"),
None,
help,
|diag| {
let mut app = Applicability::Unspecified;
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
.maybe_par()
.to_string();
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
let method_body = if let Some(first_stmt) = then_block.stmts.first() {
let (block_snippet, _) =
snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
let closure = if method_name == "then" { "|| " } else { "" };
format!("{closure} {{ {block_snippet}; {arg_snip} }}")
} else {
arg_snip.into_owned()
};
diag.span_suggestion(
expr.span,
"try",
format!("{cond_snip}.{method_name}({method_body})"),
app,
);
},
);
}
}

View File

@ -1,7 +1,7 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use rustc_errors::Diag;
use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym;
use rustc_span::Span;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt};
use clippy_utils::ty::is_type_diagnostic_item;
@ -77,33 +77,32 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
&generics_snip[1..generics_snip.len() - 1]
};
multispan_sugg(
diag,
"consider adding a type parameter",
vec![
(
generics_suggestion_span,
format!(
"<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
if generics_snip.is_empty() { "" } else { ", " },
if vis.suggestions.is_empty() {
""
} else {
// request users to add `Default` bound so that generic constructors can be used
" + Default"
},
),
let mut suggestions = vec![
(
generics_suggestion_span,
format!(
"<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
if generics_snip.is_empty() { "" } else { ", " },
if vis.suggestions.is_empty() {
""
} else {
// request users to add `Default` bound so that generic constructors can be used
" + Default"
},
),
(
target.span(),
format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
),
],
);
),
(
target.span(),
format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
),
];
suggestions.extend(vis.suggestions);
if !vis.suggestions.is_empty() {
multispan_sugg(diag, "...and use generic constructor", vis.suggestions);
}
diag.multipart_suggestion(
"add a type parameter for `BuildHasher`",
suggestions,
Applicability::MaybeIncorrect,
);
}
if !cx.effective_visibilities.is_exported(item.owner_id.def_id) {

View File

@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -45,8 +45,6 @@ declare_clippy_lint! {
declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, span, "..", &mut app);
span_lint_hir_and_then(
cx,
IMPLICIT_RETURN,
@ -54,14 +52,20 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
span,
"missing `return` statement",
|diag| {
diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app);
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, span, "..", &mut app);
diag.span_suggestion_with_style(
span,
"add `return` as shown",
format!("return {snip}"),
app,
SuggestionStyle::ShowAlways,
);
},
);
}
fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) {
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
span_lint_hir_and_then(
cx,
IMPLICIT_RETURN,
@ -69,11 +73,14 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp
break_span,
"missing `return` statement",
|diag| {
diag.span_suggestion(
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
diag.span_suggestion_with_style(
break_span,
"change `break` to `return` as shown",
format!("return {snip}"),
app,
SuggestionStyle::ShowAlways,
);
},
);

View File

@ -1,4 +1,4 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet_with_context;
@ -117,11 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option<u128> {
fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> {
if let ExprKind::Binary(op, l, r) = expr.kind {
let tr = cx.typeck_results();
if let Some(Constant::Int(c)) = constant(cx, tr, r) {
let ecx = ConstEvalCtxt::new(cx);
if let Some(Constant::Int(c)) = ecx.eval(r) {
return Some((c, op.node, l));
};
if let Some(Constant::Int(c)) = constant(cx, tr, l) {
if let Some(Constant::Int(c)) = ecx.eval(l) {
return Some((c, invert_op(op.node)?, r));
}
}

View File

@ -55,7 +55,6 @@ impl IncompatibleMsrv {
}
}
#[allow(clippy::cast_lossless)]
fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion {
if let Some(version) = self.is_above_msrv.get(&def_id) {
return *version;
@ -67,9 +66,9 @@ impl IncompatibleMsrv {
since: StableSince::Version(version),
..
} => Some(RustcVersion::new(
version.major as _,
version.minor as _,
version.patch as _,
version.major.into(),
version.minor.into(),
version.patch.into(),
)),
_ => None,
}) {

View File

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLet;
use clippy_utils::ty::is_copy;
@ -246,7 +246,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
&& let parent_id = cx.tcx.parent_hir_id(expr.hir_id)
&& let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id)
&& let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind
&& let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr)
&& let Some(Constant::Int(index_value)) = ConstEvalCtxt::new(cx).eval(index_expr)
&& let Ok(index_value) = index_value.try_into()
&& index_value < max_suggested_slice

View File

@ -1,7 +1,7 @@
//! lint on indexing and slicing operations
use clippy_config::Conf;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
use clippy_utils::{higher, is_from_proc_macro};
@ -70,8 +70,6 @@ declare_clippy_lint! {
///
/// Use instead:
/// ```no_run
/// # #![allow(unused)]
///
/// # let x = vec![0; 5];
/// # let y = [0, 1, 2, 3];
/// x.get(2);
@ -179,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
return;
}
// Index is a constant uint.
if let Some(constant) = constant(cx, cx.typeck_results(), index) {
if let Some(constant) = ConstEvalCtxt::new(cx).eval(index) {
// only `usize` index is legal in rust array index
// leave other type to rustc
if let Constant::Int(off) = constant
@ -217,14 +215,15 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
/// Returns a tuple of options with the start and end (exclusive) values of
/// the range. If the start or end is not constant, None is returned.
fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) {
let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr));
let ecx = ConstEvalCtxt::new(cx);
let s = range.start.map(|expr| ecx.eval(expr));
let start = match s {
Some(Some(Constant::Int(x))) => Some(x),
Some(_) => None,
None => Some(0),
};
let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr));
let e = range.end.map(|expr| ecx.eval(expr));
let end = match e {
Some(Some(Constant::Int(x))) => {
if range.limits == RangeLimits::Closed {

View File

@ -41,7 +41,6 @@ declare_clippy_lint! {
/// ### Example
/// ```no_run
/// let infinite_iter = 0..;
/// # #[allow(unused)]
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
/// ```
#[clippy::version = "pre 1.29.0"]

View File

@ -1,6 +1,6 @@
//! lint on inherent implementations
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_lint_allowed;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::LocalDefId;
@ -105,13 +105,14 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
// `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first.
lint_spans.sort_by_key(|x| x.0.lo());
for (span, first_span) in lint_spans {
span_lint_and_note(
span_lint_and_then(
cx,
MULTIPLE_INHERENT_IMPL,
span,
"multiple implementations of this structure",
Some(first_span),
"first implementation here",
|diag| {
diag.span_note(first_span, "first implementation here");
},
);
}
}

View File

@ -7,7 +7,7 @@ use rustc_span::Span;
use clippy_utils::comparisons;
use clippy_utils::comparisons::Rel;
use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::consts::{ConstEvalCtxt, FullInt};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::snippet;
@ -95,7 +95,7 @@ fn upcast_comparison_bounds_err<'tcx>(
invert: bool,
) {
if let Some((lb, ub)) = lhs_bounds {
if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) {
if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) {
if rel == Rel::Eq || rel == Rel::Ne {
if norm_rhs_val < lb || norm_rhs_val > ub {
err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);

View File

@ -1,5 +1,5 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call_first_node;
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
@ -66,16 +66,18 @@ impl LateLintPass<'_> for LargeIncludeFile {
&& (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
{
span_lint_and_note(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LARGE_INCLUDE_FILE,
expr.span.source_callsite(),
"attempted to include a large file",
None,
format!(
"the configuration allows a maximum size of {} bytes",
self.max_file_size
),
|diag| {
diag.note(format!(
"the configuration allows a maximum size of {} bytes",
self.max_file_size
));
},
);
}
}

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
use rustc_hir::{LetStmt, LocalSource, PatKind};
@ -149,43 +149,53 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
});
if contains_sync_guard {
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_LOCK,
local.span,
"non-binding `let` on a synchronization lock",
None,
"consider using an underscore-prefixed named \
binding or dropping explicitly with `std::mem::drop`",
|diag| {
diag.help(
"consider using an underscore-prefixed named \
binding or dropping explicitly with `std::mem::drop`",
);
},
);
} else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[])
{
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_FUTURE,
local.span,
"non-binding `let` on a future",
None,
"consider awaiting the future or dropping explicitly with `std::mem::drop`",
|diag| {
diag.help("consider awaiting the future or dropping explicitly with `std::mem::drop`");
},
);
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_MUST_USE,
local.span,
"non-binding `let` on an expression with `#[must_use]` type",
None,
"consider explicitly using expression value",
|diag| {
diag.help("consider explicitly using expression value");
},
);
} else if is_must_use_func_call(cx, init) {
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_MUST_USE,
local.span,
"non-binding `let` on a result of a `#[must_use]` function",
None,
"consider explicitly using function result",
|diag| {
diag.help("consider explicitly using function result");
},
);
}
@ -204,18 +214,22 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
return;
}
span_lint_and_help(
span_lint_and_then(
cx,
LET_UNDERSCORE_UNTYPED,
local.span,
"non-binding `let` without a type annotation",
Some(Span::new(
local.pat.span.hi(),
local.pat.span.hi() + BytePos(1),
local.pat.span.ctxt(),
local.pat.span.parent(),
)),
"consider adding a type annotation",
|diag| {
diag.span_help(
Span::new(
local.pat.span.hi(),
local.pat.span.hi() + BytePos(1),
local.pat.span.ctxt(),
local.pat.span.parent(),
),
"consider adding a type annotation",
);
},
);
}
}

View File

@ -1,78 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
{
store.register_removed(
"clippy::should_assert_eq",
"`assert!()` will be more flexible with RFC 2011",
);
store.register_removed(
"clippy::extend_from_slice",
"`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
);
store.register_removed(
"clippy::range_step_by_zero",
"`iterator.step_by(0)` panics nowadays",
);
store.register_removed(
"clippy::unstable_as_slice",
"`Vec::as_slice` has been stabilized in 1.7",
);
store.register_removed(
"clippy::unstable_as_mut_slice",
"`Vec::as_mut_slice` has been stabilized in 1.7",
);
store.register_removed(
"clippy::misaligned_transmute",
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
);
store.register_removed(
"clippy::assign_ops",
"using compound assignment operators (e.g., `+=`) is harmless",
);
store.register_removed(
"clippy::if_let_redundant_pattern_matching",
"this lint has been changed to redundant_pattern_matching",
);
store.register_removed(
"clippy::unsafe_vector_initialization",
"the replacement suggested by this lint had substantially different behavior",
);
store.register_removed(
"clippy::unused_collect",
"`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
);
store.register_removed(
"clippy::replace_consts",
"associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
);
store.register_removed(
"clippy::regex_macro",
"the regex! macro has been removed from the regex crate in 2018",
);
store.register_removed(
"clippy::find_map",
"this lint has been replaced by `manual_find_map`, a more specific lint",
);
store.register_removed(
"clippy::filter_map",
"this lint has been replaced by `manual_filter_map`, a more specific lint",
);
store.register_removed(
"clippy::pub_enum_variant_names",
"set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
);
store.register_removed(
"clippy::wrong_pub_self_convention",
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
);
store.register_removed(
"clippy::maybe_misused_cfg",
"this lint has been replaced by `unexpected_cfgs`",
);
store.register_removed(
"clippy::mismatched_target_os",
"this lint has been replaced by `unexpected_cfgs`",
);
}

View File

@ -13,7 +13,6 @@
#![feature(stmt_expr_attributes)]
#![feature(unwrap_infallible)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(
clippy::missing_docs_in_private_items,
clippy::must_use_candidate,
@ -64,13 +63,11 @@ extern crate clippy_utils;
#[macro_use]
extern crate declare_clippy_lint;
#[cfg(feature = "internal")]
pub mod deprecated_lints;
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils;
mod declared_lints;
mod renamed_lints;
mod deprecated_lints;
// begin lints modules, do not remove this comment, its used in `update_lints`
mod absolute_paths;
@ -372,6 +369,7 @@ mod unsafe_removed_from_name;
mod unused_async;
mod unused_io_amount;
mod unused_peekable;
mod unused_result_ok;
mod unused_rounding;
mod unused_self;
mod unused_unit;
@ -495,7 +493,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 {
@ -531,10 +529,14 @@ fn register_categories(store: &mut rustc_lint::LintStore) {
/// Used in `./src/driver.rs`.
#[expect(clippy::too_many_lines)]
pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
register_removed_non_tool_lints(store);
register_categories(store);
include!("lib.deprecated.rs");
for (old_name, new_name) in deprecated_lints::RENAMED {
store.register_renamed(old_name, new_name);
}
for (name, reason) in deprecated_lints::DEPRECATED {
store.register_removed(name, reason);
}
#[cfg(feature = "internal")]
{
@ -669,6 +671,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf)));
store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk));
store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
@ -818,7 +821,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf)));
store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate));
store.register_late_pass(move |_| Box::new(operators::Operators::new(conf)));
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf)));
store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf)));
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf)));
@ -907,68 +910,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf)));
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf)));
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains));
store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
// add lints here, do not remove this comment, it's used in `new_lint`
}
#[rustfmt::skip]
fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
store.register_removed(
"should_assert_eq",
"`assert!()` will be more flexible with RFC 2011",
);
store.register_removed(
"extend_from_slice",
"`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
);
store.register_removed(
"range_step_by_zero",
"`iterator.step_by(0)` panics nowadays",
);
store.register_removed(
"unstable_as_slice",
"`Vec::as_slice` has been stabilized in 1.7",
);
store.register_removed(
"unstable_as_mut_slice",
"`Vec::as_mut_slice` has been stabilized in 1.7",
);
store.register_removed(
"misaligned_transmute",
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
);
store.register_removed(
"assign_ops",
"using compound assignment operators (e.g., `+=`) is harmless",
);
store.register_removed(
"if_let_redundant_pattern_matching",
"this lint has been changed to redundant_pattern_matching",
);
store.register_removed(
"unsafe_vector_initialization",
"the replacement suggested by this lint had substantially different behavior",
);
store.register_removed(
"reverse_range_loop",
"this lint is now included in reversed_empty_ranges",
);
}
/// Register renamed lints.
///
/// Used in `./src/driver.rs`.
pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
ls.register_renamed(old_name, new_name);
}
}
// only exists to let the dogfood integration test works.
// Don't run clippy as an executable directly
#[allow(dead_code)]
fn main() {
panic!("Please use the cargo-clippy executable");
}

View File

@ -2,13 +2,13 @@
//! floating-point literal expressions.
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::numeric_literal::{NumericLiteral, Radix};
use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{Expr, ExprKind, LitKind};
use rustc_ast::token;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass;
use rustc_span::Span;
@ -159,63 +159,39 @@ enum WarningType {
}
impl WarningType {
fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: Span) {
fn lint_and_text(&self) -> (&'static Lint, &'static str, &'static str) {
match self {
Self::MistypedLiteralSuffix => span_lint_and_sugg(
cx,
Self::MistypedLiteralSuffix => (
MISTYPED_LITERAL_SUFFIXES,
span,
"mistyped literal suffix",
"did you mean to write",
suggested_format,
Applicability::MaybeIncorrect,
),
Self::UnreadableLiteral => span_lint_and_sugg(
cx,
UNREADABLE_LITERAL,
span,
"long literal lacking separators",
"consider",
suggested_format,
Applicability::MachineApplicable,
),
Self::LargeDigitGroups => span_lint_and_sugg(
cx,
LARGE_DIGIT_GROUPS,
span,
"digit groups should be smaller",
"consider",
suggested_format,
Applicability::MachineApplicable,
),
Self::InconsistentDigitGrouping => span_lint_and_sugg(
cx,
Self::UnreadableLiteral => (UNREADABLE_LITERAL, "long literal lacking separators", "consider"),
Self::LargeDigitGroups => (LARGE_DIGIT_GROUPS, "digit groups should be smaller", "consider"),
Self::InconsistentDigitGrouping => (
INCONSISTENT_DIGIT_GROUPING,
span,
"digits grouped inconsistently by underscores",
"consider",
suggested_format,
Applicability::MachineApplicable,
),
Self::DecimalRepresentation => span_lint_and_sugg(
cx,
Self::DecimalRepresentation => (
DECIMAL_LITERAL_REPRESENTATION,
span,
"integer literal has a better hexadecimal representation",
"consider",
suggested_format,
Applicability::MachineApplicable,
),
Self::UnusualByteGroupings => span_lint_and_sugg(
cx,
Self::UnusualByteGroupings => (
UNUSUAL_BYTE_GROUPINGS,
span,
"digits of hex, binary or octal literal not in groups of equal size",
"consider",
suggested_format,
Applicability::MachineApplicable,
),
};
}
}
fn display(&self, num_lit: &NumericLiteral<'_>, cx: &EarlyContext<'_>, span: Span) {
let (lint, message, try_msg) = self.lint_and_text();
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(cx, lint, span, message, |diag| {
diag.span_suggestion(span, try_msg, num_lit.format(), Applicability::MaybeIncorrect);
});
}
}
@ -293,7 +269,7 @@ impl LiteralDigitGrouping {
WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true,
};
if should_warn {
warning_type.display(num_lit.format(), cx, span);
warning_type.display(&num_lit, cx, span);
}
}
}
@ -346,11 +322,14 @@ impl LiteralDigitGrouping {
}
}
*part = main_part;
let mut sugg = num_lit.format();
sugg.push('_');
sugg.push(missing_char);
sugg.push_str(last_group);
WarningType::MistypedLiteralSuffix.display(sugg, cx, span);
let (lint, message, try_msg) = WarningType::MistypedLiteralSuffix.lint_and_text();
span_lint_and_then(cx, lint, span, message, |diag| {
let mut sugg = num_lit.format();
sugg.push('_');
sugg.push(missing_char);
sugg.push_str(last_group);
diag.span_suggestion(span, try_msg, sugg, Applicability::MaybeIncorrect);
});
false
} else {
true
@ -471,7 +450,7 @@ impl DecimalLiteralRepresentation {
let hex = format!("{val:#X}");
let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| {
warning_type.display(num_lit.format(), cx, span);
warning_type.display(&num_lit, cx, span);
});
}
}

View File

@ -2,6 +2,7 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_block, is_integer_const};
use rustc_ast::Label;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr};
use rustc_hir::{Expr, Pat};
@ -17,6 +18,7 @@ pub(super) fn check<'tcx>(
arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
label: Option<Label>,
) {
// Look for variables that are incremented once per loop iteration.
let mut increment_visitor = IncrementVisitor::new(cx);
@ -34,7 +36,7 @@ pub(super) fn check<'tcx>(
{
let mut applicability = Applicability::MaybeIncorrect;
let span = expr.span.with_hi(arg.span.hi());
let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name));
let int_name = match ty.map(Ty::kind) {
// usize or inferred
Some(ty::Uint(UintTy::Usize)) | None => {
@ -45,7 +47,7 @@ pub(super) fn check<'tcx>(
format!("the variable `{name}` is used as a loop counter"),
"consider using",
format!(
"for ({name}, {}) in {}.enumerate()",
"{loop_label}for ({name}, {}) in {}.enumerate()",
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
make_iterator_snippet(cx, arg, &mut applicability),
),
@ -68,7 +70,7 @@ pub(super) fn check<'tcx>(
span,
"consider using",
format!(
"for ({name}, {}) in (0_{int_name}..).zip({})",
"{loop_label}for ({name}, {}) in (0_{int_name}..).zip({})",
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
make_iterator_snippet(cx, arg, &mut applicability),
),

View File

@ -1,8 +1,9 @@
use super::FOR_KV_MAP;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{pat_is_wild, sugg};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
@ -40,13 +41,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx
format!("you seem to want to iterate on a map's {kind}s"),
|diag| {
let map = sugg::Sugg::hir(cx, arg, "map");
multispan_sugg(
diag,
diag.multipart_suggestion(
"use the corresponding method",
vec![
(pat_span, snippet(cx, new_pat_span, kind).into_owned()),
(arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())),
],
Applicability::MachineApplicable,
);
},
);

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::{match_def_path, paths, SpanlessEq};
use rustc_errors::Applicability;
@ -38,11 +38,10 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>,
};
let loop_replacement = format!("while let Some({}) = {}.pop()", pat, snippet(cx, receiver_span, ".."));
multispan_sugg_with_applicability(
diag,
diag.multipart_suggestion(
"consider using a `while..let` loop",
vec![(loop_span, loop_replacement), (pop_span, pop_replacement)],
Applicability::MachineApplicable,
[(loop_span, loop_replacement), (pop_span, pop_replacement)],
);
},
);

View File

@ -25,6 +25,7 @@ mod while_let_on_iterator;
use clippy_config::msrvs::Msrv;
use clippy_config::Conf;
use clippy_utils::higher;
use rustc_ast::Label;
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -448,7 +449,7 @@ declare_clippy_lint! {
#[clippy::version = "1.80.0"]
pub WHILE_FLOAT,
nursery,
"while loops comaparing floating point values"
"while loops comparing floating point values"
}
declare_clippy_lint! {
@ -760,6 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
body,
loop_id,
span,
label,
}) = for_loop
{
// we don't want to check expanded macros
@ -768,7 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
if body.span.from_expansion() {
return;
}
self.check_for_loop(cx, pat, arg, body, expr, span);
self.check_for_loop(cx, pat, arg, body, expr, span, label);
if let ExprKind::Block(block, _) = body.kind {
never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
}
@ -808,6 +810,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
}
impl Loops {
#[allow(clippy::too_many_arguments)]
fn check_for_loop<'tcx>(
&self,
cx: &LateContext<'tcx>,
@ -816,11 +819,12 @@ impl Loops {
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
span: Span,
label: Option<Label>,
) {
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
if !is_manual_memcpy_triggered {
needless_range_loop::check(cx, pat, arg, body, expr);
explicit_counter_loop::check(cx, pat, arg, body, expr);
explicit_counter_loop::check(cx, pat, arg, body, expr, label);
}
self.check_for_loop_arg(cx, pat, arg);
for_kv_map::check(cx, pat, arg, body);

View File

@ -1,11 +1,12 @@
use super::NEEDLESS_RANGE_LOOP;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::ty::has_iter_method;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq};
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
@ -145,8 +146,7 @@ pub(super) fn check<'tcx>(
arg.span,
format!("the loop variable `{}` is used to index `{indexed}`", ident.name),
|diag| {
multispan_sugg(
diag,
diag.multipart_suggestion(
"consider using an iterator and enumerate()",
vec![
(pat.span, format!("({}, <item>)", ident.name)),
@ -155,6 +155,7 @@ pub(super) fn check<'tcx>(
format!("{indexed}.{method}().enumerate(){method_1}{method_2}"),
),
],
Applicability::HasPlaceholders,
);
},
);
@ -171,10 +172,10 @@ pub(super) fn check<'tcx>(
arg.span,
format!("the loop variable `{}` is only used to index `{indexed}`", ident.name),
|diag| {
multispan_sugg(
diag,
diag.multipart_suggestion(
"consider using an iterator",
vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
Applicability::HasPlaceholders,
);
},
);

View File

@ -1,7 +1,8 @@
use super::UNUSED_ENUMERATE_INDEX;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::{pat_is_wild, sugg};
use rustc_errors::Applicability;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
use rustc_lint::LateContext;
@ -28,13 +29,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_
"you seem to use `.enumerate()` and immediately discard the index",
|diag| {
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
multispan_sugg(
diag,
diag.multipart_suggestion(
"remove the `.enumerate()` call",
vec![
(pat.span, snippet(cx, elem.span, "..").into_owned()),
(arg.span, base_iter.to_string()),
],
Applicability::MachineApplicable,
);
},
);

View File

@ -1,5 +1,5 @@
use super::WHILE_IMMUTABLE_CONDITION;
use clippy_utils::consts::constant;
use clippy_utils::consts::ConstEvalCtxt;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::usage::mutated_variables;
use rustc_hir::def::{DefKind, Res};
@ -10,7 +10,7 @@ use rustc_lint::LateContext;
use std::ops::ControlFlow;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
if constant(cx, cx.typeck_results(), cond).is_some() {
if ConstEvalCtxt::new(cx).eval(cond).is_some() {
// A pure constant condition (e.g., `while false`) is not linted.
return;
}

View File

@ -14,7 +14,7 @@ use rustc_span::symbol::sym;
use rustc_span::Symbol;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::WhileLet { if_then, let_pat, let_expr, .. }) = higher::WhileLet::hir(expr)
if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr)
// check for `Some(..)` pattern
&& let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind
&& is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome)
@ -27,6 +27,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
&& !uses_iter(cx, &iter_expr_struct, if_then)
{
let mut applicability = Applicability::MachineApplicable;
let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name));
let loop_var = if let Some(some_pat) = some_pat.first() {
if is_refutable(cx, some_pat) {
// Refutable patterns don't work with for loops.
@ -57,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
expr.span.with_hi(let_expr.span.hi()),
"this loop could be written as a `for` loop",
"try",
format!("for {loop_var} in {iterator}{by_ref}"),
format!("{loop_label}for {loop_var} in {iterator}{by_ref}"),
applicability,
);
}

View File

@ -1,13 +1,13 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::higher::If;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::is_const_evaluatable;
use clippy_utils::{
eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks,
eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks,
peel_blocks_with_stmt, MaybePath,
};
use itertools::Itertools;
@ -122,8 +122,9 @@ impl<'tcx> ClampSuggestion<'tcx> {
if max_type != min_type {
return false;
}
if let Some(max) = constant(cx, cx.typeck_results(), self.params.max)
&& let Some(min) = constant(cx, cx.typeck_results(), self.params.min)
let ecx = ConstEvalCtxt::new(cx);
if let Some(max) = ecx.eval(self.params.max)
&& let Some(min) = ecx.eval(self.params.min)
&& let Some(ord) = Constant::partial_cmp(cx.tcx, max_type, &min, &max)
{
ord != Ordering::Greater
@ -146,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
if !self.msrv.meets(msrvs::CLAMP) {
return;
}
if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
if !expr.span.from_expansion() && !is_in_const_context(cx) {
let suggestion = is_if_elseif_else_pattern(cx, expr)
.or_else(|| is_max_min_pattern(cx, expr))
.or_else(|| is_call_max_min_pattern(cx, expr))
@ -159,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
}
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
if !self.msrv.meets(msrvs::CLAMP) || in_constant(cx, block.hir_id) {
if !self.msrv.meets(msrvs::CLAMP) || is_in_const_context(cx) {
return;
}
for suggestion in is_two_if_pattern(cx, block) {

View File

@ -1,4 +1,4 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::{is_from_proc_macro, path_to_local};
@ -95,8 +95,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
|| cx.tcx.features().declared(sym!(const_float_classify))
)
&& let [first, second, const_1, const_2] = exprs
&& let Some(const_1) = constant(cx, cx.typeck_results(), const_1)
&& let Some(const_2) = constant(cx, cx.typeck_results(), const_2)
&& let ecx = ConstEvalCtxt::new(cx)
&& let Some(const_1) = ecx.eval(const_1)
&& let Some(const_2) = ecx.eval(const_2)
&& path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
// The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
// case somebody does that for some reason

View File

@ -3,7 +3,7 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::sugg::Sugg;
use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators};
use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators};
use rustc_ast::ast::RangeLimits;
use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability;
@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
return;
}
if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
if is_in_const_context(cx) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
return;
}

View File

@ -1,9 +1,9 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::consts::{ConstEvalCtxt, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context;
use clippy_utils::{in_constant, path_to_local};
use clippy_utils::{is_in_const_context, path_to_local};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
&& add_rhs.span.ctxt() == ctxt
&& !in_external_macro(cx.sess(), expr.span)
&& self.msrv.meets(msrvs::REM_EUCLID)
&& (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !in_constant(cx, expr.hir_id))
&& (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !is_in_const_context(cx))
&& let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs)
&& let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs)
&& let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind
@ -117,7 +117,7 @@ fn check_for_either_unsigned_int_constant<'a>(
}
fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
let int_const = constant_full_int(cx, cx.typeck_results(), expr)?;
let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?;
match int_const {
FullInt::S(s) => s.try_into().ok(),
FullInt::U(u) => Some(u),

View File

@ -1,6 +1,6 @@
use std::fmt::Display;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg;
use rustc_errors::Applicability;
@ -66,7 +66,7 @@ fn parse_shift<'tcx>(
BinOpKind::Shr => ShiftDirection::Right,
_ => return None,
};
let const_expr = constant(cx, cx.typeck_results(), r)?;
let const_expr = ConstEvalCtxt::new(cx).eval(r)?;
if let Constant::Int(shift) = const_expr {
return Some((dir, shift, l));
}

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context;
use clippy_utils::{expr_or_init, in_constant, std_or_core};
use clippy_utils::{expr_or_init, is_in_const_context, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
&& BinOpKind::Mul == op.node
&& !expr.span.from_expansion()
// Does not apply inside const because size_of_val is not cost in stable.
&& !in_constant(cx, expr.hir_id)
&& !is_in_const_context(cx)
&& let Some(receiver) = simplify(cx, left, right)
{
let ctxt = expr.span.ctxt();

View File

@ -1,11 +1,12 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables;
use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind};
@ -14,6 +15,7 @@ use rustc_middle::ty;
use rustc_session::impl_lint_pass;
use rustc_span::source_map::Spanned;
use rustc_span::Span;
use std::iter;
declare_clippy_lint! {
/// ### What it does
@ -108,19 +110,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
format!("stripping a {kind_word} manually"),
|diag| {
diag.span_note(test_span, format!("the {kind_word} was tested here"));
multispan_sugg(
diag,
diag.multipart_suggestion(
format!("try using the `strip_{kind_word}` method"),
vec![(
iter::once((
test_span,
format!(
"if let Some(<stripped>) = {}.strip_{kind_word}({}) ",
snippet(cx, target_arg.span, ".."),
snippet(cx, pattern.span, "..")
),
)]
.into_iter()
.chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
))
.chain(strippings.into_iter().map(|span| (span, "<stripped>".into())))
.collect(),
Applicability::HasPlaceholders,
);
},
);
@ -145,7 +147,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
// Returns the length of the `expr` if it's a constant string or char.
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
let value = constant(cx, cx.typeck_results(), expr)?;
let value = ConstEvalCtxt::new(cx).eval(expr)?;
match value {
Constant::Str(value) => Some(value.len() as u128),
Constant::Char(value) => Some(value.len_utf8() as u128),
@ -183,9 +185,9 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
}
}
// Find expressions where `target` is stripped using the length of `pattern`.
// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
// method.
/// Find expressions where `target` is stripped using the length of `pattern`.
/// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
/// method.
fn find_stripping<'tcx>(
cx: &LateContext<'tcx>,
strip_kind: StripKind,

View File

@ -10,7 +10,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment};
use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment};
declare_clippy_lint! {
/// ### What it does
@ -174,7 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr)
&& !expr.span.from_expansion()
&& !in_constant(cx, expr.hir_id)
&& !is_in_const_context(cx)
{
handle(cx, if_let_or_match, expr);
}

View File

@ -1,4 +1,4 @@
use clippy_utils::consts::constant_simple;
use clippy_utils::consts::ConstEvalCtxt;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
@ -69,7 +69,7 @@ fn check_and_lint<'tcx>(
&& let Some(ty_name) = find_type_name(cx, ty)
&& let Some(or_body_snippet) = snippet_opt(cx, else_expr.span)
&& let Some(indent) = indent_of(cx, expr.span)
&& constant_simple(cx, cx.typeck_results(), else_expr).is_some()
&& ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some()
{
lint(cx, expr, let_expr, ty_name, or_body_snippet, indent);
}

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::Sugg;
use core::iter::once;
@ -54,7 +54,11 @@ where
span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
if !expr.span.from_expansion() {
multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
diag.multipart_suggestion(
msg,
first_sugg.chain(remaining_suggs).collect(),
Applicability::MachineApplicable,
);
}
});
}

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns};
use rustc_errors::Applicability;
@ -148,23 +148,27 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
Applicability::MaybeIncorrect,
),
variants => {
let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden {
suggestions.push("_".into());
"wildcard matches known variants and will also match future added variants"
let (message, add_wildcard) = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden {
(
"wildcard matches known variants and will also match future added variants",
true,
)
} else {
"wildcard match will also match any future added variants"
("wildcard match will also match any future added variants", false)
};
span_lint_and_sugg(
cx,
WILDCARD_ENUM_MATCH_ARM,
wildcard_span,
message,
"try",
suggestions.join(" | "),
Applicability::MaybeIncorrect,
);
span_lint_and_then(cx, WILDCARD_ENUM_MATCH_ARM, wildcard_span, message, |diag| {
let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
if add_wildcard {
suggestions.push("_".into());
}
diag.span_suggestion(
wildcard_span,
"try",
suggestions.join(" | "),
Applicability::MaybeIncorrect,
);
});
},
};
}
@ -176,9 +180,8 @@ enum CommonPrefixSearcher<'a> {
}
impl<'a> CommonPrefixSearcher<'a> {
fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
match path {
[path @ .., _] => self.with_prefix(path),
[] => (),
if let [path @ .., _] = path {
self.with_prefix(path);
}
}

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::macros::{is_panic, root_macro_call};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{in_constant, is_wild, peel_blocks_with_stmt};
use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt};
use rustc_hir::{Arm, Expr, PatKind};
use rustc_lint::LateContext;
use rustc_span::symbol::{kw, sym};
@ -11,7 +11,7 @@ use super::MATCH_WILD_ERR_ARM;
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
// `unwrap`/`expect` is not (yet) const, so we want to allow this in const contexts for now
if in_constant(cx, ex.hir_id) {
if is_in_const_context(cx) {
return;
}

View File

@ -27,7 +27,7 @@ mod wild_in_or_pats;
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::source::walk_span_to_context;
use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg};
use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg};
use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@ -1069,7 +1069,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
match_str_case_mismatch::check(cx, ex, arms);
redundant_guards::check(cx, arms, &self.msrv);
if !in_constant(cx, expr.hir_id) {
if !is_in_const_context(cx) {
manual_unwrap_or::check_match(cx, expr, ex, arms);
manual_map::check_match(cx, expr, ex, arms);
manual_filter::check_match(cx, ex, arms, expr);
@ -1098,7 +1098,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
else_expr,
);
}
if !in_constant(cx, expr.hir_id) {
if !is_in_const_context(cx) {
manual_unwrap_or::check_if_let(
cx,
expr,

View File

@ -1,4 +1,4 @@
use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt};
use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt};
use clippy_utils::diagnostics::span_lint_and_note;
use core::cmp::Ordering;
use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@ -33,22 +33,20 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
.filter_map(|arm| {
if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
let lhs_const = match lhs {
Some(lhs) => constant(cx, cx.typeck_results(), lhs)?,
None => {
let min_val_const = ty.numeric_min_val(cx.tcx)?;
mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
},
let lhs_const = if let Some(lhs) = lhs {
ConstEvalCtxt::new(cx).eval(lhs)?
} else {
let min_val_const = ty.numeric_min_val(cx.tcx)?;
mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
};
let rhs_const = match rhs {
Some(rhs) => constant(cx, cx.typeck_results(), rhs)?,
None => {
let max_val_const = ty.numeric_max_val(cx.tcx)?;
mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
},
let rhs_const = if let Some(rhs) = rhs {
ConstEvalCtxt::new(cx).eval(rhs)?
} else {
let max_val_const = ty.numeric_max_val(cx.tcx)?;
mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
};
let lhs_val = lhs_const.int_value(cx, ty)?;
let rhs_val = rhs_const.int_value(cx, ty)?;
let lhs_val = lhs_const.int_value(cx.tcx, ty)?;
let rhs_val = rhs_const.int_value(cx.tcx, ty)?;
let rhs_bound = match range_end {
RangeEnd::Included => EndBound::Included(rhs_val),
RangeEnd::Excluded => EndBound::Excluded(rhs_val),
@ -60,7 +58,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
}
if let PatKind::Lit(value) = pat.kind {
let value = constant_full_int(cx, cx.typeck_results(), value)?;
let value = ConstEvalCtxt::new(cx).eval_full_int(value)?;
return Some(SpannedRange {
span: pat.span,
node: (value, EndBound::Included(value)),

View File

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::source::snippet;
use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used};
use clippy_utils::{in_constant, path_to_local};
use clippy_utils::{is_in_const_context, path_to_local};
use rustc_ast::{BorrowKind, LitKind};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
@ -116,7 +116,7 @@ fn check_method_calls<'tcx>(
// `s if s.is_empty()` becomes ""
// `arr if arr.is_empty()` becomes []
if ty.is_str() && !in_constant(cx, if_expr.hir_id) {
if ty.is_str() && !is_in_const_context(cx) {
r#""""#.into()
} else if slice_like {
"[]".into()

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir::{Pat, PatKind, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
@ -15,13 +15,15 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
&& fields.len() == def.non_enum_variant().fields.len()
&& !def.non_enum_variant().is_field_list_non_exhaustive()
{
span_lint_and_help(
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
REST_PAT_IN_FULLY_BOUND_STRUCTS,
pat.span,
"unnecessary use of `..` pattern in struct binding. All fields were already bound",
None,
"consider removing `..` from this binding",
|diag| {
diag.help("consider removing `..` from this binding");
},
);
}
}

Some files were not shown because too many files have changed in this diff Show More