mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Merge commit 'cb806113e0f83a8f9b47d35b453b676543bcc40e' into clippy-subtree-update
This commit is contained in:
parent
4c6a3f4b6e
commit
1ac76a2062
297 changed files with 5624 additions and 4064 deletions
|
@ -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"]
|
||||
|
|
13
.github/workflows/clippy.yml
vendored
13
.github/workflows/clippy.yml
vendored
|
@ -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
|
||||
|
|
17
.github/workflows/clippy_bors.yml
vendored
17
.github/workflows/clippy_bors.yml
vendored
|
@ -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
|
||||
|
|
5
.github/workflows/clippy_dev.yml
vendored
5
.github/workflows/clippy_dev.yml
vendored
|
@ -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
|
||||
|
|
4
.github/workflows/lintcheck.yml
vendored
4
.github/workflows/lintcheck.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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('_', "-")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#![feature(let_chains)]
|
||||
#![feature(rustc_private)]
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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(|_| ())
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = \"..\"`");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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)
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
]}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`",
|
||||
);
|
||||
}
|
|
@ -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, it’s 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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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)],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{expr_block, snippet, SpanRangeExt};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
|
||||
use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
|
||||
use core::cmp::max;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{
|
||||
is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs,
|
||||
};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_arena::DroplessArena;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BindingMode, Block, Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::{walk_pat, Visitor};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
|
||||
|
@ -27,52 +32,58 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
|||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
|
||||
if expr.span.from_expansion() {
|
||||
// Don't lint match expressions present in
|
||||
// macro_rules! block
|
||||
return;
|
||||
}
|
||||
if let PatKind::Or(..) = arms[0].pat.kind {
|
||||
// don't lint for or patterns for now, this makes
|
||||
// the lint noisy in unnecessary situations
|
||||
return;
|
||||
}
|
||||
let els = arms[1].body;
|
||||
let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) {
|
||||
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) {
|
||||
if let [arm1, arm2] = arms
|
||||
&& arm1.guard.is_none()
|
||||
&& arm2.guard.is_none()
|
||||
&& !expr.span.from_expansion()
|
||||
// don't lint for or patterns for now, this makes
|
||||
// the lint noisy in unnecessary situations
|
||||
&& !matches!(arm1.pat.kind, PatKind::Or(..))
|
||||
{
|
||||
let els = if is_unit_expr(peel_blocks(arm2.body)) && !empty_arm_has_comment(cx, arm2.body.span) {
|
||||
None
|
||||
} else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
|
||||
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
|
||||
} else if let ExprKind::Block(block, _) = arm2.body.kind {
|
||||
if matches!((block.stmts, block.expr), ([], Some(_)) | ([_], None)) {
|
||||
// single statement/expr "else" block, don't lint
|
||||
return;
|
||||
}
|
||||
// block with 2+ statements or 1 expr and 1+ statement
|
||||
Some(els)
|
||||
Some(arm2.body)
|
||||
} else {
|
||||
// not a block or an empty block w/ comments, don't lint
|
||||
return;
|
||||
};
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(ex);
|
||||
if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) &&
|
||||
(check_single_pattern(arms) || check_opt_like(cx, arms, ty)) {
|
||||
report_single_pattern(cx, ex, arms, expr, els);
|
||||
let typeck = cx.typeck_results();
|
||||
if *typeck.expr_ty(ex).peel_refs().kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
|
||||
let mut v = PatVisitor {
|
||||
typeck,
|
||||
has_enum: false,
|
||||
};
|
||||
if v.visit_pat(arm2.pat).is_break() {
|
||||
return;
|
||||
}
|
||||
if v.has_enum {
|
||||
let cx = PatCtxt {
|
||||
tcx: cx.tcx,
|
||||
param_env: cx.param_env,
|
||||
typeck,
|
||||
arena: DroplessArena::default(),
|
||||
};
|
||||
let mut state = PatState::Other;
|
||||
if !(state.add_pat(&cx, arm2.pat) || state.add_pat(&cx, arm1.pat)) {
|
||||
// Don't lint if the pattern contains an enum which doesn't have a wild match.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
report_single_pattern(cx, ex, arm1, expr, els);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_single_pattern(arms: &[Arm<'_>]) -> bool {
|
||||
is_wild(arms[1].pat)
|
||||
}
|
||||
|
||||
fn report_single_pattern(
|
||||
cx: &LateContext<'_>,
|
||||
ex: &Expr<'_>,
|
||||
arms: &[Arm<'_>],
|
||||
expr: &Expr<'_>,
|
||||
els: Option<&Expr<'_>>,
|
||||
) {
|
||||
fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) {
|
||||
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
|
@ -80,9 +91,9 @@ fn report_single_pattern(
|
|||
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
|
||||
});
|
||||
|
||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
|
||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
|
||||
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
|
||||
&& let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex))
|
||||
&& let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
|
||||
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
|
||||
&& let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait()
|
||||
&& (ty.is_integral()
|
||||
|
@ -114,17 +125,17 @@ fn report_single_pattern(
|
|||
snippet(cx, ex.span, ".."),
|
||||
// PartialEq for different reference counts may not exist.
|
||||
"&".repeat(ref_count_diff),
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
|
||||
snippet(cx, arm.pat.span, ".."),
|
||||
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
|
||||
);
|
||||
(msg, sugg)
|
||||
} else {
|
||||
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
|
||||
let sugg = format!(
|
||||
"if let {} = {} {}{els_str}",
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
snippet(cx, arm.pat.span, ".."),
|
||||
snippet(cx, ex.span, ".."),
|
||||
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
|
||||
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
|
||||
);
|
||||
(msg, sugg)
|
||||
};
|
||||
|
@ -132,106 +143,227 @@ fn report_single_pattern(
|
|||
span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
|
||||
}
|
||||
|
||||
fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool {
|
||||
// We don't want to lint if the second arm contains an enum which could
|
||||
// have more variants in the future.
|
||||
form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat)
|
||||
struct PatVisitor<'tcx> {
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
has_enum: bool,
|
||||
}
|
||||
|
||||
/// Returns `true` if all of the types in the pattern are enums which we know
|
||||
/// won't be expanded in the future
|
||||
fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {
|
||||
let mut paths_and_types = Vec::new();
|
||||
collect_pat_paths(&mut paths_and_types, cx, pat, ty);
|
||||
paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty))
|
||||
}
|
||||
|
||||
/// Returns `true` if the given type is an enum we know won't be expanded in the future
|
||||
fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
// list of candidate `Enum`s we know will never get any more members
|
||||
let candidates = [sym::Cow, sym::Option, sym::Result];
|
||||
|
||||
for candidate_ty in candidates {
|
||||
if is_type_diagnostic_item(cx, ty, candidate_ty) {
|
||||
return true;
|
||||
impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> {
|
||||
type Result = ControlFlow<()>;
|
||||
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) -> Self::Result {
|
||||
if matches!(pat.kind, PatKind::Binding(..)) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum);
|
||||
walk_pat(self, pat)
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Collects types from the given pattern
|
||||
fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) {
|
||||
match pat.kind {
|
||||
PatKind::Tuple(inner, _) => inner.iter().for_each(|p| {
|
||||
let p_ty = cx.typeck_results().pat_ty(p);
|
||||
collect_pat_paths(acc, cx, p, p_ty);
|
||||
}),
|
||||
PatKind::TupleStruct(..) | PatKind::Binding(BindingMode::NONE, .., None) | PatKind::Path(_) => {
|
||||
acc.push(ty);
|
||||
},
|
||||
_ => {},
|
||||
/// The context needed to manipulate a `PatState`.
|
||||
struct PatCtxt<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
arena: DroplessArena,
|
||||
}
|
||||
|
||||
/// State for tracking whether a match can become non-exhaustive by adding a variant to a contained
|
||||
/// enum.
|
||||
///
|
||||
/// This treats certain std enums as if they will never be extended.
|
||||
enum PatState<'a> {
|
||||
/// Either a wild match or an uninteresting type. Uninteresting types include:
|
||||
/// * builtin types (e.g. `i32` or `!`)
|
||||
/// * A struct/tuple/array containing only uninteresting types.
|
||||
/// * A std enum containing only uninteresting types.
|
||||
Wild,
|
||||
/// A std enum we know won't be extended. Tracks the states of each variant separately.
|
||||
///
|
||||
/// This is not used for `Option` since it uses the current pattern to track it's state.
|
||||
StdEnum(&'a mut [PatState<'a>]),
|
||||
/// Either the initial state for a pattern or a non-std enum. There is currently no need to
|
||||
/// distinguish these cases.
|
||||
///
|
||||
/// For non-std enums there's no need to track the state of sub-patterns as the state of just
|
||||
/// this pattern on it's own is enough for linting. Consider two cases:
|
||||
/// * This enum has no wild match. This case alone is enough to determine we can lint.
|
||||
/// * This enum has a wild match and therefore all sub-patterns also have a wild match.
|
||||
///
|
||||
/// In both cases the sub patterns are not needed to determine whether to lint.
|
||||
Other,
|
||||
}
|
||||
impl<'a> PatState<'a> {
|
||||
/// Adds a set of patterns as a product type to the current state. Returns whether or not the
|
||||
/// current state is a wild match after the merge.
|
||||
fn add_product_pat<'tcx>(
|
||||
&mut self,
|
||||
cx: &'a PatCtxt<'tcx>,
|
||||
pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>,
|
||||
) -> bool {
|
||||
// Ideally this would actually keep track of the state separately for each pattern. Doing so would
|
||||
// require implementing something similar to exhaustiveness checking which is a significant increase
|
||||
// in complexity.
|
||||
//
|
||||
// For now treat this as a wild match only if all the sub-patterns are wild
|
||||
let is_wild = pats.into_iter().all(|p| {
|
||||
let mut state = Self::Other;
|
||||
state.add_pat(cx, p)
|
||||
});
|
||||
if is_wild {
|
||||
*self = Self::Wild;
|
||||
}
|
||||
is_wild
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given arm of pattern matching contains wildcard patterns.
|
||||
fn contains_only_wilds(pat: &Pat<'_>) -> bool {
|
||||
match pat.kind {
|
||||
PatKind::Wild => true,
|
||||
PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds),
|
||||
_ => false,
|
||||
/// Attempts to get the state for the enum variant, initializing the current state if necessary.
|
||||
fn get_std_enum_variant<'tcx>(
|
||||
&mut self,
|
||||
cx: &'a PatCtxt<'tcx>,
|
||||
adt: AdtDef<'tcx>,
|
||||
path: &'tcx QPath<'_>,
|
||||
hir_id: HirId,
|
||||
) -> Option<(&mut Self, &'tcx VariantDef)> {
|
||||
let states = match self {
|
||||
Self::Wild => return None,
|
||||
Self::Other => {
|
||||
*self = Self::StdEnum(cx.arena.alloc_from_iter((0..adt.variants().len()).map(|_| Self::Other)));
|
||||
let Self::StdEnum(x) = self else {
|
||||
unreachable!();
|
||||
};
|
||||
x
|
||||
},
|
||||
Self::StdEnum(x) => x,
|
||||
};
|
||||
let i = match cx.typeck.qpath_res(path, hir_id) {
|
||||
Res::Def(DefKind::Ctor(..), id) => adt.variant_index_with_ctor_id(id),
|
||||
Res::Def(DefKind::Variant, id) => adt.variant_index_with_id(id),
|
||||
_ => return None,
|
||||
};
|
||||
Some((&mut states[i.as_usize()], adt.variant(i)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given patterns forms only exhaustive matches that don't contain enum
|
||||
/// patterns without a wildcard.
|
||||
fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool {
|
||||
match (&left.kind, &right.kind) {
|
||||
(PatKind::Wild, _) | (_, PatKind::Wild) => true,
|
||||
(PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
|
||||
// We don't actually know the position and the presence of the `..` (dotdot) operator
|
||||
// in the arms, so we need to evaluate the correct offsets here in order to iterate in
|
||||
// both arms at the same time.
|
||||
let left_pos = left_pos.as_opt_usize();
|
||||
let right_pos = right_pos.as_opt_usize();
|
||||
let len = max(
|
||||
left_in.len() + usize::from(left_pos.is_some()),
|
||||
right_in.len() + usize::from(right_pos.is_some()),
|
||||
);
|
||||
let mut left_pos = left_pos.unwrap_or(usize::MAX);
|
||||
let mut right_pos = right_pos.unwrap_or(usize::MAX);
|
||||
let mut left_dot_space = 0;
|
||||
let mut right_dot_space = 0;
|
||||
for i in 0..len {
|
||||
let mut found_dotdot = false;
|
||||
if i == left_pos {
|
||||
left_dot_space += 1;
|
||||
if left_dot_space < len - left_in.len() {
|
||||
left_pos += 1;
|
||||
}
|
||||
found_dotdot = true;
|
||||
}
|
||||
if i == right_pos {
|
||||
right_dot_space += 1;
|
||||
if right_dot_space < len - right_in.len() {
|
||||
right_pos += 1;
|
||||
}
|
||||
found_dotdot = true;
|
||||
}
|
||||
if found_dotdot {
|
||||
continue;
|
||||
}
|
||||
if !contains_only_wilds(&left_in[i - left_dot_space])
|
||||
&& !contains_only_wilds(&right_in[i - right_dot_space])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fn check_all_wild_enum(&mut self) -> bool {
|
||||
if let Self::StdEnum(states) = self
|
||||
&& states.iter().all(|s| matches!(s, Self::Wild))
|
||||
{
|
||||
*self = Self::Wild;
|
||||
true
|
||||
},
|
||||
(PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right),
|
||||
(PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => {
|
||||
pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds)
|
||||
},
|
||||
_ => false,
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
fn add_struct_pats<'tcx>(
|
||||
&mut self,
|
||||
cx: &'a PatCtxt<'tcx>,
|
||||
pat: &'tcx Pat<'tcx>,
|
||||
path: &'tcx QPath<'tcx>,
|
||||
single_pat: Option<&'tcx Pat<'tcx>>,
|
||||
pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>,
|
||||
) -> bool {
|
||||
let ty::Adt(adt, _) = *cx.typeck.pat_ty(pat).kind() else {
|
||||
// Should never happen
|
||||
*self = Self::Wild;
|
||||
return true;
|
||||
};
|
||||
if adt.is_struct() {
|
||||
return if let Some(pat) = single_pat
|
||||
&& adt.non_enum_variant().fields.len() == 1
|
||||
{
|
||||
self.add_pat(cx, pat)
|
||||
} else {
|
||||
self.add_product_pat(cx, pats)
|
||||
};
|
||||
}
|
||||
match cx.tcx.get_diagnostic_name(adt.did()) {
|
||||
Some(sym::Option) => {
|
||||
if let Some(pat) = single_pat {
|
||||
self.add_pat(cx, pat)
|
||||
} else {
|
||||
*self = Self::Wild;
|
||||
true
|
||||
}
|
||||
},
|
||||
Some(sym::Result | sym::Cow) => {
|
||||
let Some((state, variant)) = self.get_std_enum_variant(cx, adt, path, pat.hir_id) else {
|
||||
return matches!(self, Self::Wild);
|
||||
};
|
||||
let is_wild = if let Some(pat) = single_pat
|
||||
&& variant.fields.len() == 1
|
||||
{
|
||||
state.add_pat(cx, pat)
|
||||
} else {
|
||||
state.add_product_pat(cx, pats)
|
||||
};
|
||||
is_wild && self.check_all_wild_enum()
|
||||
},
|
||||
_ => matches!(self, Self::Wild),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the pattern into the current state. Returns whether or not the current state is a wild
|
||||
/// match after the merge.
|
||||
#[expect(clippy::similar_names)]
|
||||
fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool {
|
||||
match pat.kind {
|
||||
PatKind::Path(_)
|
||||
if match *cx.typeck.pat_ty(pat).peel_refs().kind() {
|
||||
ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()),
|
||||
ty::Tuple(tys) => !tys.is_empty(),
|
||||
ty::Array(_, len) => len.try_eval_target_usize(cx.tcx, cx.param_env) != Some(1),
|
||||
ty::Slice(..) => true,
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
matches!(self, Self::Wild)
|
||||
},
|
||||
|
||||
// Patterns for things which can only contain a single sub-pattern.
|
||||
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
|
||||
self.add_pat(cx, pat)
|
||||
},
|
||||
PatKind::Tuple([sub_pat], pos)
|
||||
if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 =>
|
||||
{
|
||||
self.add_pat(cx, sub_pat)
|
||||
},
|
||||
PatKind::Slice([sub_pat], _, []) | PatKind::Slice([], _, [sub_pat])
|
||||
if let ty::Array(_, len) = *cx.typeck.pat_ty(pat).kind()
|
||||
&& len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(1) =>
|
||||
{
|
||||
self.add_pat(cx, sub_pat)
|
||||
},
|
||||
|
||||
PatKind::Or(pats) => pats.iter().any(|p| self.add_pat(cx, p)),
|
||||
PatKind::Tuple(pats, _) => self.add_product_pat(cx, pats),
|
||||
PatKind::Slice(head, _, tail) => self.add_product_pat(cx, head.iter().chain(tail)),
|
||||
|
||||
PatKind::TupleStruct(ref path, pats, _) => self.add_struct_pats(
|
||||
cx,
|
||||
pat,
|
||||
path,
|
||||
if let [pat] = pats { Some(pat) } else { None },
|
||||
pats.iter(),
|
||||
),
|
||||
PatKind::Struct(ref path, pats, _) => self.add_struct_pats(
|
||||
cx,
|
||||
pat,
|
||||
path,
|
||||
if let [pat] = pats { Some(pat.pat) } else { None },
|
||||
pats.iter().map(|p| p.pat),
|
||||
),
|
||||
|
||||
PatKind::Wild
|
||||
| PatKind::Binding(_, _, _, None)
|
||||
| PatKind::Lit(_)
|
||||
| PatKind::Range(..)
|
||||
| PatKind::Path(_)
|
||||
| PatKind::Never
|
||||
| PatKind::Err(_) => {
|
||||
*self = PatState::Wild;
|
||||
true
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue