Auto merge of #126398 - flip1995:clippy-subtree-update, r=Manishearth
Clippy subtree update r? `@Manishearth` Updates `Cargo.lock` with Clippy version bump.
This commit is contained in:
commit
f1586001ac
208 changed files with 3103 additions and 1672 deletions
10
Cargo.lock
10
Cargo.lock
|
@ -628,7 +628,7 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"clippy_config",
|
"clippy_config",
|
||||||
|
@ -655,7 +655,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy_config"
|
name = "clippy_config"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc-semver",
|
"rustc-semver",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -678,7 +678,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy_lints"
|
name = "clippy_lints"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"cargo_metadata 0.18.1",
|
"cargo_metadata 0.18.1",
|
||||||
|
@ -703,7 +703,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy_utils"
|
name = "clippy_utils"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"clippy_config",
|
"clippy_config",
|
||||||
|
@ -1023,7 +1023,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "declare_clippy_lint"
|
name = "declare_clippy_lint"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools 0.12.1",
|
"itertools 0.12.1",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -6,11 +6,69 @@ document.
|
||||||
|
|
||||||
## Unreleased / Beta / In Rust Nightly
|
## Unreleased / Beta / In Rust Nightly
|
||||||
|
|
||||||
[93f0a9a9...master](https://github.com/rust-lang/rust-clippy/compare/93f0a9a9...master)
|
[ca3b3937...master](https://github.com/rust-lang/rust-clippy/compare/ca3b3937...master)
|
||||||
|
|
||||||
|
## Rust 1.79
|
||||||
|
|
||||||
|
Current stable, released 2024-06-13
|
||||||
|
|
||||||
|
[View all 102 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-03-08T11%3A13%3A58Z..2024-04-18T15%3A50%3A50Z+base%3Amaster)
|
||||||
|
|
||||||
|
### New Lints
|
||||||
|
|
||||||
|
* Added [`legacy_numeric_constants`] to `style`
|
||||||
|
[#12312](https://github.com/rust-lang/rust-clippy/pull/12312)
|
||||||
|
* Added [`missing_transmute_annotations`] to `suspicious`
|
||||||
|
[#12239](https://github.com/rust-lang/rust-clippy/pull/12239)
|
||||||
|
* Added [`integer_division_remainder_used`] to `restriction`
|
||||||
|
[#12451](https://github.com/rust-lang/rust-clippy/pull/12451)
|
||||||
|
* Added [`duplicated_attributes`] to `suspicious`
|
||||||
|
[#12378](https://github.com/rust-lang/rust-clippy/pull/12378)
|
||||||
|
* Added [`manual_unwrap_or_default`] to `suspicious`
|
||||||
|
[#12440](https://github.com/rust-lang/rust-clippy/pull/12440)
|
||||||
|
* Added [`zero_repeat_side_effects`] to `suspicious`
|
||||||
|
[#12449](https://github.com/rust-lang/rust-clippy/pull/12449)
|
||||||
|
* Added [`const_is_empty`] to `suspicious`
|
||||||
|
[#12310](https://github.com/rust-lang/rust-clippy/pull/12310)
|
||||||
|
|
||||||
|
### Moves and Deprecations
|
||||||
|
|
||||||
|
* Moved [`box_default`] to `style` (From `perf`)
|
||||||
|
[#12601](https://github.com/rust-lang/rust-clippy/pull/12601)
|
||||||
|
* Moved [`manual_clamp`] to `complexity` (From `nursery` now warn-by-default)
|
||||||
|
[#12543](https://github.com/rust-lang/rust-clippy/pull/12543)
|
||||||
|
* Moved [`readonly_write_lock`] to `perf` (From `nursery` now warn-by-default)
|
||||||
|
[#12479](https://github.com/rust-lang/rust-clippy/pull/12479)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [`module_name_repetitions`]: Added the [`allowed-prefixes`] configuration to allow common prefixes.
|
||||||
|
[#12573](https://github.com/rust-lang/rust-clippy/pull/12573)
|
||||||
|
* [`cast_sign_loss`], [`cast_possible_truncation`], [`cast_lossless`]: Are now allowed in macros
|
||||||
|
[#12631](https://github.com/rust-lang/rust-clippy/pull/12631)
|
||||||
|
* [`manual_clamp`]: Now only lints on constant min and max values
|
||||||
|
[#12543](https://github.com/rust-lang/rust-clippy/pull/12543)
|
||||||
|
* [`assigning_clones`]: Now considers the [`msrv`] configuration
|
||||||
|
[#12511](https://github.com/rust-lang/rust-clippy/pull/12511)
|
||||||
|
* [`needless_return`], [`useless_let_if_seq`], [`mut_mut`], [`read_zero_byte_vec`], [`unused_io_amount`],
|
||||||
|
[`unused_peekable`]: Now respects `#[allow]` attributes on the affected statement instead
|
||||||
|
[#12446](https://github.com/rust-lang/rust-clippy/pull/12446)
|
||||||
|
|
||||||
|
### False Positive Fixes
|
||||||
|
|
||||||
|
* [`cast_lossless`]: No longer lints when casting to `u128`
|
||||||
|
[#12496](https://github.com/rust-lang/rust-clippy/pull/12496)
|
||||||
|
* [`std_instead_of_core`] No longer lints on modules that are only in `std`
|
||||||
|
[#12447](https://github.com/rust-lang/rust-clippy/pull/12447)
|
||||||
|
|
||||||
|
### ICE Fixes
|
||||||
|
|
||||||
|
* [`needless_return`]: No longer crashes on non-ascii characters
|
||||||
|
[#12493](https://github.com/rust-lang/rust-clippy/pull/12493)
|
||||||
|
|
||||||
## Rust 1.78
|
## Rust 1.78
|
||||||
|
|
||||||
Current stable, released 2024-05-02
|
Released 2024-05-02
|
||||||
|
|
||||||
[View all 112 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-01-26T05%3A46%3A23Z..2024-03-07T16%3A25%3A52Z+base%3Amaster)
|
[View all 112 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-01-26T05%3A46%3A23Z..2024-03-07T16%3A25%3A52Z+base%3Amaster)
|
||||||
|
|
||||||
|
@ -5474,6 +5532,7 @@ Released 2018-09-13
|
||||||
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
|
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
|
||||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||||
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
||||||
|
[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison
|
||||||
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
|
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
|
||||||
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
|
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
|
||||||
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
|
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
|
||||||
|
@ -5567,6 +5626,7 @@ Released 2018-09-13
|
||||||
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
|
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
|
||||||
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
|
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
|
||||||
[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args
|
[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args
|
||||||
|
[`needless_character_iteration`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_character_iteration
|
||||||
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
|
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
|
||||||
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
|
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
|
||||||
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
||||||
|
@ -5576,6 +5636,7 @@ Released 2018-09-13
|
||||||
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
|
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
|
||||||
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
|
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
|
||||||
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
|
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
|
||||||
|
[`needless_maybe_sized`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_maybe_sized
|
||||||
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
|
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
|
||||||
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
|
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
|
||||||
[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
|
[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -172,7 +172,7 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
|
||||||
|
|
||||||
Note: `allow` means to suppress the lint for your code. With `warn` the lint
|
Note: `allow` means to suppress the lint for your code. With `warn` the lint
|
||||||
will only emit a warning, while with `deny` the lint will emit an error, when
|
will only emit a warning, while with `deny` the lint will emit an error, when
|
||||||
triggering for your code. An error causes clippy to exit with an error code, so
|
triggering for your code. An error causes Clippy to exit with an error code, so
|
||||||
is useful in scripts like CI/CD.
|
is useful in scripts like CI/CD.
|
||||||
|
|
||||||
If you do not want to include your lint levels in your code, you can globally
|
If you do not want to include your lint levels in your code, you can globally
|
||||||
|
@ -238,7 +238,7 @@ define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
|
||||||
### Specifying the minimum supported Rust version
|
### Specifying the minimum supported Rust version
|
||||||
|
|
||||||
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
|
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
|
||||||
specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
|
specifying the minimum supported Rust version (MSRV) in the Clippy configuration file.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
msrv = "1.30.0"
|
msrv = "1.30.0"
|
||||||
|
|
|
@ -99,7 +99,7 @@ For more details and options, refer to the Cargo documentation.
|
||||||
### Specifying the minimum supported Rust version
|
### Specifying the minimum supported Rust version
|
||||||
|
|
||||||
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the
|
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the
|
||||||
minimum supported Rust version (MSRV) in the clippy configuration file.
|
minimum supported Rust version (MSRV) in the Clippy configuration file.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
msrv = "1.30.0"
|
msrv = "1.30.0"
|
||||||
|
|
|
@ -107,7 +107,7 @@ More about [intellij] command usage and reasons.
|
||||||
|
|
||||||
## lintcheck
|
## lintcheck
|
||||||
|
|
||||||
`cargo lintcheck` will build and run clippy on a fixed set of crates and
|
`cargo lintcheck` will build and run Clippy on a fixed set of crates and
|
||||||
generate a log of the results. You can `git diff` the updated log against its
|
generate a log of the results. You can `git diff` the updated log against its
|
||||||
previous version and see what impact your lint made on a small set of crates.
|
previous version and see what impact your lint made on a small set of crates.
|
||||||
If you add a new lint, please audit the resulting warnings and make sure there
|
If you add a new lint, please audit the resulting warnings and make sure there
|
||||||
|
|
|
@ -163,11 +163,11 @@ declare_clippy_lint! {
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// // example code where clippy issues a warning
|
/// // example code where Clippy issues a warning
|
||||||
/// ```
|
/// ```
|
||||||
/// Use instead:
|
/// Use instead:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// // example code which does not raise clippy warning
|
/// // example code which does not raise Clippy warning
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.70.0"] // <- In which version was this implemented, keep it up to date!
|
#[clippy::version = "1.70.0"] // <- In which version was this implemented, keep it up to date!
|
||||||
pub LINT_NAME, // <- The lint name IN_ALL_CAPS
|
pub LINT_NAME, // <- The lint name IN_ALL_CAPS
|
||||||
|
|
|
@ -428,7 +428,7 @@ selection of possible matches is produced by the pattern syntax. In the second
|
||||||
stage, the named subpattern references can be used to do additional tests like
|
stage, the named subpattern references can be used to do additional tests like
|
||||||
asserting that a node hasn't been created as part of a macro expansion.
|
asserting that a node hasn't been created as part of a macro expansion.
|
||||||
|
|
||||||
## Implementing clippy lints using patterns
|
## Implementing Clippy lints using patterns
|
||||||
|
|
||||||
As a "real-world" example, I re-implemented the `collapsible_if` lint using
|
As a "real-world" example, I re-implemented the `collapsible_if` lint using
|
||||||
patterns. The code can be found
|
patterns. The code can be found
|
||||||
|
@ -572,7 +572,7 @@ The pattern syntax and the *PatternTree* are independent of specific syntax tree
|
||||||
implementations (rust ast / hir, syn, ...). When looking at the different
|
implementations (rust ast / hir, syn, ...). When looking at the different
|
||||||
pattern examples in the previous sections, it can be seen that the patterns
|
pattern examples in the previous sections, it can be seen that the patterns
|
||||||
don't contain any information specific to a certain syntax tree implementation.
|
don't contain any information specific to a certain syntax tree implementation.
|
||||||
In contrast, clippy lints currently match against ast / hir syntax tree nodes
|
In contrast, Clippy lints currently match against ast / hir syntax tree nodes
|
||||||
and therefore directly depend on their implementation.
|
and therefore directly depend on their implementation.
|
||||||
|
|
||||||
The connection between the *PatternTree* and specific syntax tree
|
The connection between the *PatternTree* and specific syntax tree
|
||||||
|
@ -690,7 +690,7 @@ change, only the `IsMatch` trait implementations need to be adapted and existing
|
||||||
lints can remain unchanged. This also means that if the `IsMatch` trait
|
lints can remain unchanged. This also means that if the `IsMatch` trait
|
||||||
implementations were integrated into the compiler, updating the `IsMatch`
|
implementations were integrated into the compiler, updating the `IsMatch`
|
||||||
implementations would be required for the compiler to compile successfully. This
|
implementations would be required for the compiler to compile successfully. This
|
||||||
could reduce the number of times clippy breaks because of changes in the
|
could reduce the number of times Clippy breaks because of changes in the
|
||||||
compiler. Another advantage of the pattern's independence is that converting an
|
compiler. Another advantage of the pattern's independence is that converting an
|
||||||
`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole
|
`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole
|
||||||
pattern matching code. In fact, the pattern might work just fine without any
|
pattern matching code. In fact, the pattern might work just fine without any
|
||||||
|
@ -777,7 +777,7 @@ complexity to solve a relatively minor problem.
|
||||||
|
|
||||||
The issue of users not knowing about the *PatternTree* structure could be solved
|
The issue of users not knowing about the *PatternTree* structure could be solved
|
||||||
by a tool that, given a rust program, generates a pattern that matches only this
|
by a tool that, given a rust program, generates a pattern that matches only this
|
||||||
program (similar to the clippy author lint).
|
program (similar to the Clippy author lint).
|
||||||
|
|
||||||
For some simple cases (like the first example above), it might be possible to
|
For some simple cases (like the first example above), it might be possible to
|
||||||
successfully mix Rust and pattern syntax. This space could be further explored
|
successfully mix Rust and pattern syntax. This space could be further explored
|
||||||
|
@ -789,7 +789,7 @@ The pattern syntax is heavily inspired by regular expressions (repetitions,
|
||||||
alternatives, sequences, ...).
|
alternatives, sequences, ...).
|
||||||
|
|
||||||
From what I've seen until now, other linters also implement lints that directly
|
From what I've seen until now, other linters also implement lints that directly
|
||||||
work on syntax tree data structures, just like clippy does currently. I would
|
work on syntax tree data structures, just like Clippy does currently. I would
|
||||||
therefore consider the pattern syntax to be *new*, but please correct me if I'm
|
therefore consider the pattern syntax to be *new*, but please correct me if I'm
|
||||||
wrong.
|
wrong.
|
||||||
|
|
||||||
|
@ -982,5 +982,5 @@ pattern!{
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the future, clippy could use this system to also provide lints for custom
|
In the future, Clippy could use this system to also provide lints for custom
|
||||||
syntaxes like those found in macros.
|
syntaxes like those found in macros.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_config"
|
name = "clippy_config"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
This is a dummy crate to publish to crates.io. It primarily exists to ensure
|
This is a dummy crate to publish to crates.io. It primarily exists to ensure
|
||||||
that folks trying to install clippy from crates.io get redirected to the
|
that folks trying to install Clippy from crates.io get redirected to the
|
||||||
`rustup` technique.
|
`rustup` technique.
|
||||||
|
|
||||||
Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`,
|
Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Installing clippy via crates.io is deprecated. Please use the following:
|
Installing Clippy via crates.io is deprecated. Please use the following:
|
||||||
|
|
||||||
```terminal
|
```terminal
|
||||||
rustup component add clippy
|
rustup component add clippy
|
||||||
```
|
```
|
||||||
|
|
||||||
on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary.
|
on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing Clippy binary.
|
||||||
|
|
||||||
See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information
|
See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_lints"
|
name = "clippy_lints"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
use super::{Attribute, MAYBE_MISUSED_CFG};
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
||||||
use rustc_ast::{MetaItemKind, NestedMetaItem};
|
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_lint::EarlyContext;
|
|
||||||
use rustc_span::sym;
|
|
||||||
|
|
||||||
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|
||||||
if attr.has_name(sym::cfg)
|
|
||||||
&& let Some(items) = attr.meta_item_list()
|
|
||||||
{
|
|
||||||
check_nested_misused_cfg(cx, &items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
|
||||||
for item in items {
|
|
||||||
if let NestedMetaItem::MetaItem(meta) = item {
|
|
||||||
if let Some(ident) = meta.ident()
|
|
||||||
&& ident.name.as_str() == "features"
|
|
||||||
&& let Some(val) = meta.value_str()
|
|
||||||
{
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
MAYBE_MISUSED_CFG,
|
|
||||||
meta.span,
|
|
||||||
"'feature' may be misspelled as 'features'",
|
|
||||||
"did you mean",
|
|
||||||
format!("feature = \"{val}\""),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let MetaItemKind::List(list) = &meta.kind {
|
|
||||||
check_nested_misused_cfg(cx, list);
|
|
||||||
// If this is not a list, then we check for `cfg(test)`.
|
|
||||||
} else if let Some(ident) = meta.ident()
|
|
||||||
&& matches!(ident.name.as_str(), "tests" | "Test")
|
|
||||||
{
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
MAYBE_MISUSED_CFG,
|
|
||||||
meta.span,
|
|
||||||
format!("'test' may be misspelled as '{}'", ident.name.as_str()),
|
|
||||||
"did you mean",
|
|
||||||
"test".to_string(),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
use super::{Attribute, MISMATCHED_TARGET_OS};
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
|
||||||
use rustc_ast::{MetaItemKind, NestedMetaItem};
|
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_lint::EarlyContext;
|
|
||||||
use rustc_span::{sym, Span};
|
|
||||||
|
|
||||||
static UNIX_SYSTEMS: &[&str] = &[
|
|
||||||
"android",
|
|
||||||
"dragonfly",
|
|
||||||
"emscripten",
|
|
||||||
"freebsd",
|
|
||||||
"fuchsia",
|
|
||||||
"haiku",
|
|
||||||
"illumos",
|
|
||||||
"ios",
|
|
||||||
"l4re",
|
|
||||||
"linux",
|
|
||||||
"macos",
|
|
||||||
"netbsd",
|
|
||||||
"openbsd",
|
|
||||||
"redox",
|
|
||||||
"solaris",
|
|
||||||
"vxworks",
|
|
||||||
];
|
|
||||||
|
|
||||||
// NOTE: windows is excluded from the list because it's also a valid target family.
|
|
||||||
static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
|
|
||||||
|
|
||||||
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|
||||||
fn find_os(name: &str) -> Option<&'static str> {
|
|
||||||
UNIX_SYSTEMS
|
|
||||||
.iter()
|
|
||||||
.chain(NON_UNIX_SYSTEMS.iter())
|
|
||||||
.find(|&&os| os == name)
|
|
||||||
.copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_unix(name: &str) -> bool {
|
|
||||||
UNIX_SYSTEMS.iter().any(|&os| os == name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
|
|
||||||
let mut mismatched = Vec::new();
|
|
||||||
|
|
||||||
for item in items {
|
|
||||||
if let NestedMetaItem::MetaItem(meta) = item {
|
|
||||||
match &meta.kind {
|
|
||||||
MetaItemKind::List(list) => {
|
|
||||||
mismatched.extend(find_mismatched_target_os(list));
|
|
||||||
},
|
|
||||||
MetaItemKind::Word => {
|
|
||||||
if let Some(ident) = meta.ident()
|
|
||||||
&& let Some(os) = find_os(ident.name.as_str())
|
|
||||||
{
|
|
||||||
mismatched.push((os, ident.span));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MetaItemKind::NameValue(..) => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mismatched
|
|
||||||
}
|
|
||||||
|
|
||||||
if attr.has_name(sym::cfg)
|
|
||||||
&& let Some(list) = attr.meta_item_list()
|
|
||||||
&& let mismatched = find_mismatched_target_os(&list)
|
|
||||||
&& !mismatched.is_empty()
|
|
||||||
{
|
|
||||||
let mess = "operating system used in target family position";
|
|
||||||
|
|
||||||
span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
|
|
||||||
// Avoid showing the unix suggestion multiple times in case
|
|
||||||
// we have more than one mismatch for unix-like systems
|
|
||||||
let mut unix_suggested = false;
|
|
||||||
|
|
||||||
for (os, span) in mismatched {
|
|
||||||
let sugg = format!("target_os = \"{os}\"");
|
|
||||||
diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
|
|
||||||
|
|
||||||
if !unix_suggested && is_unix(os) {
|
|
||||||
diag.help("did you mean `unix`?");
|
|
||||||
unix_suggested = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,8 +7,6 @@ mod deprecated_semver;
|
||||||
mod duplicated_attributes;
|
mod duplicated_attributes;
|
||||||
mod empty_line_after;
|
mod empty_line_after;
|
||||||
mod inline_always;
|
mod inline_always;
|
||||||
mod maybe_misused_cfg;
|
|
||||||
mod mismatched_target_os;
|
|
||||||
mod mixed_attributes_style;
|
mod mixed_attributes_style;
|
||||||
mod non_minimal_cfg;
|
mod non_minimal_cfg;
|
||||||
mod should_panic_without_expect;
|
mod should_panic_without_expect;
|
||||||
|
@ -270,39 +268,6 @@ declare_clippy_lint! {
|
||||||
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
|
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
|
||||||
/// ### What it does
|
|
||||||
/// Checks for cfg attributes having operating systems used in target family position.
|
|
||||||
///
|
|
||||||
/// ### Why is this bad?
|
|
||||||
/// The configuration option will not be recognised and the related item will not be included
|
|
||||||
/// by the conditional compilation engine.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```no_run
|
|
||||||
/// #[cfg(linux)]
|
|
||||||
/// fn conditional() { }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Use instead:
|
|
||||||
/// ```no_run
|
|
||||||
/// # mod hidden {
|
|
||||||
/// #[cfg(target_os = "linux")]
|
|
||||||
/// fn conditional() { }
|
|
||||||
/// # }
|
|
||||||
///
|
|
||||||
/// // or
|
|
||||||
///
|
|
||||||
/// #[cfg(unix)]
|
|
||||||
/// fn conditional() { }
|
|
||||||
/// ```
|
|
||||||
/// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
|
|
||||||
#[clippy::version = "1.45.0"]
|
|
||||||
pub MISMATCHED_TARGET_OS,
|
|
||||||
correctness,
|
|
||||||
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for attributes that allow lints without a reason.
|
/// Checks for attributes that allow lints without a reason.
|
||||||
|
@ -391,38 +356,6 @@ declare_clippy_lint! {
|
||||||
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
|
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
|
||||||
/// ### What it does
|
|
||||||
/// Checks for `#[cfg(features = "...")]` and suggests to replace it with
|
|
||||||
/// `#[cfg(feature = "...")]`.
|
|
||||||
///
|
|
||||||
/// It also checks if `cfg(test)` was misspelled.
|
|
||||||
///
|
|
||||||
/// ### Why is this bad?
|
|
||||||
/// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It
|
|
||||||
/// may cause conditional compilation not work quietly.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```no_run
|
|
||||||
/// #[cfg(features = "some-feature")]
|
|
||||||
/// fn conditional() { }
|
|
||||||
/// #[cfg(tests)]
|
|
||||||
/// mod tests { }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Use instead:
|
|
||||||
/// ```no_run
|
|
||||||
/// #[cfg(feature = "some-feature")]
|
|
||||||
/// fn conditional() { }
|
|
||||||
/// #[cfg(test)]
|
|
||||||
/// mod tests { }
|
|
||||||
/// ```
|
|
||||||
#[clippy::version = "1.69.0"]
|
|
||||||
pub MAYBE_MISUSED_CFG,
|
|
||||||
suspicious,
|
|
||||||
"prevent from misusing the wrong attr name"
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
|
/// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
|
||||||
|
@ -530,7 +463,7 @@ declare_clippy_lint! {
|
||||||
/// #[allow(dead_code)]
|
/// #[allow(dead_code)]
|
||||||
/// fn foo() {}
|
/// fn foo() {}
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.78.0"]
|
#[clippy::version = "1.79.0"]
|
||||||
pub DUPLICATED_ATTRIBUTES,
|
pub DUPLICATED_ATTRIBUTES,
|
||||||
suspicious,
|
suspicious,
|
||||||
"duplicated attribute"
|
"duplicated attribute"
|
||||||
|
@ -612,11 +545,9 @@ pub struct EarlyAttributes {
|
||||||
|
|
||||||
impl_lint_pass!(EarlyAttributes => [
|
impl_lint_pass!(EarlyAttributes => [
|
||||||
DEPRECATED_CFG_ATTR,
|
DEPRECATED_CFG_ATTR,
|
||||||
MISMATCHED_TARGET_OS,
|
|
||||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||||
NON_MINIMAL_CFG,
|
NON_MINIMAL_CFG,
|
||||||
MAYBE_MISUSED_CFG,
|
|
||||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||||
UNNECESSARY_CLIPPY_CFG,
|
UNNECESSARY_CLIPPY_CFG,
|
||||||
]);
|
]);
|
||||||
|
@ -629,9 +560,7 @@ impl EarlyLintPass for EarlyAttributes {
|
||||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||||
deprecated_cfg_attr::check(cx, attr, &self.msrv);
|
deprecated_cfg_attr::check(cx, attr, &self.msrv);
|
||||||
deprecated_cfg_attr::check_clippy(cx, attr);
|
deprecated_cfg_attr::check_clippy(cx, attr);
|
||||||
mismatched_target_os::check(cx, attr);
|
|
||||||
non_minimal_cfg::check(cx, attr);
|
non_minimal_cfg::check(cx, attr);
|
||||||
maybe_misused_cfg::check(cx, attr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(EarlyContext);
|
extract_msrv_attr!(EarlyContext);
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_block_with_applicability;
|
use clippy_utils::source::snippet_block_with_applicability;
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::{higher, is_from_proc_macro};
|
||||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
|
||||||
use clippy_utils::{get_parent_expr, higher, is_from_proc_macro};
|
|
||||||
use core::ops::ControlFlow;
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource};
|
use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::sym;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -124,30 +120,6 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let _: Option<!> = for_each_expr(cond, |e| {
|
|
||||||
if let ExprKind::Closure(closure) = e.kind {
|
|
||||||
// do not lint if the closure is called using an iterator (see #1141)
|
|
||||||
if let Some(parent) = get_parent_expr(cx, e)
|
|
||||||
&& let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind
|
|
||||||
&& let caller = cx.typeck_results().expr_ty(self_arg)
|
|
||||||
&& let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
|
||||||
&& implements_trait(cx, caller, iter_id, &[])
|
|
||||||
{
|
|
||||||
return ControlFlow::Continue(Descend::No);
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = cx.tcx.hir().body(closure.body);
|
|
||||||
let ex = &body.value;
|
|
||||||
if let ExprKind::Block(block, _) = ex.kind {
|
|
||||||
if !body.value.span.from_expansion() && !block.stmts.is_empty() {
|
|
||||||
span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message.clone());
|
|
||||||
return ControlFlow::Continue(Descend::No);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ControlFlow::Continue(Descend::Yes)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,6 +174,25 @@ fn check_inverted_bool_in_condition(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
|
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
|
||||||
|
&& !expr.span.from_expansion()
|
||||||
|
&& !inner.span.from_expansion()
|
||||||
|
&& let Some(suggestion) = simplify_not(cx, inner)
|
||||||
|
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
|
||||||
|
{
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
NONMINIMAL_BOOL,
|
||||||
|
expr.span,
|
||||||
|
"this boolean expression can be simplified",
|
||||||
|
"try",
|
||||||
|
suggestion,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct NonminimalBoolVisitor<'a, 'tcx> {
|
struct NonminimalBoolVisitor<'a, 'tcx> {
|
||||||
cx: &'a LateContext<'tcx>,
|
cx: &'a LateContext<'tcx>,
|
||||||
}
|
}
|
||||||
|
@ -232,6 +251,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.cx.typeck_results().expr_ty(e).is_never() {
|
||||||
|
return Err("contains never type".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
for (n, expr) in self.terminals.iter().enumerate() {
|
for (n, expr) in self.terminals.iter().enumerate() {
|
||||||
if eq_expr_value(self.cx, e, expr) {
|
if eq_expr_value(self.cx, e, expr) {
|
||||||
#[expect(clippy::cast_possible_truncation)]
|
#[expect(clippy::cast_possible_truncation)]
|
||||||
|
@ -542,8 +566,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if improvements.is_empty() {
|
if improvements.is_empty() {
|
||||||
let mut visitor = NotSimplificationVisitor { cx: self.cx };
|
check_simplify_not(self.cx, e);
|
||||||
visitor.visit_expr(e);
|
|
||||||
} else {
|
} else {
|
||||||
nonminimal_bool_lint(
|
nonminimal_bool_lint(
|
||||||
improvements
|
improvements
|
||||||
|
@ -586,30 +609,3 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
.get_diagnostic_item(sym::Ord)
|
.get_diagnostic_item(sym::Ord)
|
||||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NotSimplificationVisitor<'a, 'tcx> {
|
|
||||||
cx: &'a LateContext<'tcx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
|
||||||
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
|
|
||||||
&& !expr.span.from_expansion()
|
|
||||||
&& !inner.span.from_expansion()
|
|
||||||
&& let Some(suggestion) = simplify_not(self.cx, inner)
|
|
||||||
&& self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
|
|
||||||
{
|
|
||||||
span_lint_and_sugg(
|
|
||||||
self.cx,
|
|
||||||
NONMINIMAL_BOOL,
|
|
||||||
expr.span,
|
|
||||||
"this boolean expression can be simplified",
|
|
||||||
"try",
|
|
||||||
suggestion,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
walk_expr(self, expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -71,12 +71,6 @@ struct CargoToml {
|
||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
struct LintsAndGroups {
|
|
||||||
lints: Vec<Spanned<String>>,
|
|
||||||
groups: Vec<(Spanned<String>, Spanned<LintConfig>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
|
fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
|
||||||
Span::new(
|
Span::new(
|
||||||
file.start_pos + BytePos::from_usize(range.start),
|
file.start_pos + BytePos::from_usize(range.start),
|
||||||
|
@ -86,27 +80,28 @@ fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, file: &SourceFile) {
|
fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) {
|
||||||
let mut by_priority = BTreeMap::<_, LintsAndGroups>::new();
|
let mut lints = Vec::new();
|
||||||
|
let mut groups = Vec::new();
|
||||||
for (name, config) in table {
|
for (name, config) in table {
|
||||||
let lints_and_groups = by_priority.entry(config.as_ref().priority()).or_default();
|
if name.get_ref() == "warnings" {
|
||||||
if groups.contains(name.get_ref().as_str()) {
|
continue;
|
||||||
lints_and_groups.groups.push((name, config));
|
}
|
||||||
|
|
||||||
|
if known_groups.contains(name.get_ref().as_str()) {
|
||||||
|
groups.push((name, config));
|
||||||
} else {
|
} else {
|
||||||
lints_and_groups.lints.push(name);
|
lints.push((name, config.into_inner()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let low_priority = by_priority
|
|
||||||
.iter()
|
|
||||||
.find(|(_, lints_and_groups)| !lints_and_groups.lints.is_empty())
|
|
||||||
.map_or(-1, |(&lowest_lint_priority, _)| lowest_lint_priority - 1);
|
|
||||||
|
|
||||||
for (priority, LintsAndGroups { lints, groups }) in by_priority {
|
for (group, group_config) in groups {
|
||||||
let Some(last_lint_alphabetically) = lints.last() else {
|
let priority = group_config.get_ref().priority();
|
||||||
continue;
|
let level = group_config.get_ref().level();
|
||||||
};
|
if let Some((conflict, _)) = lints
|
||||||
|
.iter()
|
||||||
for (group, config) in groups {
|
.rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level)
|
||||||
|
{
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
LINT_GROUPS_PRIORITY,
|
LINT_GROUPS_PRIORITY,
|
||||||
|
@ -116,22 +111,23 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>,
|
||||||
group.as_ref()
|
group.as_ref()
|
||||||
),
|
),
|
||||||
|diag| {
|
|diag| {
|
||||||
let config_span = toml_span(config.span(), file);
|
let config_span = toml_span(group_config.span(), file);
|
||||||
if config.as_ref().is_implicit() {
|
|
||||||
|
if group_config.as_ref().is_implicit() {
|
||||||
diag.span_label(config_span, "has an implicit priority of 0");
|
diag.span_label(config_span, "has an implicit priority of 0");
|
||||||
}
|
}
|
||||||
// add the label to next lint after this group that has the same priority
|
diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint");
|
||||||
let lint = lints
|
|
||||||
.iter()
|
|
||||||
.filter(|lint| lint.span().start > group.span().start)
|
|
||||||
.min_by_key(|lint| lint.span().start)
|
|
||||||
.unwrap_or(last_lint_alphabetically);
|
|
||||||
diag.span_label(toml_span(lint.span(), file), "has the same priority as this lint");
|
|
||||||
diag.note("the order of the lints in the table is ignored by Cargo");
|
diag.note("the order of the lints in the table is ignored by Cargo");
|
||||||
|
|
||||||
let mut suggestion = String::new();
|
let mut suggestion = String::new();
|
||||||
|
let low_priority = lints
|
||||||
|
.iter()
|
||||||
|
.map(|(_, config)| config.priority().saturating_sub(1))
|
||||||
|
.min()
|
||||||
|
.unwrap_or(-1);
|
||||||
Serialize::serialize(
|
Serialize::serialize(
|
||||||
&LintConfigTable {
|
&LintConfigTable {
|
||||||
level: config.as_ref().level().into(),
|
level: level.into(),
|
||||||
priority: Some(low_priority),
|
priority: Some(low_priority),
|
||||||
},
|
},
|
||||||
toml::ser::ValueSerializer::new(&mut suggestion),
|
toml::ser::ValueSerializer::new(&mut suggestion),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::ControlFlow;
|
||||||
|
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
|
||||||
use clippy_utils::{method_chain_args, sext};
|
use clippy_utils::{method_chain_args, sext};
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -266,7 +266,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
|
||||||
fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
|
fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
|
|
||||||
for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
|
for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
|
||||||
// We don't check for mul/div/rem methods here, but we could.
|
// We don't check for mul/div/rem methods here, but we could.
|
||||||
if let ExprKind::Binary(op, lhs, _rhs) = sub_expr.kind {
|
if let ExprKind::Binary(op, lhs, _rhs) = sub_expr.kind {
|
||||||
if matches!(op.node, BinOpKind::Mul | BinOpKind::Div) {
|
if matches!(op.node, BinOpKind::Mul | BinOpKind::Div) {
|
||||||
|
@ -315,7 +315,7 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
|
||||||
fn exprs_with_add_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
|
fn exprs_with_add_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
|
|
||||||
for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
|
for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
|
||||||
// We don't check for add methods here, but we could.
|
// We don't check for add methods here, but we could.
|
||||||
if let ExprKind::Binary(op, _lhs, _rhs) = sub_expr.kind {
|
if let ExprKind::Binary(op, _lhs, _rhs) = sub_expr.kind {
|
||||||
if matches!(op.node, BinOpKind::Add) {
|
if matches!(op.node, BinOpKind::Add) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::numeric_literal::NumericLiteral;
|
use clippy_utils::numeric_literal::NumericLiteral;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::visitors::{for_each_expr, Visitable};
|
use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
|
||||||
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
|
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
|
||||||
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -245,7 +245,7 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
|
||||||
/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
|
/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
|
||||||
/// dark path reimplementing this (or something similar).
|
/// dark path reimplementing this (or something similar).
|
||||||
fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool {
|
fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool {
|
||||||
for_each_expr(expr, |expr| {
|
for_each_expr_without_closures(expr, |expr| {
|
||||||
// Calls are a `Path`, and usage of locals are a `Path`. So, this checks
|
// Calls are a `Path`, and usage of locals are a `Path`. So, this checks
|
||||||
// - call() as i32
|
// - call() as i32
|
||||||
// - local as i32
|
// - local as i32
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
|
use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_ast::ast::Attribute;
|
use rustc_ast::ast::Attribute;
|
||||||
|
@ -65,7 +65,7 @@ impl CognitiveComplexity {
|
||||||
|
|
||||||
let mut cc = 1u64;
|
let mut cc = 1u64;
|
||||||
let mut returns = 0u64;
|
let mut returns = 0u64;
|
||||||
let _: Option<!> = for_each_expr(expr, |e| {
|
let _: Option<!> = for_each_expr_without_closures(expr, |e| {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
ExprKind::If(_, _, _) => {
|
ExprKind::If(_, _, _) => {
|
||||||
cc += 1;
|
cc += 1;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||||
use clippy_utils::visitors::{for_each_expr_with_closures, Visitable};
|
use clippy_utils::visitors::{for_each_expr, Visitable};
|
||||||
use clippy_utils::{get_enclosing_block, path_to_local_id};
|
use clippy_utils::{get_enclosing_block, path_to_local_id};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
|
use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
|
||||||
|
@ -82,7 +82,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI
|
||||||
let mut has_read_access = false;
|
let mut has_read_access = false;
|
||||||
|
|
||||||
// Inspect all expressions and sub-expressions in the block.
|
// Inspect all expressions and sub-expressions in the block.
|
||||||
for_each_expr_with_closures(cx, block, |expr| {
|
for_each_expr(cx, block, |expr| {
|
||||||
// Ignore expressions that are not simply `id`.
|
// Ignore expressions that are not simply `id`.
|
||||||
if !path_to_local_id(expr, id) {
|
if !path_to_local_id(expr, id) {
|
||||||
return ControlFlow::Continue(());
|
return ControlFlow::Continue(());
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
||||||
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
|
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
|
||||||
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
|
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
|
capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
|
||||||
is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
||||||
|
@ -362,7 +362,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
|
||||||
|
|
||||||
/// Checks if the statement modifies or moves any of the given locals.
|
/// Checks if the statement modifies or moves any of the given locals.
|
||||||
fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
|
fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
|
||||||
for_each_expr(s, |e| {
|
for_each_expr_without_closures(s, |e| {
|
||||||
if let Some(id) = path_to_local(e)
|
if let Some(id) = path_to_local(e)
|
||||||
&& locals.contains(&id)
|
&& locals.contains(&id)
|
||||||
&& !capture_local_usage(cx, e).is_imm_ref()
|
&& !capture_local_usage(cx, e).is_imm_ref()
|
||||||
|
@ -413,7 +413,7 @@ fn scan_block_for_eq<'tcx>(
|
||||||
|
|
||||||
let mut cond_locals = HirIdSet::default();
|
let mut cond_locals = HirIdSet::default();
|
||||||
for &cond in conds {
|
for &cond in conds {
|
||||||
let _: Option<!> = for_each_expr(cond, |e| {
|
let _: Option<!> = for_each_expr_without_closures(cond, |e| {
|
||||||
if let Some(id) = path_to_local(e) {
|
if let Some(id) = path_to_local(e) {
|
||||||
cond_locals.insert(id);
|
cond_locals.insert(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||||
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||||
crate::attrs::INLINE_ALWAYS_INFO,
|
crate::attrs::INLINE_ALWAYS_INFO,
|
||||||
crate::attrs::MAYBE_MISUSED_CFG_INFO,
|
|
||||||
crate::attrs::MISMATCHED_TARGET_OS_INFO,
|
|
||||||
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
|
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
|
||||||
crate::attrs::NON_MINIMAL_CFG_INFO,
|
crate::attrs::NON_MINIMAL_CFG_INFO,
|
||||||
crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
|
crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
|
||||||
|
@ -419,6 +417,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::methods::MAP_UNWRAP_OR_INFO,
|
crate::methods::MAP_UNWRAP_OR_INFO,
|
||||||
crate::methods::MUT_MUTEX_LOCK_INFO,
|
crate::methods::MUT_MUTEX_LOCK_INFO,
|
||||||
crate::methods::NAIVE_BYTECOUNT_INFO,
|
crate::methods::NAIVE_BYTECOUNT_INFO,
|
||||||
|
crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO,
|
||||||
crate::methods::NEEDLESS_COLLECT_INFO,
|
crate::methods::NEEDLESS_COLLECT_INFO,
|
||||||
crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
|
crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
|
||||||
crate::methods::NEEDLESS_OPTION_TAKE_INFO,
|
crate::methods::NEEDLESS_OPTION_TAKE_INFO,
|
||||||
|
@ -449,7 +448,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
|
crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
|
||||||
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
|
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
|
||||||
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
|
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
|
||||||
crate::methods::SINGLE_CHAR_PATTERN_INFO,
|
|
||||||
crate::methods::SKIP_WHILE_NEXT_INFO,
|
crate::methods::SKIP_WHILE_NEXT_INFO,
|
||||||
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
||||||
crate::methods::STRING_EXTEND_CHARS_INFO,
|
crate::methods::STRING_EXTEND_CHARS_INFO,
|
||||||
|
@ -531,6 +529,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
||||||
crate::needless_if::NEEDLESS_IF_INFO,
|
crate::needless_if::NEEDLESS_IF_INFO,
|
||||||
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
|
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
|
||||||
|
crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO,
|
||||||
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
|
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
|
||||||
crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO,
|
crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO,
|
||||||
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
|
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
|
||||||
|
@ -656,6 +655,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
|
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
|
||||||
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
|
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
|
||||||
crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO,
|
crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO,
|
||||||
|
crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO,
|
||||||
|
crate::string_patterns::SINGLE_CHAR_PATTERN_INFO,
|
||||||
crate::strings::STRING_ADD_INFO,
|
crate::strings::STRING_ADD_INFO,
|
||||||
crate::strings::STRING_ADD_ASSIGN_INFO,
|
crate::strings::STRING_ADD_ASSIGN_INFO,
|
||||||
crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO,
|
crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO,
|
||||||
|
|
|
@ -215,3 +215,29 @@ declare_deprecated_lint! {
|
||||||
pub WRONG_PUB_SELF_CONVENTION,
|
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"
|
"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::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::version = "1.80.0"]
|
||||||
|
pub MISMATCHED_TARGET_OS,
|
||||||
|
"this lint has been replaced by `unexpected_cfgs`"
|
||||||
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub fn check(
|
||||||
cx,
|
cx,
|
||||||
MISSING_SAFETY_DOC,
|
MISSING_SAFETY_DOC,
|
||||||
span,
|
span,
|
||||||
"unsafe function's docs miss `# Safety` section",
|
"unsafe function's docs are missing a `# Safety` section",
|
||||||
),
|
),
|
||||||
(true, Safety::Safe) => span_lint(
|
(true, Safety::Safe) => span_lint(
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -9,8 +9,8 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty,
|
self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt,
|
||||||
TypeVisitableExt, TypeckResults, TyCtxt,
|
TypeVisitableExt, TypeckResults,
|
||||||
};
|
};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
@ -123,7 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||||
ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
|
ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
let callee_ty = typeck.expr_ty(callee).peel_refs();
|
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))
|
if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
|
||||||
|| !check_inputs(typeck, body.params, None, args)
|
|| !check_inputs(typeck, body.params, None, args)
|
||||||
{
|
{
|
||||||
|
@ -170,15 +171,25 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||||
{
|
{
|
||||||
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
|
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
|
||||||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||||
if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait(
|
if path_to_local(callee).map_or(false, |l| {
|
||||||
cx.param_env,
|
// FIXME: Do we really need this `local_used_in` check?
|
||||||
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
|
// Isn't it checking something like... `callee(callee)`?
|
||||||
ty::PredicatePolarity::Positive,
|
// If somehow this check is needed, add some test for it,
|
||||||
) && path_to_local(callee).map_or(false, |l| {
|
// 'cuz currently nothing changes after deleting this check.
|
||||||
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
|
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
|
||||||
}) {
|
}) {
|
||||||
// Mutable closure is used after current expr; we cannot consume it.
|
match cx.tcx.infer_ctxt().build().type_implements_fn_trait(
|
||||||
snippet = format!("&mut {snippet}");
|
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(
|
diag.span_suggestion(
|
||||||
expr.span,
|
expr.span,
|
||||||
|
|
|
@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
|
if is_whole && !sym_str.contains(['e', 'E']) {
|
||||||
// Normalize the literal by stripping the fractional portion
|
// Normalize the literal by stripping the fractional portion
|
||||||
if sym_str.split('.').next().unwrap() != float_str {
|
if sym_str.split('.').next().unwrap() != float_str {
|
||||||
// If the type suffix is missing the suggestion would be
|
// If the type suffix is missing the suggestion would be
|
||||||
|
|
|
@ -2,7 +2,8 @@ use clippy_utils::consts::Constant::{Int, F32, F64};
|
||||||
use clippy_utils::consts::{constant, constant_simple, Constant};
|
use clippy_utils::consts::{constant, constant_simple, Constant};
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg,
|
eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal,
|
||||||
|
peel_blocks, sugg,
|
||||||
};
|
};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
||||||
|
@ -759,7 +760,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||||
if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
|
if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
|
||||||
let recv_ty = cx.typeck_results().expr_ty(receiver);
|
let recv_ty = cx.typeck_results().expr_ty(receiver);
|
||||||
|
|
||||||
if recv_ty.is_floating_point() && !is_no_std_crate(cx) {
|
if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) {
|
||||||
match path.ident.name.as_str() {
|
match path.ident.name.as_str() {
|
||||||
"ln" => check_ln1p(cx, expr, receiver),
|
"ln" => check_ln1p(cx, expr, receiver),
|
||||||
"log" => check_log_base(cx, expr, receiver, args),
|
"log" => check_log_base(cx, expr, receiver, args),
|
||||||
|
|
|
@ -424,14 +424,14 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
|
||||||
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
|
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
|
||||||
&& implements_trait(cx, target, display_trait_id, &[])
|
&& implements_trait(cx, target, display_trait_id, &[])
|
||||||
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
|
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
|
||||||
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
|
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite())
|
||||||
{
|
{
|
||||||
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
|
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
|
||||||
if n_needed_derefs == 0 && !needs_ref {
|
if n_needed_derefs == 0 && !needs_ref {
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
TO_STRING_IN_FORMAT_ARGS,
|
TO_STRING_IN_FORMAT_ARGS,
|
||||||
to_string_span.with_lo(receiver.span.hi()),
|
to_string_span.with_lo(receiver.span.source_callsite().hi()),
|
||||||
format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
|
format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
|
||||||
"remove this",
|
"remove this",
|
||||||
String::new(),
|
String::new(),
|
||||||
|
|
|
@ -14,7 +14,7 @@ use clippy_utils::attrs::is_proc_macro;
|
||||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::is_must_use_ty;
|
use clippy_utils::ty::is_must_use_ty;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{return_ty, trait_ref_of_method};
|
use clippy_utils::{return_ty, trait_ref_of_method};
|
||||||
|
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
|
@ -226,7 +226,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
|
fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
|
||||||
for_each_expr(body.value, |e| {
|
for_each_expr_without_closures(body.value, |e| {
|
||||||
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
|
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
|
||||||
|
|
||||||
match e.kind {
|
match e.kind {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustc_span::def_id::LocalDefId;
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::ty::type_is_unsafe_function;
|
use clippy_utils::ty::type_is_unsafe_function;
|
||||||
use clippy_utils::visitors::for_each_expr_with_closures;
|
use clippy_utils::visitors::for_each_expr;
|
||||||
use clippy_utils::{iter_input_pats, path_to_local};
|
use clippy_utils::{iter_input_pats, path_to_local};
|
||||||
|
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
|
@ -49,7 +49,7 @@ fn check_raw_ptr<'tcx>(
|
||||||
|
|
||||||
if !raw_ptrs.is_empty() {
|
if !raw_ptrs.is_empty() {
|
||||||
let typeck = cx.tcx.typeck_body(body.id());
|
let typeck = cx.tcx.typeck_body(body.id());
|
||||||
let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| {
|
let _: Option<!> = for_each_expr(cx, body.value, |e| {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
|
hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
|
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{get_async_fn_body, is_async_fn};
|
use clippy_utils::{get_async_fn_body, is_async_fn};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -153,7 +153,7 @@ fn lint_implicit_returns(
|
||||||
|
|
||||||
ExprKind::Loop(block, ..) => {
|
ExprKind::Loop(block, ..) => {
|
||||||
let mut add_return = false;
|
let mut add_return = false;
|
||||||
let _: Option<!> = for_each_expr(block, |e| {
|
let _: Option<!> = for_each_expr_without_closures(block, |e| {
|
||||||
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
||||||
if dest.target_id.ok() == Some(expr.hir_id) {
|
if dest.target_id.ok() == Some(expr.hir_id) {
|
||||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||||
|
|
|
@ -3,8 +3,8 @@ use clippy_utils::source::snippet;
|
||||||
use rustc_errors::{Applicability, SuggestionStyle};
|
use rustc_errors::{Applicability, SuggestionStyle};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, TyKind, AssocItemConstraint,
|
AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier,
|
||||||
WherePredicate,
|
TyKind, WherePredicate,
|
||||||
};
|
};
|
||||||
use rustc_hir_analysis::lower_ty;
|
use rustc_hir_analysis::lower_ty;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
@ -83,8 +83,8 @@ fn emit_lint(
|
||||||
|
|
||||||
let mut sugg = vec![(implied_span_extended, String::new())];
|
let mut sugg = vec![(implied_span_extended, String::new())];
|
||||||
|
|
||||||
// We also might need to include associated item constraints that were specified in the implied bound,
|
// We also might need to include associated item constraints that were specified in the implied
|
||||||
// but omitted in the implied-by bound:
|
// bound, but omitted in the implied-by bound:
|
||||||
// `fn f() -> impl Deref<Target = u8> + DerefMut`
|
// `fn f() -> impl Deref<Target = u8> + DerefMut`
|
||||||
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
|
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
|
||||||
let omitted_constraints: Vec<_> = implied_constraints
|
let omitted_constraints: Vec<_> = implied_constraints
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||||
use clippy_utils::higher;
|
use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
|
||||||
|
use clippy_utils::{higher, is_from_proc_macro};
|
||||||
use rustc_ast::ast::RangeLimits;
|
use rustc_ast::ast::RangeLimits;
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
|
use rustc_span::sym;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -100,11 +102,21 @@ impl IndexingSlicing {
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) {
|
if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id))
|
||||||
|
|| is_from_proc_macro(cx, expr)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ExprKind::Index(array, index, _) = &expr.kind {
|
if let ExprKind::Index(array, index, _) = &expr.kind
|
||||||
|
&& let expr_ty = cx.typeck_results().expr_ty(array)
|
||||||
|
&& let mut deref = deref_chain(cx, expr_ty)
|
||||||
|
&& deref.any(|l| {
|
||||||
|
l.peel_refs().is_slice()
|
||||||
|
|| l.peel_refs().is_array()
|
||||||
|
|| ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr)
|
||||||
|
})
|
||||||
|
{
|
||||||
let note = "the suggestion might not be applicable in constant blocks";
|
let note = "the suggestion might not be applicable in constant blocks";
|
||||||
let ty = cx.typeck_results().expr_ty(array).peel_refs();
|
let ty = cx.typeck_results().expr_ty(array).peel_refs();
|
||||||
if let Some(range) = higher::Range::hir(index) {
|
if let Some(range) = higher::Range::hir(index) {
|
||||||
|
@ -231,3 +243,33 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1
|
||||||
|
|
||||||
(start, end)
|
(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the output Ty of the `get` method on this Ty (if any) matches the Ty returned by the
|
||||||
|
/// indexing operation (if any).
|
||||||
|
fn ty_has_applicable_get_function<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
array_ty: Ty<'tcx>,
|
||||||
|
index_expr: &Expr<'_>,
|
||||||
|
) -> bool {
|
||||||
|
if let ty::Adt(_, _) = array_ty.kind()
|
||||||
|
&& let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| {
|
||||||
|
cx.tcx
|
||||||
|
.fn_sig(m.def_id)
|
||||||
|
.skip_binder()
|
||||||
|
.output()
|
||||||
|
.skip_binder()
|
||||||
|
})
|
||||||
|
&& let ty::Adt(def, args) = get_output_ty.kind()
|
||||||
|
&& cx.tcx.is_diagnostic_item(sym::Option, def.0.did)
|
||||||
|
&& let Some(option_generic_param) = args.first()
|
||||||
|
&& let generic_ty = option_generic_param.expect_ty().peel_refs()
|
||||||
|
// FIXME: ideally this would handle type params and projections properly, for now just assume it's the same type
|
||||||
|
&& (cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs()
|
||||||
|
|| matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_, _)))
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ declare_clippy_lint! {
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// let my_div = 10 >> 1;
|
/// let my_div = 10 >> 1;
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.78.0"]
|
#[clippy::version = "1.79.0"]
|
||||||
pub INTEGER_DIVISION_REMAINDER_USED,
|
pub INTEGER_DIVISION_REMAINDER_USED,
|
||||||
restriction,
|
restriction,
|
||||||
"use of disallowed default division and remainder operations"
|
"use of disallowed default division and remainder operations"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::get_parent_as_impl;
|
use clippy_utils::get_parent_as_impl;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::{implements_trait, make_normalized_projection};
|
use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection};
|
||||||
use rustc_ast::Mutability;
|
use rustc_ast::Mutability;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
|
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
|
||||||
|
@ -9,8 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::{sym, Symbol};
|
use rustc_span::sym;
|
||||||
use std::iter;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -124,33 +123,6 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||||
.is_some_and(|did| cx.effective_visibilities.is_exported(did))
|
.is_some_and(|did| cx.effective_visibilities.is_exported(did))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the deref chain of a type, starting with the type itself.
|
|
||||||
fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
|
|
||||||
iter::successors(Some(ty), |&ty| {
|
|
||||||
if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
|
|
||||||
&& implements_trait(cx, ty, deref_did, &[])
|
|
||||||
{
|
|
||||||
make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
|
|
||||||
if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) {
|
|
||||||
cx.tcx.inherent_impls(ty_did).into_iter().flatten().any(|&did| {
|
|
||||||
cx.tcx
|
|
||||||
.associated_items(did)
|
|
||||||
.filter_by_name_unhygienic(method_name)
|
|
||||||
.next()
|
|
||||||
.is_some_and(|item| item.kind == ty::AssocKind::Fn)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LateLintPass<'_> for IterWithoutIntoIter {
|
impl LateLintPass<'_> for IterWithoutIntoIter {
|
||||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
|
||||||
if !in_external_macro(cx.sess(), item.span)
|
if !in_external_macro(cx.sess(), item.span)
|
||||||
|
@ -167,7 +139,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
|
||||||
}
|
}
|
||||||
&& !deref_chain(cx, ty).any(|ty| {
|
&& !deref_chain(cx, ty).any(|ty| {
|
||||||
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
|
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
|
||||||
ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
|
ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some()
|
||||||
})
|
})
|
||||||
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
|
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
|
||||||
if item.ident.name == sym!(IntoIter) {
|
if item.ident.name == sym!(IntoIter) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ declare_clippy_lint! {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let eps = f32::EPSILON;
|
/// let eps = f32::EPSILON;
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.72.0"]
|
#[clippy::version = "1.79.0"]
|
||||||
pub LEGACY_NUMERIC_CONSTANTS,
|
pub LEGACY_NUMERIC_CONSTANTS,
|
||||||
style,
|
style,
|
||||||
"checks for usage of legacy std numeric constants and methods"
|
"checks for usage of legacy std numeric constants and methods"
|
||||||
|
|
|
@ -67,4 +67,12 @@
|
||||||
"clippy::wrong_pub_self_convention",
|
"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",
|
"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`",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,6 +251,7 @@ mod needless_else;
|
||||||
mod needless_for_each;
|
mod needless_for_each;
|
||||||
mod needless_if;
|
mod needless_if;
|
||||||
mod needless_late_init;
|
mod needless_late_init;
|
||||||
|
mod needless_maybe_sized;
|
||||||
mod needless_parens_on_range_literals;
|
mod needless_parens_on_range_literals;
|
||||||
mod needless_pass_by_ref_mut;
|
mod needless_pass_by_ref_mut;
|
||||||
mod needless_pass_by_value;
|
mod needless_pass_by_value;
|
||||||
|
@ -325,6 +326,7 @@ mod size_of_in_element_count;
|
||||||
mod size_of_ref;
|
mod size_of_ref;
|
||||||
mod slow_vector_initialization;
|
mod slow_vector_initialization;
|
||||||
mod std_instead_of_core;
|
mod std_instead_of_core;
|
||||||
|
mod string_patterns;
|
||||||
mod strings;
|
mod strings;
|
||||||
mod strlen_on_c_strings;
|
mod strlen_on_c_strings;
|
||||||
mod suspicious_operation_groupings;
|
mod suspicious_operation_groupings;
|
||||||
|
@ -1058,6 +1060,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
|
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
|
||||||
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
|
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
|
||||||
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
|
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
|
||||||
|
store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
|
||||||
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
|
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
|
||||||
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
|
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
|
||||||
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
|
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
|
||||||
|
@ -1165,6 +1168,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
store.register_late_pass(|_| Box::new(string_patterns::StringPatterns));
|
||||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ declare_clippy_lint! {
|
||||||
/// let x: Option<Vec<String>> = Some(Vec::new());
|
/// let x: Option<Vec<String>> = Some(Vec::new());
|
||||||
/// let y: Vec<String> = x.unwrap_or_default();
|
/// let y: Vec<String> = x.unwrap_or_default();
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.78.0"]
|
#[clippy::version = "1.79.0"]
|
||||||
pub MANUAL_UNWRAP_OR_DEFAULT,
|
pub MANUAL_UNWRAP_OR_DEFAULT,
|
||||||
suspicious,
|
suspicious,
|
||||||
"check if a `match` or `if let` can be simplified with `unwrap_or_default`"
|
"check if a `match` or `if let` can be simplified with `unwrap_or_default`"
|
||||||
|
@ -57,7 +57,8 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<HirId> {
|
||||||
// Since it comes from a pattern binding, we need to get the parent to actually match
|
// Since it comes from a pattern binding, we need to get the parent to actually match
|
||||||
// against it.
|
// against it.
|
||||||
&& let Some(def_id) = cx.tcx.opt_parent(def_id)
|
&& let Some(def_id) = cx.tcx.opt_parent(def_id)
|
||||||
&& cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id)
|
&& (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id)
|
||||||
|
|| cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id))
|
||||||
{
|
{
|
||||||
let mut bindings = Vec::new();
|
let mut bindings = Vec::new();
|
||||||
pat.each_binding(|_, id, _, _| bindings.push(id));
|
pat.each_binding(|_, id, _, _| bindings.push(id));
|
||||||
|
@ -80,6 +81,14 @@ fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<
|
||||||
&& cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id)
|
&& cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id)
|
||||||
{
|
{
|
||||||
Some(arm.body)
|
Some(arm.body)
|
||||||
|
} else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind
|
||||||
|
&& let Some(def_id) = path.res.opt_def_id()
|
||||||
|
// Since it comes from a pattern binding, we need to get the parent to actually match
|
||||||
|
// against it.
|
||||||
|
&& let Some(def_id) = cx.tcx.opt_parent(def_id)
|
||||||
|
&& cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id)
|
||||||
|
{
|
||||||
|
Some(arm.body)
|
||||||
} else if let PatKind::Wild = arm.pat.kind {
|
} else if let PatKind::Wild = arm.pat.kind {
|
||||||
// We consider that the `Some` check will filter it out if it's not right.
|
// We consider that the `Some` check will filter it out if it's not right.
|
||||||
Some(arm.body)
|
Some(arm.body)
|
||||||
|
|
|
@ -2,7 +2,7 @@ use clippy_config::msrvs::Msrv;
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::macros::matching_root_macro_call;
|
use clippy_utils::macros::matching_root_macro_call;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::visitors::{for_each_expr, is_local_used};
|
use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used};
|
||||||
use clippy_utils::{in_constant, path_to_local};
|
use clippy_utils::{in_constant, path_to_local};
|
||||||
use rustc_ast::{BorrowKind, LitKind};
|
use rustc_ast::{BorrowKind, LitKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -249,7 +249,7 @@ fn emit_redundant_guards<'tcx>(
|
||||||
/// an error in the future, and rustc already actively warns against this (see rust#41620),
|
/// an error in the future, and rustc already actively warns against this (see rust#41620),
|
||||||
/// so we don't consider those as usable within patterns for linting purposes.
|
/// so we don't consider those as usable within patterns for linting purposes.
|
||||||
fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
for_each_expr(expr, |expr| {
|
for_each_expr_without_closures(expr, |expr| {
|
||||||
if match expr.kind {
|
if match expr.kind {
|
||||||
ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat,
|
ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat,
|
||||||
ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => {
|
ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::source::{snippet, walk_span_to_context};
|
use clippy_utils::source::{snippet, walk_span_to_context};
|
||||||
use clippy_utils::sugg::{make_unop, Sugg};
|
use clippy_utils::sugg::{make_unop, Sugg};
|
||||||
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
|
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
|
||||||
use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr};
|
use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures};
|
||||||
use clippy_utils::{higher, is_expn_of, is_trait_method};
|
use clippy_utils::{higher, is_expn_of, is_trait_method};
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -283,7 +283,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
|
||||||
// to see that there aren't any let chains anywhere in the guard, as that would break
|
// to see that there aren't any let chains anywhere in the guard, as that would break
|
||||||
// if we suggest `t.is_none() && (let X = y && z)` for:
|
// if we suggest `t.is_none() && (let X = y && z)` for:
|
||||||
// `match t { None if let X = y && z => true, _ => false }`
|
// `match t { None if let X = y && z => true, _ => false }`
|
||||||
let has_nested_let_chain = for_each_expr(guard, |expr| {
|
let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| {
|
||||||
if matches!(expr.kind, ExprKind::Let(..)) {
|
if matches!(expr.kind, ExprKind::Let(..)) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{eq_expr_value, get_parent_expr};
|
use clippy_utils::{eq_expr_value, get_parent_expr};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -46,7 +46,7 @@ fn collect_replace_calls<'tcx>(
|
||||||
let mut methods = VecDeque::new();
|
let mut methods = VecDeque::new();
|
||||||
let mut from_args = VecDeque::new();
|
let mut from_args = VecDeque::new();
|
||||||
|
|
||||||
let _: Option<()> = for_each_expr(expr, |e| {
|
let _: Option<()> = for_each_expr_without_closures(expr, |e| {
|
||||||
if let Some(("replace", _, [from, to], _, _)) = method_call(e) {
|
if let Some(("replace", _, [from, to], _, _)) = method_call(e) {
|
||||||
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
|
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
|
||||||
methods.push_front(e);
|
methods.push_front(e);
|
||||||
|
|
|
@ -66,6 +66,7 @@ mod map_flatten;
|
||||||
mod map_identity;
|
mod map_identity;
|
||||||
mod map_unwrap_or;
|
mod map_unwrap_or;
|
||||||
mod mut_mutex_lock;
|
mod mut_mutex_lock;
|
||||||
|
mod needless_character_iteration;
|
||||||
mod needless_collect;
|
mod needless_collect;
|
||||||
mod needless_option_as_deref;
|
mod needless_option_as_deref;
|
||||||
mod needless_option_take;
|
mod needless_option_take;
|
||||||
|
@ -93,7 +94,6 @@ mod seek_from_current;
|
||||||
mod seek_to_start_instead_of_rewind;
|
mod seek_to_start_instead_of_rewind;
|
||||||
mod single_char_add_str;
|
mod single_char_add_str;
|
||||||
mod single_char_insert_string;
|
mod single_char_insert_string;
|
||||||
mod single_char_pattern;
|
|
||||||
mod single_char_push_string;
|
mod single_char_push_string;
|
||||||
mod skip_while_next;
|
mod skip_while_next;
|
||||||
mod stable_sort_primitive;
|
mod stable_sort_primitive;
|
||||||
|
@ -1140,38 +1140,6 @@ declare_clippy_lint! {
|
||||||
"not returning type containing `Self` in a `new` method"
|
"not returning type containing `Self` in a `new` method"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
|
||||||
/// ### What it does
|
|
||||||
/// Checks for string methods that receive a single-character
|
|
||||||
/// `str` as an argument, e.g., `_.split("x")`.
|
|
||||||
///
|
|
||||||
/// ### Why is this bad?
|
|
||||||
/// While this can make a perf difference on some systems,
|
|
||||||
/// benchmarks have proven inconclusive. But at least using a
|
|
||||||
/// char literal makes it clear that we are looking at a single
|
|
||||||
/// character.
|
|
||||||
///
|
|
||||||
/// ### Known problems
|
|
||||||
/// Does not catch multi-byte unicode characters. This is by
|
|
||||||
/// design, on many machines, splitting by a non-ascii char is
|
|
||||||
/// actually slower. Please do your own measurements instead of
|
|
||||||
/// relying solely on the results of this lint.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// _.split("x");
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Use instead:
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// _.split('x');
|
|
||||||
/// ```
|
|
||||||
#[clippy::version = "pre 1.29.0"]
|
|
||||||
pub SINGLE_CHAR_PATTERN,
|
|
||||||
pedantic,
|
|
||||||
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for calling `.step_by(0)` on iterators which panics.
|
/// Checks for calling `.step_by(0)` on iterators which panics.
|
||||||
|
@ -4084,12 +4052,33 @@ declare_clippy_lint! {
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// println!("the string is empty");
|
/// println!("the string is empty");
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.78.0"]
|
#[clippy::version = "1.79.0"]
|
||||||
pub CONST_IS_EMPTY,
|
pub CONST_IS_EMPTY,
|
||||||
suspicious,
|
suspicious,
|
||||||
"is_empty() called on strings known at compile time"
|
"is_empty() called on strings known at compile time"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks if an iterator is used to check if a string is ascii.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// The `str` type already implements the `is_ascii` method.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// "foo".chars().all(|c| c.is_ascii());
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// "foo".is_ascii();
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.80.0"]
|
||||||
|
pub NEEDLESS_CHARACTER_ITERATION,
|
||||||
|
suspicious,
|
||||||
|
"is_ascii() called on a char iterator"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Methods {
|
pub struct Methods {
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
|
@ -4147,7 +4136,6 @@ impl_lint_pass!(Methods => [
|
||||||
FLAT_MAP_OPTION,
|
FLAT_MAP_OPTION,
|
||||||
INEFFICIENT_TO_STRING,
|
INEFFICIENT_TO_STRING,
|
||||||
NEW_RET_NO_SELF,
|
NEW_RET_NO_SELF,
|
||||||
SINGLE_CHAR_PATTERN,
|
|
||||||
SINGLE_CHAR_ADD_STR,
|
SINGLE_CHAR_ADD_STR,
|
||||||
SEARCH_IS_SOME,
|
SEARCH_IS_SOME,
|
||||||
FILTER_NEXT,
|
FILTER_NEXT,
|
||||||
|
@ -4255,6 +4243,7 @@ impl_lint_pass!(Methods => [
|
||||||
UNNECESSARY_RESULT_MAP_OR_ELSE,
|
UNNECESSARY_RESULT_MAP_OR_ELSE,
|
||||||
MANUAL_C_STR_LITERALS,
|
MANUAL_C_STR_LITERALS,
|
||||||
UNNECESSARY_GET_THEN_CHECK,
|
UNNECESSARY_GET_THEN_CHECK,
|
||||||
|
NEEDLESS_CHARACTER_ITERATION,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Extracts a method call name, args, and `Span` of the method name.
|
/// Extracts a method call name, args, and `Span` of the method name.
|
||||||
|
@ -4301,7 +4290,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||||
inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
|
inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
|
||||||
single_char_add_str::check(cx, expr, receiver, args);
|
single_char_add_str::check(cx, expr, receiver, args);
|
||||||
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
|
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
|
||||||
single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
|
|
||||||
unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
|
unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
|
||||||
},
|
},
|
||||||
ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
|
ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
|
||||||
|
@ -4462,6 +4450,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("all", [arg]) => {
|
("all", [arg]) => {
|
||||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
|
needless_character_iteration::check(cx, expr, recv, arg, true);
|
||||||
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
||||||
iter_overeager_cloned::check(
|
iter_overeager_cloned::check(
|
||||||
cx,
|
cx,
|
||||||
|
@ -4482,6 +4471,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("any", [arg]) => {
|
("any", [arg]) => {
|
||||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
|
needless_character_iteration::check(cx, expr, recv, arg, false);
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
use super::utils::get_last_chain_binding_hir_id;
|
||||||
|
use super::NEEDLESS_CHARACTER_ITERATION;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::source::snippet_opt;
|
||||||
|
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
|
||||||
|
|
||||||
|
fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
|
||||||
|
while let ExprKind::AddrOf(_, _, e) = expr.kind {
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_expr(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
first_param: HirId,
|
||||||
|
span: Span,
|
||||||
|
before_chars: Span,
|
||||||
|
revert: bool,
|
||||||
|
is_all: bool,
|
||||||
|
) {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::MethodCall(method, receiver, [], _) => {
|
||||||
|
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
|
||||||
|
// `is_ascii`, then only `.all()` should warn.
|
||||||
|
if revert != is_all
|
||||||
|
&& method.ident.name.as_str() == "is_ascii"
|
||||||
|
&& path_to_local_id(receiver, first_param)
|
||||||
|
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
|
||||||
|
&& *char_arg_ty.kind() == ty::Char
|
||||||
|
&& let Some(snippet) = snippet_opt(cx, before_chars)
|
||||||
|
{
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
NEEDLESS_CHARACTER_ITERATION,
|
||||||
|
span,
|
||||||
|
"checking if a string is ascii using iterators",
|
||||||
|
"try",
|
||||||
|
format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExprKind::Block(block, _) => {
|
||||||
|
if block.stmts.iter().any(|stmt| !matches!(stmt.kind, StmtKind::Let(_))) {
|
||||||
|
// If there is something else than let bindings, then better not emit the lint.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(block_expr) = block.expr
|
||||||
|
// First we ensure that this is a "binding chain" (each statement is a binding
|
||||||
|
// of the previous one) and that it is a binding of the closure argument.
|
||||||
|
&& let Some(last_chain_binding_id) =
|
||||||
|
get_last_chain_binding_hir_id(first_param, block.stmts)
|
||||||
|
{
|
||||||
|
handle_expr(
|
||||||
|
cx,
|
||||||
|
block_expr,
|
||||||
|
last_chain_binding_id,
|
||||||
|
span,
|
||||||
|
before_chars,
|
||||||
|
revert,
|
||||||
|
is_all,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert, is_all),
|
||||||
|
ExprKind::Call(fn_path, [arg]) => {
|
||||||
|
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
|
||||||
|
// `is_ascii`, then only `.all()` should warn.
|
||||||
|
if revert != is_all
|
||||||
|
&& let ExprKind::Path(path) = fn_path.kind
|
||||||
|
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
|
||||||
|
&& match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
|
||||||
|
&& path_to_local_id(peels_expr_ref(arg), first_param)
|
||||||
|
&& let Some(snippet) = snippet_opt(cx, before_chars)
|
||||||
|
{
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
NEEDLESS_CHARACTER_ITERATION,
|
||||||
|
span,
|
||||||
|
"checking if a string is ascii using iterators",
|
||||||
|
"try",
|
||||||
|
format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) {
|
||||||
|
if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind
|
||||||
|
&& let body = cx.tcx.hir().body(body)
|
||||||
|
&& let Some(first_param) = body.params.first()
|
||||||
|
&& let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind
|
||||||
|
&& method.ident.name.as_str() == "chars"
|
||||||
|
&& let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs()
|
||||||
|
&& *str_ty.kind() == ty::Str
|
||||||
|
{
|
||||||
|
let expr_start = recv.span;
|
||||||
|
while let ExprKind::MethodCall(_, new_recv, _, _) = recv.kind {
|
||||||
|
recv = new_recv;
|
||||||
|
}
|
||||||
|
let body_expr = peel_blocks(body.value);
|
||||||
|
|
||||||
|
handle_expr(
|
||||||
|
cx,
|
||||||
|
body_expr,
|
||||||
|
first_param.pat.hir_id,
|
||||||
|
recv.span.with_hi(call_expr.span.hi()),
|
||||||
|
recv.span.with_hi(expr_start.hi()),
|
||||||
|
false,
|
||||||
|
is_all,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(
|
||||||
|
|
||||||
let map = cx.tcx.hir();
|
let map = cx.tcx.hir();
|
||||||
let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id));
|
let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id));
|
||||||
reference_visitor.visit_body(&body);
|
reference_visitor.visit_body(body);
|
||||||
|
|
||||||
if reference_visitor.found_reference {
|
if reference_visitor.found_reference {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
||||||
lit.span,
|
lit.span,
|
||||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||||
"try",
|
"try",
|
||||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
||||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||||
use clippy_utils::sugg::deref_closure_args;
|
use clippy_utils::sugg::deref_closure_args;
|
||||||
use clippy_utils::ty::is_type_lang_item;
|
use clippy_utils::ty::is_type_lang_item;
|
||||||
use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs};
|
use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs};
|
||||||
use hir::ExprKind;
|
use hir::ExprKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -156,13 +156,3 @@ pub(super) fn check<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
|
||||||
if let Some(parent_expr) = get_parent_expr(cx, expr)
|
|
||||||
&& let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
|
|
||||||
&& receiver.hir_id == expr.hir_id
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::utils::get_hint_if_single_char_arg;
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
|
||||||
|
use rustc_ast::BorrowKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir::{self as hir, ExprKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
|
||||||
use super::SINGLE_CHAR_ADD_STR;
|
use super::SINGLE_CHAR_ADD_STR;
|
||||||
|
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
|
||||||
/// lint for length-1 `str`s as argument for `insert_str`
|
/// lint for length-1 `str`s as argument for `insert_str`
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) {
|
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) {
|
||||||
let base_string_snippet =
|
let base_string_snippet =
|
||||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
|
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
|
||||||
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
|
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
|
||||||
|
@ -25,4 +25,43 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind
|
||||||
|
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
|
||||||
|
&& path_segment.ident.name == rustc_span::sym::to_string
|
||||||
|
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
|
||||||
|
{
|
||||||
|
let base_string_snippet =
|
||||||
|
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
||||||
|
let extension_string =
|
||||||
|
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
|
||||||
|
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
|
||||||
|
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
|
||||||
|
|
||||||
|
let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})");
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
SINGLE_CHAR_ADD_STR,
|
||||||
|
expr.span,
|
||||||
|
"calling `insert_str()` using a single-character converted to string",
|
||||||
|
"consider using `insert` without `to_string()`",
|
||||||
|
sugg,
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||||
|
if cx.typeck_results().expr_ty(expr).is_ref()
|
||||||
|
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
|
||||||
|
&& ty.is_char()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||||
|
cx.typeck_results().expr_ty(expr).is_char()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
use super::utils::get_hint_if_single_char_arg;
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_hir as hir;
|
|
||||||
use rustc_lint::LateContext;
|
|
||||||
use rustc_middle::ty;
|
|
||||||
use rustc_span::symbol::Symbol;
|
|
||||||
|
|
||||||
use super::SINGLE_CHAR_PATTERN;
|
|
||||||
|
|
||||||
const PATTERN_METHODS: [(&str, usize); 22] = [
|
|
||||||
("contains", 0),
|
|
||||||
("starts_with", 0),
|
|
||||||
("ends_with", 0),
|
|
||||||
("find", 0),
|
|
||||||
("rfind", 0),
|
|
||||||
("split", 0),
|
|
||||||
("split_inclusive", 0),
|
|
||||||
("rsplit", 0),
|
|
||||||
("split_terminator", 0),
|
|
||||||
("rsplit_terminator", 0),
|
|
||||||
("splitn", 1),
|
|
||||||
("rsplitn", 1),
|
|
||||||
("split_once", 0),
|
|
||||||
("rsplit_once", 0),
|
|
||||||
("matches", 0),
|
|
||||||
("rmatches", 0),
|
|
||||||
("match_indices", 0),
|
|
||||||
("rmatch_indices", 0),
|
|
||||||
("trim_start_matches", 0),
|
|
||||||
("trim_end_matches", 0),
|
|
||||||
("replace", 0),
|
|
||||||
("replacen", 0),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
|
|
||||||
pub(super) fn check(
|
|
||||||
cx: &LateContext<'_>,
|
|
||||||
_expr: &hir::Expr<'_>,
|
|
||||||
method_name: Symbol,
|
|
||||||
receiver: &hir::Expr<'_>,
|
|
||||||
args: &[hir::Expr<'_>],
|
|
||||||
) {
|
|
||||||
for &(method, pos) in &PATTERN_METHODS {
|
|
||||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind()
|
|
||||||
&& ty.is_str()
|
|
||||||
&& method_name.as_str() == method
|
|
||||||
&& args.len() > pos
|
|
||||||
&& let arg = &args[pos]
|
|
||||||
&& let mut applicability = Applicability::MachineApplicable
|
|
||||||
&& let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true)
|
|
||||||
{
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
SINGLE_CHAR_PATTERN,
|
|
||||||
arg.span,
|
|
||||||
"single-character string constant used as pattern",
|
|
||||||
"consider using a `char`",
|
|
||||||
hint,
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::utils::get_hint_if_single_char_arg;
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
|
||||||
|
use rustc_ast::BorrowKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir::{self as hir, ExprKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
|
||||||
use super::SINGLE_CHAR_ADD_STR;
|
use super::SINGLE_CHAR_ADD_STR;
|
||||||
|
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
|
||||||
/// lint for length-1 `str`s as argument for `push_str`
|
/// lint for length-1 `str`s as argument for `push_str`
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) {
|
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) {
|
||||||
let base_string_snippet =
|
let base_string_snippet =
|
||||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
||||||
let sugg = format!("{base_string_snippet}.push({extension_string})");
|
let sugg = format!("{base_string_snippet}.push({extension_string})");
|
||||||
|
@ -24,4 +24,42 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind
|
||||||
|
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
|
||||||
|
&& path_segment.ident.name == rustc_span::sym::to_string
|
||||||
|
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
|
||||||
|
{
|
||||||
|
let base_string_snippet =
|
||||||
|
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
||||||
|
let extension_string =
|
||||||
|
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
|
||||||
|
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
|
||||||
|
|
||||||
|
let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})");
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
SINGLE_CHAR_ADD_STR,
|
||||||
|
expr.span,
|
||||||
|
"calling `push_str()` using a single-character converted to string",
|
||||||
|
"consider using `push` without `to_string()`",
|
||||||
|
sugg,
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||||
|
if cx.typeck_results().expr_ty(expr).is_ref()
|
||||||
|
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
|
||||||
|
&& ty.is_char()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||||
|
cx.typeck_results().expr_ty(expr).is_char()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::source::snippet_with_context;
|
use clippy_utils::source::snippet_with_context;
|
||||||
use clippy_utils::usage::local_used_after_expr;
|
use clippy_utils::usage::local_used_after_expr;
|
||||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||||
use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
|
use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -209,7 +209,7 @@ fn indirect_usage<'tcx>(
|
||||||
}) = stmt.kind
|
}) = stmt.kind
|
||||||
{
|
{
|
||||||
let mut path_to_binding = None;
|
let mut path_to_binding = None;
|
||||||
let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
|
let _: Option<!> = for_each_expr(cx, init_expr, |e| {
|
||||||
if path_to_local_id(e, binding) {
|
if path_to_local_id(e, binding) {
|
||||||
path_to_binding = Some(e);
|
path_to_binding = Some(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed;
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::ty::is_copy;
|
use clippy_utils::ty::is_copy;
|
||||||
use clippy_utils::usage::mutated_variables;
|
use clippy_utils::usage::mutated_variables;
|
||||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
|
||||||
use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
|
use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
|
||||||
|
|
||||||
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
|
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
|
||||||
|
|
||||||
let _: Option<!> = for_each_expr(body.value, |e| {
|
let _: Option<!> = for_each_expr_without_closures(body.value, |e| {
|
||||||
if let hir::ExprKind::Ret(Some(e)) = &e.kind {
|
if let hir::ExprKind::Ret(Some(e)) = &e.kind {
|
||||||
let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e);
|
let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e);
|
||||||
found_mapping |= found_mapping_res;
|
found_mapping |= found_mapping_res;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::higher::ForLoop;
|
use clippy_utils::higher::ForLoop;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
|
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
|
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -61,7 +61,7 @@ pub fn check_for_loop_iter(
|
||||||
fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
|
fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
|
||||||
let mut change = false;
|
let mut change = false;
|
||||||
if let ExprKind::Block(block, ..) = body.kind {
|
if let ExprKind::Block(block, ..) = body.kind {
|
||||||
for_each_expr(block, |e| {
|
for_each_expr_without_closures(block, |e| {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
|
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
|
||||||
change |= !can_mut_borrow_both(cx, caller, assignee);
|
change |= !can_mut_borrow_both(cx, caller, assignee);
|
||||||
|
|
|
@ -4,10 +4,11 @@ use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind};
|
use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
|
use super::utils::get_last_chain_binding_hir_id;
|
||||||
use super::UNNECESSARY_RESULT_MAP_OR_ELSE;
|
use super::UNNECESSARY_RESULT_MAP_OR_ELSE;
|
||||||
|
|
||||||
fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) {
|
fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) {
|
||||||
|
@ -25,22 +26,6 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
|
|
||||||
for stmt in statements {
|
|
||||||
if let StmtKind::Let(local) = stmt.kind
|
|
||||||
&& let Some(init) = local.init
|
|
||||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
|
|
||||||
&& let hir::def::Res::Local(local_hir_id) = path.res
|
|
||||||
&& local_hir_id == hir_id
|
|
||||||
{
|
|
||||||
hir_id = local.pat.hir_id;
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(hir_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_qpath(
|
fn handle_qpath(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
expr: &Expr<'_>,
|
expr: &Expr<'_>,
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use clippy_utils::{get_parent_expr, path_to_local_id, usage};
|
use clippy_utils::{get_parent_expr, path_to_local_id, usage};
|
||||||
use rustc_ast::ast;
|
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat};
|
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
@ -49,48 +46,6 @@ pub(super) fn derefs_to_slice<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_hint_if_single_char_arg(
|
|
||||||
cx: &LateContext<'_>,
|
|
||||||
arg: &Expr<'_>,
|
|
||||||
applicability: &mut Applicability,
|
|
||||||
ascii_only: bool,
|
|
||||||
) -> Option<String> {
|
|
||||||
if let ExprKind::Lit(lit) = &arg.kind
|
|
||||||
&& let ast::LitKind::Str(r, style) = lit.node
|
|
||||||
&& let string = r.as_str()
|
|
||||||
&& let len = if ascii_only {
|
|
||||||
string.len()
|
|
||||||
} else {
|
|
||||||
string.chars().count()
|
|
||||||
}
|
|
||||||
&& len == 1
|
|
||||||
{
|
|
||||||
let snip = snippet_with_applicability(cx, arg.span, string, applicability);
|
|
||||||
let ch = if let ast::StrStyle::Raw(nhash) = style {
|
|
||||||
let nhash = nhash as usize;
|
|
||||||
// for raw string: r##"a"##
|
|
||||||
&snip[(nhash + 2)..(snip.len() - 1 - nhash)]
|
|
||||||
} else {
|
|
||||||
// for regular string: "a"
|
|
||||||
&snip[1..(snip.len() - 1)]
|
|
||||||
};
|
|
||||||
|
|
||||||
let hint = format!(
|
|
||||||
"'{}'",
|
|
||||||
match ch {
|
|
||||||
"'" => "\\'",
|
|
||||||
r"\" => "\\\\",
|
|
||||||
"\\\"" => "\"", // no need to escape `"` in `'"'`
|
|
||||||
_ => ch,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(hint)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a
|
/// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a
|
||||||
/// use of `CloneOrCopyVisitor`.
|
/// use of `CloneOrCopyVisitor`.
|
||||||
pub(super) fn clone_or_copy_needed<'tcx>(
|
pub(super) fn clone_or_copy_needed<'tcx>(
|
||||||
|
@ -175,3 +130,19 @@ impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
|
||||||
.any(|hir_id| path_to_local_id(expr, *hir_id))
|
.any(|hir_id| path_to_local_id(expr, *hir_id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
|
||||||
|
for stmt in statements {
|
||||||
|
if let StmtKind::Let(local) = stmt.kind
|
||||||
|
&& let Some(init) = local.init
|
||||||
|
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
|
||||||
|
&& let rustc_hir::def::Res::Local(local_hir_id) = path.res
|
||||||
|
&& local_hir_id == hir_id
|
||||||
|
{
|
||||||
|
hir_id = local.pat.hir_id;
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(hir_id)
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rustc_span::Span;
|
||||||
use super::ZERO_PREFIXED_LITERAL;
|
use super::ZERO_PREFIXED_LITERAL;
|
||||||
|
|
||||||
pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
|
pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
|
||||||
let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0');
|
let trimmed_lit_snip = lit_snip.trim_start_matches(['_', '0']);
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
ZERO_PREFIXED_LITERAL,
|
ZERO_PREFIXED_LITERAL,
|
||||||
|
@ -20,7 +20,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
// do not advise to use octal form if the literal cannot be expressed in base 8.
|
// do not advise to use octal form if the literal cannot be expressed in base 8.
|
||||||
if !lit_snip.contains(|c| c == '8' || c == '9') {
|
if !lit_snip.contains(['8', '9']) {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
lit_span,
|
lit_span,
|
||||||
"if you mean to use an octal constant, use `0o`",
|
"if you mean to use an octal constant, use `0o`",
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::ops::ControlFlow;
|
||||||
use clippy_utils::comparisons::{normalize_comparison, Rel};
|
use clippy_utils::comparisons::{normalize_comparison, Rel};
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{eq_expr_value, hash_expr, higher};
|
use clippy_utils::{eq_expr_value, hash_expr, higher};
|
||||||
use rustc_ast::{LitKind, RangeLimits};
|
use rustc_ast::{LitKind, RangeLimits};
|
||||||
use rustc_data_structures::packed::Pu128;
|
use rustc_data_structures::packed::Pu128;
|
||||||
|
@ -405,7 +405,7 @@ impl LateLintPass<'_> for MissingAssertsForIndexing {
|
||||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
|
fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
|
||||||
let mut map = UnhashMap::default();
|
let mut map = UnhashMap::default();
|
||||||
|
|
||||||
for_each_expr(body.value, |expr| {
|
for_each_expr_without_closures(body.value, |expr| {
|
||||||
check_index(cx, expr, &mut map);
|
check_index(cx, expr, &mut map);
|
||||||
check_assert(cx, expr, &mut map);
|
check_assert(cx, expr, &mut map);
|
||||||
ControlFlow::<!, ()>::Continue(())
|
ControlFlow::<!, ()>::Continue(())
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use clippy_config::msrvs::{self, Msrv};
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
|
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
|
||||||
use clippy_utils::ty::has_drop;
|
|
||||||
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
|
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||||
|
@ -121,10 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FnKind::Method(_, sig, ..) => {
|
FnKind::Method(_, sig, ..) => {
|
||||||
if trait_ref_of_method(cx, def_id).is_some()
|
if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) {
|
||||||
|| already_const(sig.header)
|
|
||||||
|| method_accepts_droppable(cx, def_id)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -151,26 +147,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||||
|
|
||||||
let mir = cx.tcx.optimized_mir(def_id);
|
let mir = cx.tcx.optimized_mir(def_id);
|
||||||
|
|
||||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
|
if let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
|
||||||
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
|
|
||||||
cx.tcx.dcx().span_err(span, err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
|
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extract_msrv_attr!(LateContext);
|
extract_msrv_attr!(LateContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
|
|
||||||
/// can't be made const then, because `drop` can't be const-evaluated.
|
|
||||||
fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
|
|
||||||
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
|
|
||||||
|
|
||||||
// If any of the params are droppable, return true
|
|
||||||
sig.inputs().iter().any(|&ty| has_drop(cx, ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't have to lint on something that's already `const`
|
// We don't have to lint on something that's already `const`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn already_const(header: hir::FnHeader) -> bool {
|
fn already_const(header: hir::FnHeader) -> bool {
|
||||||
|
|
|
@ -110,7 +110,7 @@ fn should_lint<'tcx>(
|
||||||
// Is there a call to `DebugStruct::debug_struct`? Do lint if there is.
|
// Is there a call to `DebugStruct::debug_struct`? Do lint if there is.
|
||||||
let mut has_debug_struct = false;
|
let mut has_debug_struct = false;
|
||||||
|
|
||||||
for_each_expr(block, |expr| {
|
for_each_expr(cx, block, |expr| {
|
||||||
if let ExprKind::MethodCall(path, recv, ..) = &expr.kind {
|
if let ExprKind::MethodCall(path, recv, ..) = &expr.kind {
|
||||||
let recv_ty = typeck_results.expr_ty(recv).peel_refs();
|
let recv_ty = typeck_results.expr_ty(recv).peel_refs();
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ fn check_struct<'tcx>(
|
||||||
let mut has_direct_field_access = false;
|
let mut has_direct_field_access = false;
|
||||||
let mut field_accesses = FxHashSet::default();
|
let mut field_accesses = FxHashSet::default();
|
||||||
|
|
||||||
for_each_expr(block, |expr| {
|
for_each_expr(cx, block, |expr| {
|
||||||
if let ExprKind::Field(target, ident) = expr.kind
|
if let ExprKind::Field(target, ident) = expr.kind
|
||||||
&& let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs()
|
&& let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs()
|
||||||
&& target_ty == self_ty
|
&& target_ty == self_ty
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable};
|
use clippy_utils::visitors::{for_each_expr, Descend, Visitable};
|
||||||
use core::ops::ControlFlow::Continue;
|
use core::ops::ControlFlow::Continue;
|
||||||
use hir::def::{DefKind, Res};
|
use hir::def::{DefKind, Res};
|
||||||
use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
|
use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
|
||||||
|
@ -96,7 +96,7 @@ fn collect_unsafe_exprs<'tcx>(
|
||||||
node: impl Visitable<'tcx>,
|
node: impl Visitable<'tcx>,
|
||||||
unsafe_ops: &mut Vec<(&'static str, Span)>,
|
unsafe_ops: &mut Vec<(&'static str, Span)>,
|
||||||
) {
|
) {
|
||||||
for_each_expr_with_closures(cx, node, |expr| {
|
for_each_expr(cx, node, |expr| {
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
|
ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt,
|
get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, is_receiver_of_method_call,
|
||||||
span_extract_comment, SpanlessEq,
|
peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq,
|
||||||
};
|
};
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -154,7 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
||||||
snip = snip.blockify();
|
snip = snip.blockify();
|
||||||
}
|
}
|
||||||
|
|
||||||
if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) {
|
if (condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id))
|
||||||
|
|| is_receiver_of_method_call(cx, e)
|
||||||
|
|| is_as_argument(cx, e)
|
||||||
|
{
|
||||||
snip = snip.maybe_par();
|
snip = snip.maybe_par();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,3 +445,7 @@ fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool)
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_as_argument(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||||
|
matches!(get_parent_expr(cx, e).map(|e| e.kind), Some(ExprKind::Cast(_, _)))
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::path_to_local;
|
use clippy_utils::path_to_local;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::needs_ordered_drop;
|
use clippy_utils::ty::needs_ordered_drop;
|
||||||
use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used};
|
use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::{Applicability, MultiSpan};
|
use rustc_errors::{Applicability, MultiSpan};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
|
@ -63,7 +63,7 @@ declare_clippy_lint! {
|
||||||
declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
|
declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
|
||||||
|
|
||||||
fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
|
fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
|
||||||
for_each_expr_with_closures(cx, stmt, |e| {
|
for_each_expr(cx, stmt, |e| {
|
||||||
if matches!(e.kind, ExprKind::Assign(..)) {
|
if matches!(e.kind, ExprKind::Assign(..)) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,7 +74,7 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains_let(cond: &Expr<'_>) -> bool {
|
fn contains_let(cond: &Expr<'_>) -> bool {
|
||||||
for_each_expr(cond, |e| {
|
for_each_expr_without_closures(cond, |e| {
|
||||||
if matches!(e.kind, ExprKind::Let(_)) {
|
if matches!(e.kind, ExprKind::Let(_)) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
|
164
src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
Normal file
164
src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::def_id::{DefId, DefIdMap};
|
||||||
|
use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::ty::{ClauseKind, PredicatePolarity};
|
||||||
|
use rustc_session::declare_lint_pass;
|
||||||
|
use rustc_span::symbol::Ident;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Lints `?Sized` bounds applied to type parameters that cannot be unsized
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// The `?Sized` bound is misleading because it cannot be satisfied by an
|
||||||
|
/// unsized type
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// // `T` cannot be unsized because `Clone` requires it to be `Sized`
|
||||||
|
/// fn f<T: Clone + ?Sized>(t: &T) {}
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// fn f<T: Clone>(t: &T) {}
|
||||||
|
///
|
||||||
|
/// // or choose alternative bounds for `T` so that it can be unsized
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.79.0"]
|
||||||
|
pub NEEDLESS_MAYBE_SIZED,
|
||||||
|
suspicious,
|
||||||
|
"a `?Sized` bound that is unusable due to a `Sized` requirement"
|
||||||
|
}
|
||||||
|
declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]);
|
||||||
|
|
||||||
|
#[allow(clippy::struct_field_names)]
|
||||||
|
struct Bound<'tcx> {
|
||||||
|
/// The [`DefId`] of the type parameter the bound refers to
|
||||||
|
param: DefId,
|
||||||
|
ident: Ident,
|
||||||
|
|
||||||
|
trait_bound: &'tcx PolyTraitRef<'tcx>,
|
||||||
|
modifier: TraitBoundModifier,
|
||||||
|
|
||||||
|
predicate_pos: usize,
|
||||||
|
bound_pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds all of the [`Bound`]s that refer to a type parameter and are not from a macro expansion
|
||||||
|
fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item = Bound<'tcx>> {
|
||||||
|
generics
|
||||||
|
.predicates
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(predicate_pos, predicate)| {
|
||||||
|
let WherePredicate::BoundPredicate(bound_predicate) = predicate else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (param, ident) = bound_predicate.bounded_ty.as_generic_param()?;
|
||||||
|
|
||||||
|
Some(
|
||||||
|
bound_predicate
|
||||||
|
.bounds
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(move |(bound_pos, bound)| match bound {
|
||||||
|
&GenericBound::Trait(ref trait_bound, modifier) => Some(Bound {
|
||||||
|
param,
|
||||||
|
ident,
|
||||||
|
trait_bound,
|
||||||
|
modifier,
|
||||||
|
predicate_pos,
|
||||||
|
bound_pos,
|
||||||
|
}),
|
||||||
|
GenericBound::Outlives(_) => None,
|
||||||
|
})
|
||||||
|
.filter(|bound| !bound.trait_bound.span.from_expansion()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the
|
||||||
|
/// path taken to find a `Sized` bound if one is found
|
||||||
|
fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> Option<Vec<DefId>> {
|
||||||
|
fn search(cx: &LateContext<'_>, path: &mut Vec<DefId>) -> bool {
|
||||||
|
let trait_def_id = *path.last().unwrap();
|
||||||
|
|
||||||
|
if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for &(predicate, _) in cx.tcx.super_predicates_of(trait_def_id).predicates {
|
||||||
|
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||||
|
&& trait_predicate.polarity == PredicatePolarity::Positive
|
||||||
|
&& !path.contains(&trait_predicate.def_id())
|
||||||
|
{
|
||||||
|
path.push(trait_predicate.def_id());
|
||||||
|
if search(cx, path) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut path = vec![trait_bound.trait_ref.trait_def_id()?];
|
||||||
|
search(cx, &mut path).then_some(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LateLintPass<'_> for NeedlessMaybeSized {
|
||||||
|
fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) {
|
||||||
|
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics)
|
||||||
|
.filter(|bound| {
|
||||||
|
bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait)
|
||||||
|
&& bound.modifier == TraitBoundModifier::Maybe
|
||||||
|
})
|
||||||
|
.map(|bound| (bound.param, bound))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for bound in type_param_bounds(generics) {
|
||||||
|
if bound.modifier == TraitBoundModifier::None
|
||||||
|
&& let Some(sized_bound) = maybe_sized_params.get(&bound.param)
|
||||||
|
&& let Some(path) = path_to_sized_bound(cx, bound.trait_bound)
|
||||||
|
{
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
NEEDLESS_MAYBE_SIZED,
|
||||||
|
sized_bound.trait_bound.span,
|
||||||
|
"`?Sized` bound is ignored because of a `Sized` requirement",
|
||||||
|
|diag| {
|
||||||
|
let ty_param = sized_bound.ident;
|
||||||
|
diag.span_note(
|
||||||
|
bound.trait_bound.span,
|
||||||
|
format!("`{ty_param}` cannot be unsized because of the bound"),
|
||||||
|
);
|
||||||
|
|
||||||
|
for &[current_id, next_id] in path.array_windows() {
|
||||||
|
let current = cx.tcx.item_name(current_id);
|
||||||
|
let next = cx.tcx.item_name(next_id);
|
||||||
|
diag.note(format!("...because `{current}` has the bound `{next}`"));
|
||||||
|
}
|
||||||
|
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
generics.span_for_bound_removal(sized_bound.predicate_pos, sized_bound.bound_pos),
|
||||||
|
"change the bounds that require `Sized`, or remove the `?Sized` bound",
|
||||||
|
"",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use super::needless_pass_by_value::requires_exact_signature;
|
use super::needless_pass_by_value::requires_exact_signature;
|
||||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::visitors::for_each_expr_with_closures;
|
use clippy_utils::visitors::for_each_expr;
|
||||||
use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self};
|
use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self};
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
||||||
// We retrieve all the closures declared in the function because they will not be found
|
// We retrieve all the closures declared in the function because they will not be found
|
||||||
// by `euv::Delegate`.
|
// by `euv::Delegate`.
|
||||||
let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
|
let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
|
||||||
for_each_expr_with_closures(cx, body, |expr| {
|
for_each_expr(cx, body, |expr| {
|
||||||
if let ExprKind::Closure(closure) = expr.kind {
|
if let ExprKind::Closure(closure) = expr.kind {
|
||||||
closures.insert(closure.def_id);
|
closures.insert(closure.def_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::ty::implements_trait;
|
||||||
use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, std_or_core};
|
use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp};
|
use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::EarlyBinder;
|
use rustc_middle::ty::EarlyBinder;
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
@ -111,7 +112,7 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL
|
||||||
|
|
||||||
impl LateLintPass<'_> for NonCanonicalImpls {
|
impl LateLintPass<'_> for NonCanonicalImpls {
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
|
fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) {
|
||||||
let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else {
|
let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -128,6 +129,9 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
||||||
let ExprKind::Block(block, ..) = body.value.kind else {
|
let ExprKind::Block(block, ..) = body.value.kind else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if in_external_macro(cx.sess(), block.span) || is_from_proc_macro(cx, impl_item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
|
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
|
||||||
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
|
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::ptr;
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::in_constant;
|
use clippy_utils::in_constant;
|
||||||
use clippy_utils::macros::macro_backtrace;
|
use clippy_utils::macros::macro_backtrace;
|
||||||
use clippy_utils::ty::InteriorMut;
|
use clippy_utils::ty::{implements_trait, InteriorMut};
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
|
@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
|
||||||
use rustc_middle::ty::adjustment::Adjust;
|
use rustc_middle::ty::adjustment::Adjust;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
|
use rustc_span::{sym, Span, DUMMY_SP};
|
||||||
use rustc_target::abi::VariantIdx;
|
use rustc_target::abi::VariantIdx;
|
||||||
|
|
||||||
// FIXME: this is a correctness problem but there's no suitable
|
// FIXME: this is a correctness problem but there's no suitable
|
||||||
|
@ -127,19 +127,19 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum Source {
|
enum Source<'tcx> {
|
||||||
Item { item: Span },
|
Item { item: Span, ty: Ty<'tcx> },
|
||||||
Assoc { item: Span },
|
Assoc { item: Span },
|
||||||
Expr { expr: Span },
|
Expr { expr: Span },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source<'_> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn lint(&self) -> (&'static Lint, &'static str, Span) {
|
fn lint(&self) -> (&'static Lint, &'static str, Span) {
|
||||||
match self {
|
match self {
|
||||||
Self::Item { item } | Self::Assoc { item, .. } => (
|
Self::Item { item, .. } | Self::Assoc { item, .. } => (
|
||||||
DECLARE_INTERIOR_MUTABLE_CONST,
|
DECLARE_INTERIOR_MUTABLE_CONST,
|
||||||
"a `const` item should never be interior mutable",
|
"a `const` item should not be interior mutable",
|
||||||
*item,
|
*item,
|
||||||
),
|
),
|
||||||
Self::Expr { expr } => (
|
Self::Expr { expr } => (
|
||||||
|
@ -151,16 +151,24 @@ impl Source {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lint(cx: &LateContext<'_>, source: Source) {
|
fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) {
|
||||||
let (lint, msg, span) = source.lint();
|
let (lint, msg, span) = source.lint();
|
||||||
span_lint_and_then(cx, lint, span, msg, |diag| {
|
span_lint_and_then(cx, lint, span, msg, |diag| {
|
||||||
if span.from_expansion() {
|
if span.from_expansion() {
|
||||||
return; // Don't give suggestions into macros.
|
return; // Don't give suggestions into macros.
|
||||||
}
|
}
|
||||||
match source {
|
match source {
|
||||||
Source::Item { .. } => {
|
Source::Item { ty, .. } => {
|
||||||
let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
|
let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else {
|
||||||
diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
|
return;
|
||||||
|
};
|
||||||
|
if implements_trait(cx, ty, sync_trait, &[]) {
|
||||||
|
diag.help("consider making this a static item");
|
||||||
|
} else {
|
||||||
|
diag.help(
|
||||||
|
"consider making this `Sync` so that it can go in a static item or using a `thread_local`",
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Source::Assoc { .. } => (),
|
Source::Assoc { .. } => (),
|
||||||
Source::Expr { .. } => {
|
Source::Expr { .. } => {
|
||||||
|
@ -311,7 +319,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
|
||||||
&& self.interior_mut.is_interior_mut_ty(cx, ty)
|
&& self.interior_mut.is_interior_mut_ty(cx, ty)
|
||||||
&& Self::is_value_unfrozen_poly(cx, body_id, ty)
|
&& Self::is_value_unfrozen_poly(cx, body_id, ty)
|
||||||
{
|
{
|
||||||
lint(cx, Source::Item { item: it.span });
|
lint(cx, Source::Item { item: it.span, ty });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::ty::implements_trait;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
|
use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -62,7 +62,7 @@ pub(super) fn check<'tcx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
let found_multiple = for_each_expr(e, |e| {
|
let found_multiple = for_each_expr_without_closures(e, |e| {
|
||||||
if eq_expr_value(cx, assignee, e) {
|
if eq_expr_value(cx, assignee, e) {
|
||||||
if found {
|
if found {
|
||||||
return ControlFlow::Break(());
|
return ControlFlow::Break(());
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
|
||||||
|
|
||||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
||||||
let mut panics = Vec::new();
|
let mut panics = Vec::new();
|
||||||
let _: Option<!> = for_each_expr(body.value, |e| {
|
let _: Option<!> = for_each_expr(cx, body.value, |e| {
|
||||||
let Some(macro_call) = root_macro_call_first_node(cx, e) else {
|
let Some(macro_call) = root_macro_call_first_node(cx, e) else {
|
||||||
return ControlFlow::Continue(Descend::Yes);
|
return ControlFlow::Continue(Descend::Yes);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::peel_blocks;
|
use clippy_utils::peel_blocks;
|
||||||
use clippy_utils::source::{snippet, walk_span_to_context};
|
use clippy_utils::source::{snippet, walk_span_to_context};
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::ty::implements_trait;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource,
|
Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource,
|
||||||
|
@ -107,7 +107,7 @@ fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||||
if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
|
if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
|
||||||
&& let ExprKind::Call(_, [into_future_arg]) = match_value.kind
|
&& let ExprKind::Call(_, [into_future_arg]) = match_value.kind
|
||||||
&& let ctxt = expr.span.ctxt()
|
&& let ctxt = expr.span.ctxt()
|
||||||
&& for_each_expr(into_future_arg, |e| {
|
&& for_each_expr_without_closures(into_future_arg, |e| {
|
||||||
walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
|
walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
|
||||||
})
|
})
|
||||||
.is_none()
|
.is_none()
|
||||||
|
|
|
@ -48,6 +48,9 @@ impl EarlyLintPass for DerefAddrOf {
|
||||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
|
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
|
||||||
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind
|
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind
|
||||||
&& let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind
|
&& let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind
|
||||||
|
// NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section.
|
||||||
|
// See #12854 for details.
|
||||||
|
&& !matches!(addrof_target.kind, ExprKind::Array(_))
|
||||||
&& deref_target.span.eq_ctxt(e.span)
|
&& deref_target.span.eq_ctxt(e.span)
|
||||||
&& !addrof_target.span.from_expansion()
|
&& !addrof_target.span.from_expansion()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||||
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
||||||
use clippy_utils::sugg::has_enclosing_paren;
|
use clippy_utils::sugg::has_enclosing_paren;
|
||||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res,
|
binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res,
|
||||||
path_to_local_id, span_contains_cfg, span_find_starting_semi,
|
path_to_local_id, span_contains_cfg, span_find_starting_semi,
|
||||||
|
@ -436,7 +436,7 @@ fn emit_return_lint(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||||
for_each_expr_with_closures(cx, expr, |e| {
|
for_each_expr(cx, expr, |e| {
|
||||||
if let Some(def_id) = fn_def_id(cx, e)
|
if let Some(def_id) = fn_def_id(cx, e)
|
||||||
&& cx
|
&& cx
|
||||||
.tcx
|
.tcx
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::{indent_of, snippet};
|
use clippy_utils::source::{indent_of, snippet};
|
||||||
use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary};
|
use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary};
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||||
|
@ -12,6 +12,7 @@ use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span, DUMMY_SP};
|
use rustc_span::{sym, Span, DUMMY_SP};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -57,7 +58,6 @@ impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]);
|
||||||
pub struct SignificantDropTightening<'tcx> {
|
pub struct SignificantDropTightening<'tcx> {
|
||||||
apas: FxIndexMap<HirId, AuxParamsAttr>,
|
apas: FxIndexMap<HirId, AuxParamsAttr>,
|
||||||
/// Auxiliary structure used to avoid having to verify the same type multiple times.
|
/// Auxiliary structure used to avoid having to verify the same type multiple times.
|
||||||
seen_types: FxHashSet<Ty<'tcx>>,
|
|
||||||
type_cache: FxHashMap<Ty<'tcx>, bool>,
|
type_cache: FxHashMap<Ty<'tcx>, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
|
||||||
self.apas.clear();
|
self.apas.clear();
|
||||||
let initial_dummy_stmt = dummy_stmt_expr(body.value);
|
let initial_dummy_stmt = dummy_stmt_expr(body.value);
|
||||||
let mut ap = AuxParams::new(&mut self.apas, &initial_dummy_stmt);
|
let mut ap = AuxParams::new(&mut self.apas, &initial_dummy_stmt);
|
||||||
StmtsChecker::new(&mut ap, cx, &mut self.seen_types, &mut self.type_cache).visit_body(body);
|
StmtsChecker::new(&mut ap, cx, &mut self.type_cache).visit_body(body);
|
||||||
for apa in ap.apas.values() {
|
for apa in ap.apas.values() {
|
||||||
if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr {
|
if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr {
|
||||||
continue;
|
continue;
|
||||||
|
@ -142,28 +142,25 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
|
||||||
/// Checks the existence of the `#[has_significant_drop]` attribute.
|
/// Checks the existence of the `#[has_significant_drop]` attribute.
|
||||||
struct AttrChecker<'cx, 'others, 'tcx> {
|
struct AttrChecker<'cx, 'others, 'tcx> {
|
||||||
cx: &'cx LateContext<'tcx>,
|
cx: &'cx LateContext<'tcx>,
|
||||||
seen_types: &'others mut FxHashSet<Ty<'tcx>>,
|
|
||||||
type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
|
type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
|
impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(cx: &'cx LateContext<'tcx>, type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>) -> Self {
|
||||||
cx: &'cx LateContext<'tcx>,
|
Self { cx, type_cache }
|
||||||
seen_types: &'others mut FxHashSet<Ty<'tcx>>,
|
|
||||||
type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
|
|
||||||
) -> Self {
|
|
||||||
seen_types.clear();
|
|
||||||
Self {
|
|
||||||
cx,
|
|
||||||
seen_types,
|
|
||||||
type_cache,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
|
fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
|
||||||
// The borrow checker prevents us from using something fancier like or_insert_with.
|
let ty = self
|
||||||
if let Some(ty) = self.type_cache.get(&ty) {
|
.cx
|
||||||
return *ty;
|
.tcx
|
||||||
|
.try_normalize_erasing_regions(self.cx.param_env, ty)
|
||||||
|
.unwrap_or(ty);
|
||||||
|
match self.type_cache.entry(ty) {
|
||||||
|
Entry::Occupied(e) => return *e.get(),
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
e.insert(false);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
let value = self.has_sig_drop_attr_uncached(ty);
|
let value = self.has_sig_drop_attr_uncached(ty);
|
||||||
self.type_cache.insert(ty, value);
|
self.type_cache.insert(ty, value);
|
||||||
|
@ -185,7 +182,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
|
||||||
rustc_middle::ty::Adt(a, b) => {
|
rustc_middle::ty::Adt(a, b) => {
|
||||||
for f in a.all_fields() {
|
for f in a.all_fields() {
|
||||||
let ty = f.ty(self.cx.tcx, b);
|
let ty = f.ty(self.cx.tcx, b);
|
||||||
if !self.has_seen_ty(ty) && self.has_sig_drop_attr(ty) {
|
if self.has_sig_drop_attr(ty) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,16 +202,11 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool {
|
|
||||||
!self.seen_types.insert(ty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> {
|
struct StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> {
|
||||||
ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
|
ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
|
||||||
cx: &'lc LateContext<'tcx>,
|
cx: &'lc LateContext<'tcx>,
|
||||||
seen_types: &'others mut FxHashSet<Ty<'tcx>>,
|
|
||||||
type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
|
type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,15 +214,9 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx
|
||||||
fn new(
|
fn new(
|
||||||
ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
|
ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
|
||||||
cx: &'lc LateContext<'tcx>,
|
cx: &'lc LateContext<'tcx>,
|
||||||
seen_types: &'others mut FxHashSet<Ty<'tcx>>,
|
|
||||||
type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
|
type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self { ap, cx, type_cache }
|
||||||
ap,
|
|
||||||
cx,
|
|
||||||
seen_types,
|
|
||||||
type_cache,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn manage_has_expensive_expr_after_last_attr(&mut self) {
|
fn manage_has_expensive_expr_after_last_attr(&mut self) {
|
||||||
|
@ -288,7 +274,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
|
||||||
apa.counter = apa.counter.wrapping_add(1);
|
apa.counter = apa.counter.wrapping_add(1);
|
||||||
apa.has_expensive_expr_after_last_attr = false;
|
apa.has_expensive_expr_after_last_attr = false;
|
||||||
};
|
};
|
||||||
let mut ac = AttrChecker::new(self.cx, self.seen_types, self.type_cache);
|
let mut ac = AttrChecker::new(self.cx, self.type_cache);
|
||||||
if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) {
|
if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) {
|
||||||
if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind
|
if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind
|
||||||
&& let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
|
&& let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
|
||||||
|
|
227
src/tools/clippy/clippy_lints/src/string_patterns.rs
Normal file
227
src/tools/clippy/clippy_lints/src/string_patterns.rs
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||||
|
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||||
|
use clippy_utils::macros::matching_root_macro_call;
|
||||||
|
use clippy_utils::path_to_local_id;
|
||||||
|
use clippy_utils::source::{snippet, str_literal_to_char_literal};
|
||||||
|
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rustc_ast::{BinOpKind, LitKind};
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{Expr, ExprKind, PatKind};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::ty;
|
||||||
|
use rustc_session::declare_lint_pass;
|
||||||
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for manual `char` comparison in string patterns
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// This can be written more concisely using a `char` or an array of `char`.
|
||||||
|
/// This is more readable and more optimized when comparing to only one `char`.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// "Hello World!".trim_end_matches(|c| c == '.' || c == ',' || c == '!' || c == '?');
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// "Hello World!".trim_end_matches(['.', ',', '!', '?']);
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.80.0"]
|
||||||
|
pub MANUAL_PATTERN_CHAR_COMPARISON,
|
||||||
|
style,
|
||||||
|
"manual char comparison in string patterns"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for string methods that receive a single-character
|
||||||
|
/// `str` as an argument, e.g., `_.split("x")`.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// While this can make a perf difference on some systems,
|
||||||
|
/// benchmarks have proven inconclusive. But at least using a
|
||||||
|
/// char literal makes it clear that we are looking at a single
|
||||||
|
/// character.
|
||||||
|
///
|
||||||
|
/// ### Known problems
|
||||||
|
/// Does not catch multi-byte unicode characters. This is by
|
||||||
|
/// design, on many machines, splitting by a non-ascii char is
|
||||||
|
/// actually slower. Please do your own measurements instead of
|
||||||
|
/// relying solely on the results of this lint.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// _.split("x");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// _.split('x');
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "pre 1.29.0"]
|
||||||
|
pub SINGLE_CHAR_PATTERN,
|
||||||
|
pedantic,
|
||||||
|
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]);
|
||||||
|
|
||||||
|
const PATTERN_METHODS: [(&str, usize); 22] = [
|
||||||
|
("contains", 0),
|
||||||
|
("starts_with", 0),
|
||||||
|
("ends_with", 0),
|
||||||
|
("find", 0),
|
||||||
|
("rfind", 0),
|
||||||
|
("split", 0),
|
||||||
|
("split_inclusive", 0),
|
||||||
|
("rsplit", 0),
|
||||||
|
("split_terminator", 0),
|
||||||
|
("rsplit_terminator", 0),
|
||||||
|
("splitn", 1),
|
||||||
|
("rsplitn", 1),
|
||||||
|
("split_once", 0),
|
||||||
|
("rsplit_once", 0),
|
||||||
|
("matches", 0),
|
||||||
|
("rmatches", 0),
|
||||||
|
("match_indices", 0),
|
||||||
|
("rmatch_indices", 0),
|
||||||
|
("trim_start_matches", 0),
|
||||||
|
("trim_end_matches", 0),
|
||||||
|
("replace", 0),
|
||||||
|
("replacen", 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
fn check_single_char_pattern_lint(cx: &LateContext<'_>, arg: &Expr<'_>) {
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
if let Some(hint) = str_literal_to_char_literal(cx, arg, &mut applicability, true) {
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
SINGLE_CHAR_PATTERN,
|
||||||
|
arg.span,
|
||||||
|
"single-character string constant used as pattern",
|
||||||
|
"consider using a `char`",
|
||||||
|
hint,
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_char_span<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Span> {
|
||||||
|
if cx.typeck_results().expr_ty_adjusted(expr).is_char()
|
||||||
|
&& !expr.span.from_expansion()
|
||||||
|
&& switch_to_eager_eval(cx, expr)
|
||||||
|
{
|
||||||
|
Some(expr.span)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<'_>) {
|
||||||
|
if let ExprKind::Closure(closure) = method_arg.kind
|
||||||
|
&& let body = cx.tcx.hir().body(closure.body)
|
||||||
|
&& let Some(PatKind::Binding(_, binding, ..)) = body.params.first().map(|p| p.pat.kind)
|
||||||
|
{
|
||||||
|
let mut set_char_spans: Vec<Span> = Vec::new();
|
||||||
|
|
||||||
|
// We want to retrieve all the comparisons done.
|
||||||
|
// They are ordered in a nested way and so we need to traverse the AST to collect them all.
|
||||||
|
if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> {
|
||||||
|
match sub_expr.kind {
|
||||||
|
ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => {
|
||||||
|
if path_to_local_id(left, binding)
|
||||||
|
&& let Some(span) = get_char_span(cx, right)
|
||||||
|
{
|
||||||
|
set_char_spans.push(span);
|
||||||
|
ControlFlow::Continue(Descend::No)
|
||||||
|
} else if path_to_local_id(right, binding)
|
||||||
|
&& let Some(span) = get_char_span(cx, left)
|
||||||
|
{
|
||||||
|
set_char_spans.push(span);
|
||||||
|
ControlFlow::Continue(Descend::No)
|
||||||
|
} else {
|
||||||
|
ControlFlow::Break(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExprKind::Binary(op, _, _) if op.node == BinOpKind::Or => ControlFlow::Continue(Descend::Yes),
|
||||||
|
ExprKind::Match(match_value, [arm, _], _) => {
|
||||||
|
if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none()
|
||||||
|
|| arm.guard.is_some()
|
||||||
|
|| !path_to_local_id(match_value, binding)
|
||||||
|
{
|
||||||
|
return ControlFlow::Break(());
|
||||||
|
}
|
||||||
|
if arm.pat.walk_short(|pat| match pat.kind {
|
||||||
|
PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => {
|
||||||
|
if let LitKind::Char(_) = lit.node {
|
||||||
|
set_char_spans.push(lit.span);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
},
|
||||||
|
PatKind::Or(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}) {
|
||||||
|
ControlFlow::Continue(Descend::No)
|
||||||
|
} else {
|
||||||
|
ControlFlow::Break(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => ControlFlow::Break(()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
MANUAL_PATTERN_CHAR_COMPARISON,
|
||||||
|
method_arg.span,
|
||||||
|
"this manual char comparison can be written more succinctly",
|
||||||
|
|diag| {
|
||||||
|
if let [set_char_span] = set_char_spans[..] {
|
||||||
|
diag.span_suggestion(
|
||||||
|
method_arg.span,
|
||||||
|
"consider using a `char`",
|
||||||
|
snippet(cx, set_char_span, "c"),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
diag.span_suggestion(
|
||||||
|
method_arg.span,
|
||||||
|
"consider using an array of `char`",
|
||||||
|
format!(
|
||||||
|
"[{}]",
|
||||||
|
set_char_spans.into_iter().map(|span| snippet(cx, span, "c")).join(", ")
|
||||||
|
),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for StringPatterns {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
|
if !expr.span.from_expansion()
|
||||||
|
&& let ExprKind::MethodCall(method, receiver, args, _) = expr.kind
|
||||||
|
&& let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind()
|
||||||
|
&& ty.is_str()
|
||||||
|
&& let method_name = method.ident.name.as_str()
|
||||||
|
&& let Some(&(_, pos)) = PATTERN_METHODS
|
||||||
|
.iter()
|
||||||
|
.find(|(array_method_name, _)| *array_method_name == method_name)
|
||||||
|
&& let Some(arg) = args.get(pos)
|
||||||
|
{
|
||||||
|
check_single_char_pattern_lint(cx, arg);
|
||||||
|
|
||||||
|
check_manual_pattern_char_comparison(cx, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -399,13 +399,17 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
|
||||||
&& let ty::Ref(_, ty, ..) = ty.kind()
|
&& let ty::Ref(_, ty, ..) = ty.kind()
|
||||||
&& ty.is_str()
|
&& ty.is_str()
|
||||||
{
|
{
|
||||||
span_lint_and_help(
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability);
|
||||||
|
|
||||||
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
STR_TO_STRING,
|
STR_TO_STRING,
|
||||||
expr.span,
|
expr.span,
|
||||||
"`to_string()` called on a `&str`",
|
"`to_string()` called on a `&str`",
|
||||||
None,
|
"try",
|
||||||
"consider using `.to_owned()`",
|
format!("{snippet}.to_owned()"),
|
||||||
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS};
|
use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
||||||
|
|
||||||
fn count_binops(expr: &hir::Expr<'_>) -> u32 {
|
fn count_binops(expr: &hir::Expr<'_>) -> u32 {
|
||||||
let mut count = 0u32;
|
let mut count = 0u32;
|
||||||
let _: Option<!> = for_each_expr(expr, |e| {
|
let _: Option<!> = for_each_expr_without_closures(expr, |e| {
|
||||||
if matches!(
|
if matches!(
|
||||||
e.kind,
|
e.kind,
|
||||||
hir::ExprKind::Binary(..)
|
hir::ExprKind::Binary(..)
|
||||||
|
|
|
@ -31,7 +31,6 @@ fn get_parent_local_binding_ty<'tcx>(cx: &LateContext<'tcx>, expr_hir_id: HirId)
|
||||||
fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool {
|
fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool {
|
||||||
let def_id = cx.tcx.hir().enclosing_body_owner(expr_hir_id);
|
let def_id = cx.tcx.hir().enclosing_body_owner(expr_hir_id);
|
||||||
if let Some(body) = cx.tcx.hir().maybe_body_owned_by(def_id) {
|
if let Some(body) = cx.tcx.hir().maybe_body_owned_by(def_id) {
|
||||||
let body = cx.tcx.hir().body(body.id());
|
|
||||||
return body.value.peel_blocks().hir_id == expr_hir_id;
|
return body.value.peel_blocks().hir_id == expr_hir_id;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
|
@ -546,7 +546,7 @@ declare_clippy_lint! {
|
||||||
/// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
|
/// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.77.0"]
|
#[clippy::version = "1.79.0"]
|
||||||
pub MISSING_TRANSMUTE_ANNOTATIONS,
|
pub MISSING_TRANSMUTE_ANNOTATIONS,
|
||||||
suspicious,
|
suspicious,
|
||||||
"warns if a transmute call doesn't have all generics specified"
|
"warns if a transmute call doesn't have all generics specified"
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::ControlFlow;
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::is_lint_allowed;
|
use clippy_utils::is_lint_allowed;
|
||||||
use clippy_utils::source::walk_span_to_context;
|
use clippy_utils::source::walk_span_to_context;
|
||||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||||
use hir::HirId;
|
use hir::HirId;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -296,7 +296,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should roughly be the reverse of `block_parents_have_safety_comment`
|
// this should roughly be the reverse of `block_parents_have_safety_comment`
|
||||||
if for_each_expr_with_closures(cx, expr, |expr| match expr.kind {
|
if for_each_expr(cx, expr, |expr| match expr.kind {
|
||||||
hir::ExprKind::Block(
|
hir::ExprKind::Block(
|
||||||
Block {
|
Block {
|
||||||
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
|
||||||
let parent_item = cx.tcx.hir().expect_item(parent);
|
let parent_item = cx.tcx.hir().expect_item(parent);
|
||||||
let assoc_item = cx.tcx.associated_item(impl_item.owner_id);
|
let assoc_item = cx.tcx.associated_item(impl_item.owner_id);
|
||||||
let contains_todo = |cx, body: &'_ Body<'_>| -> bool {
|
let contains_todo = |cx, body: &'_ Body<'_>| -> bool {
|
||||||
clippy_utils::visitors::for_each_expr(body.value, |e| {
|
clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| {
|
||||||
if let Some(macro_call) = root_macro_call_first_node(cx, e) {
|
if let Some(macro_call) = root_macro_call_first_node(cx, e) {
|
||||||
if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
|
if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
|
|
|
@ -77,7 +77,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc
|
||||||
let body = cx.tcx.hir().body(body_id);
|
let body = cx.tcx.hir().body(body_id);
|
||||||
let typeck = cx.tcx.typeck(impl_item.owner_id.def_id);
|
let typeck = cx.tcx.typeck(impl_item.owner_id.def_id);
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let _: Option<!> = for_each_expr(body.value, |e| {
|
let _: Option<!> = for_each_expr(cx, body.value, |e| {
|
||||||
// check for `expect`
|
// check for `expect`
|
||||||
if let Some(arglists) = method_chain_args(e, &["expect"]) {
|
if let Some(arglists) = method_chain_args(e, &["expect"]) {
|
||||||
let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
|
let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
|
||||||
|
|
|
@ -733,7 +733,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
||||||
match stmt.value.kind {
|
match stmt.value.kind {
|
||||||
StmtKind::Let(local) => {
|
StmtKind::Let(local) => {
|
||||||
bind!(self, local);
|
bind!(self, local);
|
||||||
kind!("Local({local})");
|
kind!("Let({local})");
|
||||||
self.option(field!(local.init), "init", |init| {
|
self.option(field!(local.init), "init", |init| {
|
||||||
self.expr(init);
|
self.expr(init);
|
||||||
});
|
});
|
||||||
|
|
|
@ -213,7 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
||||||
output: &mut self.registered_lints,
|
output: &mut self.registered_lints,
|
||||||
cx,
|
cx,
|
||||||
};
|
};
|
||||||
let body_id = cx.tcx.hir().body_owned_by(
|
let body = cx.tcx.hir().body_owned_by(
|
||||||
impl_item_refs
|
impl_item_refs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|iiref| iiref.ident.as_str() == "get_lints")
|
.find(|iiref| iiref.ident.as_str() == "get_lints")
|
||||||
|
@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
||||||
.owner_id
|
.owner_id
|
||||||
.def_id,
|
.def_id,
|
||||||
);
|
);
|
||||||
collector.visit_expr(cx.tcx.hir().body(body_id).value);
|
collector.visit_expr(body.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -719,7 +719,7 @@ fn get_lint_group_and_level_or_lint(
|
||||||
Some(sym::clippy),
|
Some(sym::clippy),
|
||||||
&std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
|
&std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
|
||||||
);
|
);
|
||||||
if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
|
if let CheckLintNameResult::Tool(lint_lst, None) = result {
|
||||||
if let Some(group) = get_lint_group(cx, lint_lst[0]) {
|
if let Some(group) = get_lint_group(cx, lint_lst[0]) {
|
||||||
if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
|
if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::higher::VecArgs;
|
use clippy_utils::higher::VecArgs;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use rustc_ast::LitKind;
|
use rustc_ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{ExprKind, Node};
|
use rustc_hir::{ExprKind, Node};
|
||||||
|
@ -36,7 +36,7 @@ declare_clippy_lint! {
|
||||||
/// side_effect();
|
/// side_effect();
|
||||||
/// let a: [i32; 0] = [];
|
/// let a: [i32; 0] = [];
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.75.0"]
|
#[clippy::version = "1.79.0"]
|
||||||
pub ZERO_REPEAT_SIDE_EFFECTS,
|
pub ZERO_REPEAT_SIDE_EFFECTS,
|
||||||
suspicious,
|
suspicious,
|
||||||
"usage of zero-sized initializations of arrays or vecs causing side effects"
|
"usage of zero-sized initializations of arrays or vecs causing side effects"
|
||||||
|
@ -65,7 +65,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects {
|
||||||
|
|
||||||
fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) {
|
fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) {
|
||||||
// check if expr is a call or has a call inside it
|
// check if expr is a call or has a call inside it
|
||||||
if for_each_expr(inner_expr, |x| {
|
if for_each_expr_without_closures(inner_expr, |x| {
|
||||||
if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind {
|
if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind {
|
||||||
std::ops::ControlFlow::Break(())
|
std::ops::ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_utils"
|
name = "clippy_utils"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
|
@ -412,7 +412,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||||
/// Simple constant folding: Insert an expression, get a constant or none.
|
/// Simple constant folding: Insert an expression, get a constant or none.
|
||||||
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
|
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e),
|
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value),
|
||||||
|
ExprKind::DropTemps(e) => self.expr(e),
|
||||||
ExprKind::Path(ref qpath) => {
|
ExprKind::Path(ref qpath) => {
|
||||||
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
|
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
|
||||||
let result = mir_to_const(this.lcx, result)?;
|
let result = mir_to_const(this.lcx, result)?;
|
||||||
|
@ -490,7 +491,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||||
/// leaves the local crate.
|
/// leaves the local crate.
|
||||||
pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
|
pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr_is_empty(e),
|
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value),
|
||||||
|
ExprKind::DropTemps(e) => self.expr_is_empty(e),
|
||||||
ExprKind::Path(ref qpath) => {
|
ExprKind::Path(ref qpath) => {
|
||||||
if !self
|
if !self
|
||||||
.typeck_results
|
.typeck_results
|
||||||
|
@ -811,12 +813,8 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
|
||||||
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
|
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
|
||||||
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
|
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
|
||||||
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))),
|
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))),
|
||||||
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
|
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))),
|
||||||
int.try_into().expect("invalid f32 bit representation"),
|
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))),
|
||||||
))),
|
|
||||||
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
|
|
||||||
int.try_into().expect("invalid f64 bit representation"),
|
|
||||||
))),
|
|
||||||
ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))),
|
ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,9 +7,9 @@ use rustc_data_structures::fx::FxHasher;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::MatchSource::TryDesugar;
|
use rustc_hir::MatchSource::TryDesugar;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
ArrayLen, BinOpKind, BindingMode, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
|
ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy,
|
||||||
GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path,
|
GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField,
|
||||||
PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, AssocItemConstraint,
|
PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind,
|
||||||
};
|
};
|
||||||
use rustc_lexer::{tokenize, TokenKind};
|
use rustc_lexer::{tokenize, TokenKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -519,7 +519,11 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eq_assoc_type_binding(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool {
|
fn eq_assoc_type_binding(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool {
|
||||||
left.ident.name == right.ident.name && self.eq_ty(left.ty().expect("expected assoc type binding"), right.ty().expect("expected assoc type binding"))
|
left.ident.name == right.ident.name
|
||||||
|
&& self.eq_ty(
|
||||||
|
left.ty().expect("expected assoc type binding"),
|
||||||
|
right.ty().expect("expected assoc type binding"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
|
fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
|
||||||
|
|
|
@ -126,7 +126,7 @@ use visitors::Visitable;
|
||||||
use crate::consts::{constant, mir_to_const, Constant};
|
use crate::consts::{constant, mir_to_const, Constant};
|
||||||
use crate::higher::Range;
|
use crate::higher::Range;
|
||||||
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
||||||
use crate::visitors::for_each_expr;
|
use crate::visitors::for_each_expr_without_closures;
|
||||||
|
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
|
|
||||||
|
@ -321,6 +321,15 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
|
||||||
.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
|
.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the given method call expression calls an inherent method.
|
||||||
|
pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||||
|
cx.tcx.trait_of_item(method_id).is_none()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if a method is defined in an impl of a diagnostic item
|
/// Checks if a method is defined in an impl of a diagnostic item
|
||||||
pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
|
pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
|
||||||
if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
|
if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
|
||||||
|
@ -1313,7 +1322,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<
|
||||||
|
|
||||||
/// Returns `true` if `expr` contains a return expression
|
/// Returns `true` if `expr` contains a return expression
|
||||||
pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
|
pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
|
||||||
for_each_expr(expr, |e| {
|
for_each_expr_without_closures(expr, |e| {
|
||||||
if matches!(e.kind, ExprKind::Ret(..)) {
|
if matches!(e.kind, ExprKind::Ret(..)) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -3392,3 +3401,14 @@ pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
|
||||||
|
|
||||||
contains_block(expr, false)
|
contains_block(expr, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the specified expression is in a receiver position.
|
||||||
|
pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||||
|
&& let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
|
||||||
|
&& receiver.hir_id == expr.hir_id
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::similar_names)] // `expr` and `expn`
|
#![allow(clippy::similar_names)] // `expr` and `expn`
|
||||||
|
|
||||||
use crate::visitors::{for_each_expr, Descend};
|
use crate::visitors::{for_each_expr_without_closures, Descend};
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
|
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
|
||||||
|
@ -323,7 +323,7 @@ fn find_assert_args_inner<'a, const N: usize>(
|
||||||
Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
|
Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
|
||||||
};
|
};
|
||||||
let mut args = ArrayVec::new();
|
let mut args = ArrayVec::new();
|
||||||
let panic_expn = for_each_expr(expr, |e| {
|
let panic_expn = for_each_expr_without_closures(expr, |e| {
|
||||||
if args.is_full() {
|
if args.is_full() {
|
||||||
match PanicExpn::parse(e) {
|
match PanicExpn::parse(e) {
|
||||||
Some(expn) => ControlFlow::Break(expn),
|
Some(expn) => ControlFlow::Break(expn),
|
||||||
|
@ -349,7 +349,7 @@ fn find_assert_within_debug_assert<'a>(
|
||||||
expn: ExpnId,
|
expn: ExpnId,
|
||||||
assert_name: Symbol,
|
assert_name: Symbol,
|
||||||
) -> Option<(&'a Expr<'a>, ExpnId)> {
|
) -> Option<(&'a Expr<'a>, ExpnId)> {
|
||||||
for_each_expr(expr, |e| {
|
for_each_expr_without_closures(expr, |e| {
|
||||||
if !e.span.from_expansion() {
|
if !e.span.from_expansion() {
|
||||||
return ControlFlow::Continue(Descend::No);
|
return ControlFlow::Continue(Descend::No);
|
||||||
}
|
}
|
||||||
|
@ -397,7 +397,7 @@ impl FormatArgsStorage {
|
||||||
///
|
///
|
||||||
/// See also [`find_format_arg_expr`]
|
/// See also [`find_format_arg_expr`]
|
||||||
pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> {
|
pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> {
|
||||||
let format_args_expr = for_each_expr(start, |expr| {
|
let format_args_expr = for_each_expr_without_closures(start, |expr| {
|
||||||
let ctxt = expr.span.ctxt();
|
let ctxt = expr.span.ctxt();
|
||||||
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
||||||
if macro_backtrace(expr.span)
|
if macro_backtrace(expr.span)
|
||||||
|
@ -439,7 +439,7 @@ pub fn find_format_arg_expr<'hir, 'ast>(
|
||||||
parent: _,
|
parent: _,
|
||||||
} = target.expr.span.data();
|
} = target.expr.span.data();
|
||||||
|
|
||||||
for_each_expr(start, |expr| {
|
for_each_expr_without_closures(start, |expr| {
|
||||||
// When incremental compilation is enabled spans gain a parent during AST to HIR lowering,
|
// When incremental compilation is enabled spans gain a parent during AST to HIR lowering,
|
||||||
// since we're comparing an AST span to a HIR one we need to ignore the parent field
|
// since we're comparing an AST span to a HIR one we need to ignore the parent field
|
||||||
let data = expr.span.data();
|
let data = expr.span.data();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::source::snippet;
|
use crate::source::snippet;
|
||||||
use crate::visitors::{for_each_expr, Descend};
|
use crate::visitors::{for_each_expr_without_closures, Descend};
|
||||||
use crate::{path_to_local_id, strip_pat_refs};
|
use crate::{path_to_local_id, strip_pat_refs};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
|
use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
|
||||||
|
@ -31,7 +31,7 @@ fn extract_clone_suggestions<'tcx>(
|
||||||
body: &'tcx Body<'_>,
|
body: &'tcx Body<'_>,
|
||||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||||
let mut spans = Vec::new();
|
let mut spans = Vec::new();
|
||||||
for_each_expr(body, |e| {
|
for_each_expr_without_closures(body, |e| {
|
||||||
if let ExprKind::MethodCall(seg, recv, [], _) = e.kind
|
if let ExprKind::MethodCall(seg, recv, [], _) = e.kind
|
||||||
&& path_to_local_id(recv, id)
|
&& path_to_local_id(recv, id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,9 +40,13 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
for bb in &*body.basic_blocks {
|
for bb in &*body.basic_blocks {
|
||||||
check_terminator(tcx, body, bb.terminator(), msrv)?;
|
// Cleanup blocks are ignored entirely by const eval, so we can too:
|
||||||
for stmt in &bb.statements {
|
// https://github.com/rust-lang/rust/blob/1dea922ea6e74f99a0e97de5cdb8174e4dea0444/compiler/rustc_const_eval/src/transform/check_consts/check.rs#L382
|
||||||
check_statement(tcx, body, def_id, stmt, msrv)?;
|
if !bb.is_cleanup {
|
||||||
|
check_terminator(tcx, body, bb.terminator(), msrv)?;
|
||||||
|
for stmt in &bb.statements {
|
||||||
|
check_statement(tcx, body, def_id, stmt, msrv)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
|
use rustc_ast::{LitKind, StrStyle};
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
|
use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
|
||||||
|
@ -500,6 +501,50 @@ pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span {
|
||||||
extended.with_lo(extended.lo() - BytePos(1))
|
extended.with_lo(extended.lo() - BytePos(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts `expr` to a `char` literal if it's a `str` literal containing a single
|
||||||
|
/// character (or a single byte with `ascii_only`)
|
||||||
|
pub fn str_literal_to_char_literal(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
applicability: &mut Applicability,
|
||||||
|
ascii_only: bool,
|
||||||
|
) -> Option<String> {
|
||||||
|
if let ExprKind::Lit(lit) = &expr.kind
|
||||||
|
&& let LitKind::Str(r, style) = lit.node
|
||||||
|
&& let string = r.as_str()
|
||||||
|
&& let len = if ascii_only {
|
||||||
|
string.len()
|
||||||
|
} else {
|
||||||
|
string.chars().count()
|
||||||
|
}
|
||||||
|
&& len == 1
|
||||||
|
{
|
||||||
|
let snip = snippet_with_applicability(cx, expr.span, string, applicability);
|
||||||
|
let ch = if let StrStyle::Raw(nhash) = style {
|
||||||
|
let nhash = nhash as usize;
|
||||||
|
// for raw string: r##"a"##
|
||||||
|
&snip[(nhash + 2)..(snip.len() - 1 - nhash)]
|
||||||
|
} else {
|
||||||
|
// for regular string: "a"
|
||||||
|
&snip[1..(snip.len() - 1)]
|
||||||
|
};
|
||||||
|
|
||||||
|
let hint = format!(
|
||||||
|
"'{}'",
|
||||||
|
match ch {
|
||||||
|
"'" => "\\'",
|
||||||
|
r"\" => "\\\\",
|
||||||
|
"\\\"" => "\"", // no need to escape `"` in `'"'`
|
||||||
|
_ => ch,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(hint)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{reindent_multiline, without_block_comments};
|
use super::{reindent_multiline, without_block_comments};
|
||||||
|
|
|
@ -17,9 +17,9 @@ use rustc_middle::mir::ConstValue;
|
||||||
use rustc_middle::traits::EvaluationResult;
|
use rustc_middle::traits::EvaluationResult;
|
||||||
use rustc_middle::ty::layout::ValidityRequirement;
|
use rustc_middle::ty::layout::ValidityRequirement;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
|
self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind,
|
||||||
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
|
GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
|
||||||
TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
|
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
|
||||||
};
|
};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||||
|
@ -861,7 +861,6 @@ impl core::ops::Add<u32> for EnumValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to read the given constant as though it were an enum value.
|
/// Attempts to read the given constant as though it were an enum value.
|
||||||
#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
|
|
||||||
pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
|
pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
|
||||||
if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
|
if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
|
||||||
match tcx.type_of(id).instantiate_identity().kind() {
|
match tcx.type_of(id).instantiate_identity().kind() {
|
||||||
|
@ -1314,3 +1313,39 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>
|
||||||
pub fn is_manually_drop(ty: Ty<'_>) -> bool {
|
pub fn is_manually_drop(ty: Ty<'_>) -> bool {
|
||||||
ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop)
|
ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the deref chain of a type, starting with the type itself.
|
||||||
|
pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
|
||||||
|
iter::successors(Some(ty), |&ty| {
|
||||||
|
if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
|
||||||
|
&& implements_trait(cx, ty, deref_did, &[])
|
||||||
|
{
|
||||||
|
make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a Ty<'_> has some inherent method Symbol.
|
||||||
|
/// This does not look for impls in the type's `Deref::Target` type.
|
||||||
|
/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
|
||||||
|
pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {
|
||||||
|
if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) {
|
||||||
|
cx.tcx
|
||||||
|
.inherent_impls(ty_did)
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|&did| {
|
||||||
|
cx.tcx
|
||||||
|
.associated_items(did)
|
||||||
|
.filter_by_name_unhygienic(method_name)
|
||||||
|
.next()
|
||||||
|
.filter(|item| item.kind == AssocKind::Fn)
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.flatten()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -206,8 +206,18 @@ fn path_segment_certainty(
|
||||||
// Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE.
|
// Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE.
|
||||||
if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
|
if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
|
||||||
let generics = cx.tcx.generics_of(def_id);
|
let generics = cx.tcx.generics_of(def_id);
|
||||||
let count = generics.own_params.len() - usize::from(generics.host_effect_index.is_some());
|
|
||||||
let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 {
|
let own_count = generics.own_params.len()
|
||||||
|
- usize::from(generics.host_effect_index.is_some_and(|index| {
|
||||||
|
// Check that the host index actually belongs to this resolution.
|
||||||
|
// E.g. for `Add::add`, host_effect_index is `Some(2)`, but it's part of the parent `Add`
|
||||||
|
// trait's generics.
|
||||||
|
// Add params: [Self#0, Rhs#1, host#2] parent_count=0, count=3
|
||||||
|
// Add::add params: [] parent_count=3, count=3
|
||||||
|
// (3..3).contains(&host_effect_index) => false
|
||||||
|
(generics.parent_count..generics.count()).contains(&index)
|
||||||
|
}));
|
||||||
|
let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && own_count == 0 {
|
||||||
Certainty::Certain(None)
|
Certainty::Certain(None)
|
||||||
} else {
|
} else {
|
||||||
Certainty::Uncertain
|
Certainty::Uncertain
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend, Visitable};
|
use crate::visitors::{for_each_expr, for_each_expr_without_closures, Descend, Visitable};
|
||||||
use crate::{self as utils, get_enclosing_loop_or_multi_call_closure};
|
use crate::{self as utils, get_enclosing_loop_or_multi_call_closure};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use hir::def::Res;
|
use hir::def::Res;
|
||||||
|
@ -145,7 +145,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
||||||
for_each_expr(expression, |e| {
|
for_each_expr_without_closures(expression, |e| {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()),
|
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()),
|
||||||
// Something special could be done here to handle while or for loop
|
// Something special could be done here to handle while or for loop
|
||||||
|
@ -159,7 +159,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool {
|
pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool {
|
||||||
for_each_expr_with_closures(cx, v, |e| {
|
for_each_expr(cx, v, |e| {
|
||||||
if utils::path_to_local_id(e, local_id) {
|
if utils::path_to_local_id(e, local_id) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,7 +184,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr
|
||||||
let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id);
|
let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id);
|
||||||
|
|
||||||
let mut past_expr = false;
|
let mut past_expr = false;
|
||||||
for_each_expr_with_closures(cx, block, |e| {
|
for_each_expr(cx, block, |e| {
|
||||||
if past_expr {
|
if past_expr {
|
||||||
if utils::path_to_local_id(e, local_id) {
|
if utils::path_to_local_id(e, local_id) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
|
|
|
@ -100,7 +100,7 @@ visitable_ref!(Stmt, visit_stmt);
|
||||||
|
|
||||||
/// Calls the given function once for each expression contained. This does not enter any bodies or
|
/// Calls the given function once for each expression contained. This does not enter any bodies or
|
||||||
/// nested items.
|
/// nested items.
|
||||||
pub fn for_each_expr<'tcx, B, C: Continue>(
|
pub fn for_each_expr_without_closures<'tcx, B, C: Continue>(
|
||||||
node: impl Visitable<'tcx>,
|
node: impl Visitable<'tcx>,
|
||||||
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
|
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
|
||||||
) -> Option<B> {
|
) -> Option<B> {
|
||||||
|
@ -134,7 +134,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>(
|
||||||
|
|
||||||
/// Calls the given function once for each expression contained. This will enter bodies, but not
|
/// Calls the given function once for each expression contained. This will enter bodies, but not
|
||||||
/// nested items.
|
/// nested items.
|
||||||
pub fn for_each_expr_with_closures<'tcx, B, C: Continue>(
|
pub fn for_each_expr<'tcx, B, C: Continue>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
node: impl Visitable<'tcx>,
|
node: impl Visitable<'tcx>,
|
||||||
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
|
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
|
||||||
|
@ -181,7 +181,7 @@ pub fn for_each_expr_with_closures<'tcx, B, C: Continue>(
|
||||||
|
|
||||||
/// returns `true` if expr contains match expr desugared from try
|
/// returns `true` if expr contains match expr desugared from try
|
||||||
fn contains_try(expr: &Expr<'_>) -> bool {
|
fn contains_try(expr: &Expr<'_>) -> bool {
|
||||||
for_each_expr(expr, |e| {
|
for_each_expr_without_closures(expr, |e| {
|
||||||
if matches!(e.kind, ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) {
|
if matches!(e.kind, ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -286,7 +286,7 @@ where
|
||||||
|
|
||||||
/// Checks if the given resolved path is used in the given body.
|
/// Checks if the given resolved path is used in the given body.
|
||||||
pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
|
pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
|
||||||
for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| {
|
for_each_expr(cx, cx.tcx.hir().body(body).value, |e| {
|
||||||
if let ExprKind::Path(p) = &e.kind {
|
if let ExprKind::Path(p) = &e.kind {
|
||||||
if cx.qpath_res(p, e.hir_id) == res {
|
if cx.qpath_res(p, e.hir_id) == res {
|
||||||
return ControlFlow::Break(());
|
return ControlFlow::Break(());
|
||||||
|
@ -299,7 +299,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
|
||||||
|
|
||||||
/// Checks if the given local is used.
|
/// Checks if the given local is used.
|
||||||
pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
|
pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
|
||||||
for_each_expr_with_closures(cx, visitable, |e| {
|
for_each_expr(cx, visitable, |e| {
|
||||||
if path_to_local_id(e, id) {
|
if path_to_local_id(e, id) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -757,7 +757,7 @@ pub fn for_each_local_assignment<'tcx, B>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
|
pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
|
||||||
for_each_expr(expr, |e| {
|
for_each_expr_without_closures(expr, |e| {
|
||||||
if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
|
if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -776,7 +776,7 @@ pub fn local_used_once<'tcx>(
|
||||||
) -> Option<&'tcx Expr<'tcx>> {
|
) -> Option<&'tcx Expr<'tcx>> {
|
||||||
let mut expr = None;
|
let mut expr = None;
|
||||||
|
|
||||||
let cf = for_each_expr_with_closures(cx, visitable, |e| {
|
let cf = for_each_expr(cx, visitable, |e| {
|
||||||
if path_to_local_id(e, id) && expr.replace(e).is_some() {
|
if path_to_local_id(e, id) && expr.replace(e).is_some() {
|
||||||
ControlFlow::Break(())
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "declare_clippy_lint"
|
name = "declare_clippy_lint"
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
## `cargo lintcheck`
|
## `cargo lintcheck`
|
||||||
|
|
||||||
Runs clippy on a fixed set of crates read from
|
Runs Clippy on a fixed set of crates read from
|
||||||
`lintcheck/lintcheck_crates.toml` and saves logs of the lint warnings into the
|
`lintcheck/lintcheck_crates.toml` and saves logs of the lint warnings into the
|
||||||
repo. We can then check the diff and spot new or disappearing warnings.
|
repo. We can then check the diff and spot new or disappearing warnings.
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ This lets us spot bad suggestions or false positives automatically in some cases
|
||||||
|
|
||||||
> Note: Fix mode implies `--all-targets`, so it can fix as much code as it can.
|
> Note: Fix mode implies `--all-targets`, so it can fix as much code as it can.
|
||||||
|
|
||||||
Please note that the target dir should be cleaned afterwards since clippy will modify
|
Please note that the target dir should be cleaned afterwards since Clippy will modify
|
||||||
the downloaded sources which can lead to unexpected results when running lintcheck again afterwards.
|
the downloaded sources which can lead to unexpected results when running lintcheck again afterwards.
|
||||||
|
|
||||||
### Recursive mode
|
### Recursive mode
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue