Auto merge of #112203 - flip1995:clippyup, r=Manishearth

Update Clippy

r? `@Manishearth`
This commit is contained in:
bors 2023-06-02 10:55:54 +00:00
commit 0939ec13d8
115 changed files with 3032 additions and 665 deletions

View File

@ -294,7 +294,7 @@ dependencies = [
"serde_json", "serde_json",
"sha2", "sha2",
"tar", "tar",
"toml", "toml 0.5.7",
"xz2", "xz2",
] ]
@ -311,7 +311,7 @@ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
"serde_json", "serde_json",
"toml", "toml 0.5.7",
] ]
[[package]] [[package]]
@ -581,7 +581,7 @@ checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
[[package]] [[package]]
name = "clippy" name = "clippy"
version = "0.1.71" version = "0.1.72"
dependencies = [ dependencies = [
"clap 4.2.1", "clap 4.2.1",
"clippy_lints", "clippy_lints",
@ -596,7 +596,6 @@ dependencies = [
"quote", "quote",
"regex", "regex",
"rustc-semver", "rustc-semver",
"rustc-workspace-hack",
"rustc_tools_util", "rustc_tools_util",
"serde", "serde",
"syn 2.0.8", "syn 2.0.8",
@ -604,7 +603,7 @@ dependencies = [
"termize", "termize",
"tester", "tester",
"tokio", "tokio",
"toml", "toml 0.7.4",
"walkdir", "walkdir",
] ]
@ -623,7 +622,7 @@ dependencies = [
[[package]] [[package]]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.71" version = "0.1.72"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"cargo_metadata 0.15.3", "cargo_metadata 0.15.3",
@ -639,7 +638,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"tempfile", "tempfile",
"toml", "toml 0.7.4",
"unicode-normalization", "unicode-normalization",
"unicode-script", "unicode-script",
"url", "url",
@ -647,7 +646,7 @@ dependencies = [
[[package]] [[package]]
name = "clippy_utils" name = "clippy_utils"
version = "0.1.71" version = "0.1.72"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"if_chain", "if_chain",
@ -930,7 +929,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
[[package]] [[package]]
name = "declare_clippy_lint" name = "declare_clippy_lint"
version = "0.1.71" version = "0.1.72"
dependencies = [ dependencies = [
"itertools", "itertools",
"quote", "quote",
@ -2111,7 +2110,7 @@ dependencies = [
"serde_json", "serde_json",
"shlex", "shlex",
"tempfile", "tempfile",
"toml", "toml 0.5.7",
"topological-sort", "topological-sort",
] ]
@ -4363,7 +4362,7 @@ dependencies = [
"serde_json", "serde_json",
"term", "term",
"thiserror", "thiserror",
"toml", "toml 0.5.7",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
"unicode_categories", "unicode_categories",
@ -4484,6 +4483,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.5" version = "0.10.5"
@ -4995,6 +5003,40 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "toml"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]] [[package]]
name = "topological-sort" name = "topological-sort"
version = "0.2.2" version = "0.2.2"
@ -5358,7 +5400,6 @@ dependencies = [
"idna", "idna",
"matches", "matches",
"percent-encoding", "percent-encoding",
"serde",
] ]
[[package]] [[package]]
@ -5685,6 +5726,15 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "writeable" name = "writeable"
version = "0.5.1" version = "0.5.1"

View File

@ -12,29 +12,6 @@ body:
description: What does this lint do? description: What does this lint do?
validations: validations:
required: true required: true
- type: input
id: lint-name
attributes:
label: Lint Name
description: Please provide the lint name.
- type: dropdown
id: category
attributes:
label: Category
description: >
What category should this lint go into? If you're unsure you can select
multiple categories. You can find a category description in the
`README`.
multiple: true
options:
- correctness
- suspicious
- style
- complexity
- perf
- pedantic
- restriction
- cargo
- type: textarea - type: textarea
id: advantage id: advantage
attributes: attributes:

View File

@ -6,7 +6,132 @@ document.
## Unreleased / Beta / In Rust Nightly ## Unreleased / Beta / In Rust Nightly
[149392b0...master](https://github.com/rust-lang/rust-clippy/compare/149392b0...master) [83e42a23...master](https://github.com/rust-lang/rust-clippy/compare/83e42a23...master)
## Rust 1.70
Current beta, released 2023-06-01
[149392b0...83e42a23](https://github.com/rust-lang/rust-clippy/compare/149392b0...83e42a23)
### New Lints
* [`large_futures`]
[#10414](https://github.com/rust-lang/rust-clippy/pull/10414)
* [`missing_assert_message`]
[#10362](https://github.com/rust-lang/rust-clippy/pull/10362)
* [`clear_with_drain`]
[#10528](https://github.com/rust-lang/rust-clippy/pull/10528)
* [`redundant_async_block`]
[#10448](https://github.com/rust-lang/rust-clippy/pull/10448)
* [`collection_is_never_read`]
[#10415](https://github.com/rust-lang/rust-clippy/pull/10415)
* [`let_with_type_underscore`]
[#10467](https://github.com/rust-lang/rust-clippy/pull/10467)
* [`tests_outside_test_module`]
[#10543](https://github.com/rust-lang/rust-clippy/pull/10543)
* [`allow_attributes`]
[#10481](https://github.com/rust-lang/rust-clippy/pull/10481)
* [`suspicious_doc_comments`]
[#10497](https://github.com/rust-lang/rust-clippy/pull/10497)
* [`unnecessary_box_returns`]
[#9102](https://github.com/rust-lang/rust-clippy/pull/9102)
* [`manual_main_separator_str`]
[#10483](https://github.com/rust-lang/rust-clippy/pull/10483)
* [`unnecessary_struct_initialization`]
[#10489](https://github.com/rust-lang/rust-clippy/pull/10489)
* [`manual_slice_size_calculation`]
[#10601](https://github.com/rust-lang/rust-clippy/pull/10601)
* [`lines_filter_map_ok`]
[#10534](https://github.com/rust-lang/rust-clippy/pull/10534)
### Moves and Deprecations
* Moved [`let_underscore_untyped`] to `restriction`
[#10442](https://github.com/rust-lang/rust-clippy/pull/10442)
### Enhancements
* [`extra_unused_type_parameters`]: No longer lints on public items if `avoid-breaking-exported-api` is set
[#10536](https://github.com/rust-lang/rust-clippy/pull/10536)
* [`len_without_is_empty`]: Now also detects `async` functions
[#10359](https://github.com/rust-lang/rust-clippy/pull/10359)
* [`arithmetic_side_effects`]: Now correctly handles divisions and modulo expressions if the right-hand-side
is unknown
[#10585](https://github.com/rust-lang/rust-clippy/pull/10585)
* [`nonminimal_bool`]: No longer ignores `#[allow]` attributes
[#10588](https://github.com/rust-lang/rust-clippy/pull/10588)
* [`uninit_vec`], [`uninit_assumed_init`]: Now uses a better heuristic
[#10520](https://github.com/rust-lang/rust-clippy/pull/10520)
* [`ifs_same_cond`]: Now also detects immutable method calls.
[#10350](https://github.com/rust-lang/rust-clippy/pull/10350)
* [`arithmetic_side_effects`]: No longer lints on right or left shifts with constant integers, as the
compiler warns about them
[#10309](https://github.com/rust-lang/rust-clippy/pull/10309)
* [`items_after_statements`]: `#[allow(items_after_statements)]` now works on items
[#10542](https://github.com/rust-lang/rust-clippy/pull/10542)
* [`significant_drop_tightening`]: Was optimized
[#10533](https://github.com/rust-lang/rust-clippy/pull/10533)
### False Positive Fixes
* [`single_component_path_imports`]: No longer lints if the import is used relative to `self`
[#10566](https://github.com/rust-lang/rust-clippy/pull/10566)
* [`derivable_impls`]: No longer suggests deriving `Default` on generics with implicit arguments
[#10399](https://github.com/rust-lang/rust-clippy/pull/10399)
* [`let_unit_value`]: No longer lints if the expression contains an `await`
[#10439](https://github.com/rust-lang/rust-clippy/pull/10439)
* [`double_must_use`]: Now ignores `async` functions
[#10589](https://github.com/rust-lang/rust-clippy/pull/10589)
* [`manual_clamp`]: No longer lints in constant context
[#10479](https://github.com/rust-lang/rust-clippy/pull/10479)
* [`almost_swapped`]: Now ignores external macros
[#10502](https://github.com/rust-lang/rust-clippy/pull/10502)
* [`nonminimal_bool`]: Now ignores macros
[#10527](https://github.com/rust-lang/rust-clippy/pull/10527)
* [`needless_return`]: No longer lints match statements with incompatible branches
[#10593](https://github.com/rust-lang/rust-clippy/pull/10593)
* [`use_self`]: Do not suggest using `Self` in const generic parameters
[#10375](https://github.com/rust-lang/rust-clippy/pull/10375)
* [`mem_replace_option_with_none`]: No longer lints on field expressions
[#10594](https://github.com/rust-lang/rust-clippy/pull/10594)
* [`items_after_statements`]: No longer lints on times from macros
[#10542](https://github.com/rust-lang/rust-clippy/pull/10542)
* [`print_literal`], [`write_literal`]: No longer lint strings coming from the `file!()` macro
[#10573](https://github.com/rust-lang/rust-clippy/pull/10573)
* [`uninit_vec`], [`uninit_assumed_init`]: Now check the types inside arrays and tuples
[#10553](https://github.com/rust-lang/rust-clippy/pull/10553)
* [`almost_swapped`]: No longer lints if a variable is assigned to itself
[#10499](https://github.com/rust-lang/rust-clippy/pull/10499)
* [`missing_docs_in_private_items`]: No longer lints on public items
[#10324](https://github.com/rust-lang/rust-clippy/pull/10324)
### Suggestion Fixes/Improvements
* [`extra_unused_type_parameters`]: The suggestion is now machine applicable
[#10536](https://github.com/rust-lang/rust-clippy/pull/10536)
* [`match_single_binding`]: Now adds a semicolon after the suggestion
[#10470](https://github.com/rust-lang/rust-clippy/pull/10470)
* [`missing_const_for_fn`]: Now includes a note if the change could break compatibility
[#10618](https://github.com/rust-lang/rust-clippy/pull/10618)
* [`cast_possible_truncation`]: Corrected suggestion for float and wildcard casts
[#10496](https://github.com/rust-lang/rust-clippy/pull/10496)
* [`transmutes_expressible_as_ptr_casts`]: The suggestion now includes parentheses when they are required
[#10454](https://github.com/rust-lang/rust-clippy/pull/10454)
### ICE Fixes
* [`needless_borrow`]: No longer panics on ambiguous projections
[#10403](https://github.com/rust-lang/rust-clippy/pull/10403)
* [`multiple_unsafe_ops_per_block`]: Fix ICE when calling a function-like object in an unsafe block
[#10405](https://github.com/rust-lang/rust-clippy/pull/10405)
### Others
* `clippy-driver` now searches parent directories for `clippy.toml` files
[#10592](https://github.com/rust-lang/rust-clippy/pull/10592)
* Fixed a deserialization error for the `array-size-threshold` config value
[#10423](https://github.com/rust-lang/rust-clippy/pull/10423)
## Rust 1.69 ## Rust 1.69
@ -4838,6 +4963,7 @@ Released 2018-09-13
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
[`missing_fields_in_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_fields_in_debug
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
@ -4874,6 +5000,7 @@ Released 2018-09-13
[`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
[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
[`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
@ -4949,6 +5076,7 @@ Released 2018-09-13
[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr [`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
[`ptr_cast_constness`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.1.71" version = "0.1.72"
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"
@ -30,16 +30,11 @@ termize = "0.1"
compiletest_rs = { version = "0.10", features = ["tmp"] } compiletest_rs = { version = "0.10", features = ["tmp"] }
tester = "0.9" tester = "0.9"
regex = "1.5" regex = "1.5"
toml = "0.5" toml = "0.7.3"
walkdir = "2.3" walkdir = "2.3"
# This is used by the `collect-metadata` alias. # This is used by the `collect-metadata` alias.
filetime = "0.2" filetime = "0.2"
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
# for more information.
rustc-workspace-hack = "1.0"
# UI test dependencies # UI test dependencies
clap = { version = "4.1.4", features = ["derive"] } clap = { version = "4.1.4", features = ["derive"] }
clippy_utils = { path = "clippy_utils" } clippy_utils = { path = "clippy_utils" }

View File

@ -2,8 +2,14 @@
> **Note:** The configuration file is unstable and may be deprecated in the future. > **Note:** The configuration file is unstable and may be deprecated in the future.
Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for in:
basic `variable = value` mapping e.g.
1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or
2. The directory specified by the
[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or
3. The current directory.
It contains a basic `variable = value` mapping e.g.
```toml ```toml
avoid-breaking-exported-api = false avoid-breaking-exported-api = false

View File

@ -630,8 +630,14 @@ Before submitting your PR make sure you followed all the basic requirements:
## Adding configuration to a lint ## Adding configuration to a lint
Clippy supports the configuration of lints values using a `clippy.toml` file in Clippy supports the configuration of lints values using a `clippy.toml` file which is searched for in:
the workspace directory. Adding a configuration to a lint can be useful for
1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or
2. The directory specified by the
[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or
3. The current directory.
Adding a configuration to a lint can be useful for
thresholds or to constrain some behavior that can be seen as a false positive thresholds or to constrain some behavior that can be seen as a false positive
for some users. Adding a configuration is done in the following steps: for some users. Adding a configuration is done in the following steps:

View File

@ -5,6 +5,7 @@
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
use indoc::indoc; use indoc::indoc;
use std::convert::Infallible;
fn main() { fn main() {
let matches = get_clap_config(); let matches = get_clap_config();
@ -180,7 +181,8 @@ fn get_clap_config() -> ArgMatches {
.short('n') .short('n')
.long("name") .long("name")
.help("Name of the new lint in snake case, ex: fn_too_long") .help("Name of the new lint in snake case, ex: fn_too_long")
.required(true), .required(true)
.value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))),
Arg::new("category") Arg::new("category")
.short('c') .short('c')
.long("category") .long("category")

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.71" version = "0.1.72"
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"
@ -20,15 +20,13 @@ quine-mc_cluskey = "0.2"
regex-syntax = "0.7" regex-syntax = "0.7"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true }
tempfile = { version = "3.2", optional = true } tempfile = { version = "3.3.0", optional = true }
toml = "0.5" toml = "0.7.3"
unicode-normalization = "0.1" unicode-normalization = "0.1"
unicode-script = { version = "0.5", default-features = false } unicode-script = { version = "0.5", default-features = false }
semver = "1.0" semver = "1.0"
rustc-semver = "1.1" rustc-semver = "1.1"
# NOTE: cargo requires serde feat in its url dep url = "2.2"
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
url = { version = "2.2", features = ["serde"] }
[features] [features]
deny-warnings = ["clippy_utils/deny-warnings"] deny-warnings = ["clippy_utils/deny-warnings"]

View File

@ -40,7 +40,7 @@ declare_clippy_lint! {
/// a.len() /// a.len()
/// } /// }
/// ``` /// ```
#[clippy::version = "1.69.0"] #[clippy::version = "1.70.0"]
pub ALLOW_ATTRIBUTES, pub ALLOW_ATTRIBUTES,
restriction, restriction,
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."

View File

@ -88,7 +88,6 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
NonminimalBoolVisitor { cx }.visit_body(body); NonminimalBoolVisitor { cx }.visit_body(body);
} }
} }
struct NonminimalBoolVisitor<'a, 'tcx> { struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
} }
@ -473,6 +472,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
self.bool_expr(e); self.bool_expr(e);
}, },
ExprKind::Unary(UnOp::Not, inner) => { ExprKind::Unary(UnOp::Not, inner) => {
if let ExprKind::Unary(UnOp::Not, ex) = inner.kind &&
!self.cx.typeck_results().node_types()[ex.hir_id].is_bool() {
return;
}
if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() { if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
self.bool_expr(e); self.bool_expr(e);
} }

View File

@ -17,6 +17,7 @@ mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_any;
mod fn_to_numeric_cast_with_truncation; mod fn_to_numeric_cast_with_truncation;
mod ptr_as_ptr; mod ptr_as_ptr;
mod ptr_cast_constness;
mod unnecessary_cast; mod unnecessary_cast;
mod utils; mod utils;
@ -363,7 +364,7 @@ declare_clippy_lint! {
/// namely `*const T` to `*const U` and `*mut T` to `*mut U`. /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because /// Though `as` casts between raw pointers are not terrible, `pointer::cast` is safer because
/// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
/// ///
/// ### Example /// ### Example
@ -386,6 +387,34 @@ declare_clippy_lint! {
"casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for `as` casts between raw pointers which change its constness, namely `*const T` to
/// `*mut T` and `*mut T` to `*const T`.
///
/// ### Why is this bad?
/// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and
/// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another
/// type.
///
/// ### Example
/// ```rust
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr = ptr as *mut u32;
/// let ptr = mut_ptr as *const u32;
/// ```
/// Use instead:
/// ```rust
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr = ptr.cast_mut();
/// let ptr = mut_ptr.cast_const();
/// ```
#[clippy::version = "1.71.0"]
pub PTR_CAST_CONSTNESS,
pedantic,
"casting using `as` from and to raw pointers to change constness when specialized methods apply"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for casts from an enum type to an integral type which will definitely truncate the /// Checks for casts from an enum type to an integral type which will definitely truncate the
@ -652,6 +681,7 @@ impl_lint_pass!(Casts => [
FN_TO_NUMERIC_CAST_WITH_TRUNCATION, FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
CHAR_LIT_AS_U8, CHAR_LIT_AS_U8,
PTR_AS_PTR, PTR_AS_PTR,
PTR_CAST_CONSTNESS,
CAST_ENUM_TRUNCATION, CAST_ENUM_TRUNCATION,
CAST_ENUM_CONSTRUCTOR, CAST_ENUM_CONSTRUCTOR,
CAST_ABS_TO_UNSIGNED, CAST_ABS_TO_UNSIGNED,
@ -685,6 +715,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
return; return;
} }
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);

View File

@ -0,0 +1,44 @@
use clippy_utils::msrvs::POINTER_CAST_CONSTNESS;
use clippy_utils::sugg::Sugg;
use clippy_utils::{diagnostics::span_lint_and_sugg, msrvs::Msrv};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty, TypeAndMut};
use super::PTR_CAST_CONSTNESS;
pub(super) fn check(
cx: &LateContext<'_>,
expr: &Expr<'_>,
cast_expr: &Expr<'_>,
cast_from: Ty<'_>,
cast_to: Ty<'_>,
msrv: &Msrv,
) {
if_chain! {
if msrv.meets(POINTER_CAST_CONSTNESS);
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind();
if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind();
if matches!((from_mutbl, to_mutbl),
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not));
then {
let sugg = Sugg::hir(cx, cast_expr, "_");
let constness = match *to_mutbl {
Mutability::Not => "const",
Mutability::Mut => "mut",
};
span_lint_and_sugg(
cx,
PTR_CAST_CONSTNESS,
expr.span,
"`as` casting between raw pointers while changing its constness",
&format!("try `pointer::cast_{constness}`, a safer alternative"),
format!("{}.cast_{constness}()", sugg.maybe_par()),
Applicability::MachineApplicable,
);
}
}
}

View File

@ -37,7 +37,7 @@ declare_clippy_lint! {
/// println!("{sample}"); /// println!("{sample}");
/// } /// }
/// ``` /// ```
#[clippy::version = "1.69.0"] #[clippy::version = "1.70.0"]
pub COLLECTION_IS_NEVER_READ, pub COLLECTION_IS_NEVER_READ,
nursery, nursery,
"a collection is never queried" "a collection is never queried"

View File

@ -89,6 +89,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_AS_PTR_INFO,
crate::casts::PTR_CAST_CONSTNESS_INFO,
crate::casts::UNNECESSARY_CAST_INFO, crate::casts::UNNECESSARY_CAST_INFO,
crate::checked_conversions::CHECKED_CONVERSIONS_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
@ -427,6 +428,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO,
crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO, crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO,
crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO, crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO,
crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO, crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO,
@ -447,6 +449,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
crate::needless_continue::NEEDLESS_CONTINUE_INFO, crate::needless_continue::NEEDLESS_CONTINUE_INFO,
crate::needless_else::NEEDLESS_ELSE_INFO,
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_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,

View File

@ -1,4 +1,4 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, match_def_path, paths}; use clippy_utils::{diagnostics::span_lint_and_sugg, is_ty_alias, match_def_path, paths};
use hir::{def::Res, ExprKind}; use hir::{def::Res, ExprKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
@ -43,12 +43,23 @@ declare_clippy_lint! {
} }
declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]); declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]);
fn is_alias(ty: hir::Ty<'_>) -> bool {
if let hir::TyKind::Path(ref qpath) = ty.kind {
is_ty_alias(qpath)
} else {
false
}
}
impl LateLintPass<'_> for DefaultConstructedUnitStructs { impl LateLintPass<'_> for DefaultConstructedUnitStructs {
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if_chain!( if_chain!(
// make sure we have a call to `Default::default` // make sure we have a call to `Default::default`
if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind; if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind;
if let ExprKind::Path(ref qpath@ hir::QPath::TypeRelative(_,_)) = fn_expr.kind; if let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind;
// make sure this isn't a type alias:
// `<Foo as Bar>::Assoc` cannot be used as a constructor
if !is_alias(*base);
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
// make sure we have a struct with no fields (unit struct) // make sure we have a struct with no fields (unit struct)

View File

@ -38,7 +38,7 @@ declare_clippy_lint! {
/// wait(fut).await; /// wait(fut).await;
/// } /// }
/// ``` /// ```
#[clippy::version = "1.68.0"] #[clippy::version = "1.70.0"]
pub LARGE_FUTURES, pub LARGE_FUTURES,
pedantic, pedantic,
"large future may lead to unexpected stack overflows" "large future may lead to unexpected stack overflows"

View File

@ -38,7 +38,7 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Repeat(_, _) = expr.kind if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)

View File

@ -17,7 +17,7 @@ declare_clippy_lint! {
/// ```rust,ignore /// ```rust,ignore
/// let my_number = 1; /// let my_number = 1;
/// ``` /// ```
#[clippy::version = "1.69.0"] #[clippy::version = "1.70.0"]
pub LET_WITH_TYPE_UNDERSCORE, pub LET_WITH_TYPE_UNDERSCORE,
complexity, complexity,
"unneeded underscore type (`_`) in a variable declaration" "unneeded underscore type (`_`) in a variable declaration"

View File

@ -202,6 +202,7 @@ mod missing_assert_message;
mod missing_const_for_fn; mod missing_const_for_fn;
mod missing_doc; mod missing_doc;
mod missing_enforced_import_rename; mod missing_enforced_import_rename;
mod missing_fields_in_debug;
mod missing_inline; mod missing_inline;
mod missing_trait_methods; mod missing_trait_methods;
mod mixed_read_write_in_expression; mod mixed_read_write_in_expression;
@ -217,6 +218,7 @@ mod needless_arbitrary_self_type;
mod needless_bool; mod needless_bool;
mod needless_borrowed_ref; mod needless_borrowed_ref;
mod needless_continue; mod needless_continue;
mod needless_else;
mod needless_for_each; mod needless_for_each;
mod needless_late_init; mod needless_late_init;
mod needless_parens_on_range_literals; mod needless_parens_on_range_literals;
@ -333,7 +335,7 @@ mod zero_sized_map_values;
pub use crate::utils::conf::{lookup_conf_file, Conf}; pub use crate::utils::conf::{lookup_conf_file, Conf};
use crate::utils::{ use crate::utils::{
conf::{format_error, metadata::get_configuration_metadata, TryConf}, conf::{metadata::get_configuration_metadata, TryConf},
FindAll, FindAll,
}; };
@ -369,23 +371,36 @@ pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>
}, },
}; };
let TryConf { conf, errors, warnings } = utils::conf::read(file_name); let TryConf { conf, errors, warnings } = utils::conf::read(sess, file_name);
// all conf errors are non-fatal, we just use the default conf in case of error // all conf errors are non-fatal, we just use the default conf in case of error
for error in errors { for error in errors {
sess.err(format!( if let Some(span) = error.span {
"error reading Clippy's configuration file `{}`: {}", sess.span_err(
file_name.display(), span,
format_error(error) format!("error reading Clippy's configuration file: {}", error.message),
)); );
} else {
sess.err(format!(
"error reading Clippy's configuration file `{}`: {}",
file_name.display(),
error.message
));
}
} }
for warning in warnings { for warning in warnings {
sess.struct_warn(format!( if let Some(span) = warning.span {
"error reading Clippy's configuration file `{}`: {}", sess.span_warn(
file_name.display(), span,
format_error(warning) format!("error reading Clippy's configuration file: {}", warning.message),
)) );
.emit(); } else {
sess.warn(format!(
"error reading Clippy's configuration file `{}`: {}",
file_name.display(),
warning.message
));
}
} }
conf conf
@ -990,6 +1005,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns));
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
// 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`
} }

View File

@ -77,53 +77,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
local.els.is_none() && local.els.is_none() &&
local.ty.is_none() && local.ty.is_none() &&
init.span.ctxt() == stmt.span.ctxt() && init.span.ctxt() == stmt.span.ctxt() &&
let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) { let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
match if_let_or_match { {
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { match if_let_or_match {
if expr_is_simple_identity(let_pat, if_then); IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! {
if let Some(if_else) = if_else; if expr_is_simple_identity(let_pat, if_then);
if expr_diverges(cx, if_else); if let Some(if_else) = if_else;
then { if expr_diverges(cx, if_else);
emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else); then {
} emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else);
}, }
IfLetOrMatch::Match(match_expr, arms, source) => { },
if self.matches_behaviour == MatchLintBehaviour::Never { IfLetOrMatch::Match(match_expr, arms, source) => {
return; if self.matches_behaviour == MatchLintBehaviour::Never {
} return;
if source != MatchSource::Normal { }
return; if source != MatchSource::Normal {
} return;
// Any other number than two arms doesn't (necessarily) }
// have a trivial mapping to let else. // Any other number than two arms doesn't (necessarily)
if arms.len() != 2 { // have a trivial mapping to let else.
return; if arms.len() != 2 {
} return;
// Guards don't give us an easy mapping either }
if arms.iter().any(|arm| arm.guard.is_some()) { // Guards don't give us an easy mapping either
return; if arms.iter().any(|arm| arm.guard.is_some()) {
} return;
let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; }
let diverging_arm_opt = arms let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
.iter() let diverging_arm_opt = arms
.enumerate() .iter()
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); .enumerate()
let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
// If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. let Some((idx, diverging_arm)) = diverging_arm_opt else { return; };
// However, if it arrives in second position, its pattern may cover some cases already covered // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
// by the diverging one. // However, if it arrives in second position, its pattern may cover some cases already covered
// TODO: accept the non-diverging arm as a second position if patterns are disjointed. // by the diverging one.
if idx == 0 { // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
return; if idx == 0 {
} return;
let pat_arm = &arms[1 - idx]; }
if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { let pat_arm = &arms[1 - idx];
return; if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) {
} return;
}
emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body); emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body);
}, },
} }
}; };
} }
@ -145,10 +146,9 @@ fn emit_manual_let_else(
"this could be rewritten as `let...else`", "this could be rewritten as `let...else`",
|diag| { |diag| {
// This is far from perfect, for example there needs to be: // This is far from perfect, for example there needs to be:
// * mut additions for the bindings // * tracking for multi-binding cases: let (foo, bar) = if let (Some(foo), Ok(bar)) = ...
// * renamings of the bindings for `PatKind::Or` // * renamings of the bindings for many `PatKind`s like structs, slices, etc.
// * unused binding collision detection with existing ones // * unused binding collision detection with existing ones
// * putting patterns with at the top level | inside ()
// for this to be machine applicable. // for this to be machine applicable.
let mut app = Applicability::HasPlaceholders; let mut app = Applicability::HasPlaceholders;
let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
@ -159,28 +159,62 @@ fn emit_manual_let_else(
} else { } else {
format!("{{ {sn_else} }}") format!("{{ {sn_else} }}")
}; };
let sn_bl = match pat.kind { let sn_bl = replace_in_pattern(cx, span, local, pat, &mut app);
PatKind::Or(..) => {
let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
format!("({sn_pat})")
},
// Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
PatKind::TupleStruct(ref w, args, ..) if args.len() == 1 => {
let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default();
let (sn_inner, _) = snippet_with_context(cx, local.span, span.ctxt(), "", &mut app);
format!("{sn_wrapper}({sn_inner})")
},
_ => {
let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
sn_pat.into_owned()
},
};
let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};");
diag.span_suggestion(span, "consider writing", sugg, app); diag.span_suggestion(span, "consider writing", sugg, app);
}, },
); );
} }
// replaces the locals in the pattern
fn replace_in_pattern(
cx: &LateContext<'_>,
span: Span,
local: &Pat<'_>,
pat: &Pat<'_>,
app: &mut Applicability,
) -> String {
let mut bindings_count = 0;
pat.each_binding_or_first(&mut |_, _, _, _| bindings_count += 1);
// If the pattern creates multiple bindings, exit early,
// as otherwise we might paste the pattern to the positions of multiple bindings.
if bindings_count > 1 {
let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app);
return sn_pat.into_owned();
}
match pat.kind {
PatKind::Binding(..) => {
let (sn_bdg, _) = snippet_with_context(cx, local.span, span.ctxt(), "", app);
return sn_bdg.to_string();
},
PatKind::Or(pats) => {
let patterns = pats
.iter()
.map(|pat| replace_in_pattern(cx, span, local, pat, app))
.collect::<Vec<_>>();
let or_pat = patterns.join(" | ");
return format!("({or_pat})");
},
// Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
PatKind::TupleStruct(ref w, args, dot_dot_pos) => {
let mut args = args
.iter()
.map(|pat| replace_in_pattern(cx, span, local, pat, app))
.collect::<Vec<_>>();
if let Some(pos) = dot_dot_pos.as_opt_usize() {
args.insert(pos, "..".to_owned());
}
let args = args.join(", ");
let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default();
return format!("{sn_wrapper}({args})");
},
_ => {},
}
let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app);
sn_pat.into_owned()
}
/// Check whether an expression is divergent. May give false negatives. /// Check whether an expression is divergent. May give false negatives.
fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
struct V<'cx, 'tcx> { struct V<'cx, 'tcx> {

View File

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

View File

@ -25,7 +25,7 @@ mod wild_in_or_pats;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet_opt, walk_span_to_context}; use clippy_utils::source::{snippet_opt, walk_span_to_context};
use clippy_utils::{higher, in_constant, is_span_match, tokenize_with_text}; use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, tokenize_with_text};
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
use rustc_lexer::TokenKind; use rustc_lexer::TokenKind;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -974,12 +974,16 @@ impl_lint_pass!(Matches => [
impl<'tcx> LateLintPass<'tcx> for Matches { impl<'tcx> LateLintPass<'tcx> for Matches {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) { if is_direct_expn_of(expr.span, "matches").is_none() && in_external_macro(cx.sess(), expr.span) {
return; return;
} }
let from_expansion = expr.span.from_expansion(); let from_expansion = expr.span.from_expansion();
if let ExprKind::Match(ex, arms, source) = expr.kind { if let ExprKind::Match(ex, arms, source) = expr.kind {
if is_direct_expn_of(expr.span, "matches").is_some() {
redundant_pattern_match::check_match(cx, expr, ex, arms);
}
if source == MatchSource::Normal && !is_span_match(cx, expr.span) { if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
return; return;
} }

View File

@ -1,10 +1,10 @@
use super::REDUNDANT_PATTERN_MATCHING; use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then; 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::Sugg; use clippy_utils::sugg::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; use clippy_utils::visitors::any_temporaries_need_ordered_drop;
use clippy_utils::{higher, is_trait_method}; use clippy_utils::{higher, is_expn_of, is_trait_method};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -190,24 +190,19 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
if let Some(good_method) = found_good_method(cx, arms, node_pair) { if let Some(good_method) = found_good_method(cx, arms, node_pair) {
let span = expr.span.to(op.span); let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span));
let result_expr = match &op.kind { let result_expr = match &op.kind {
ExprKind::AddrOf(_, _, borrowed) => borrowed, ExprKind::AddrOf(_, _, borrowed) => borrowed,
_ => op, _ => op,
}; };
span_lint_and_then( span_lint_and_sugg(
cx, cx,
REDUNDANT_PATTERN_MATCHING, REDUNDANT_PATTERN_MATCHING,
expr.span, span,
&format!("redundant pattern matching, consider using `{good_method}`"), &format!("redundant pattern matching, consider using `{good_method}`"),
|diag| { "try this",
diag.span_suggestion( format!("{}.{good_method}", snippet(cx, result_expr.span, "_")),
span, Applicability::MachineApplicable,
"try this",
format!("{}.{good_method}", snippet(cx, result_expr.span, "_")),
Applicability::MaybeIncorrect, // snippet
);
},
); );
} }
} }

View File

@ -3191,7 +3191,7 @@ declare_clippy_lint! {
/// let mut v = vec![1, 2, 3]; /// let mut v = vec![1, 2, 3];
/// v.clear(); /// v.clear();
/// ``` /// ```
#[clippy::version = "1.69.0"] #[clippy::version = "1.70.0"]
pub CLEAR_WITH_DRAIN, pub CLEAR_WITH_DRAIN,
nursery, nursery,
"calling `drain` in order to `clear` a container" "calling `drain` in order to `clear` a container"

View File

@ -36,7 +36,7 @@ declare_clippy_lint! {
/// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests"); /// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
/// } /// }
/// ``` /// ```
#[clippy::version = "1.69.0"] #[clippy::version = "1.70.0"]
pub MISSING_ASSERT_MESSAGE, pub MISSING_ASSERT_MESSAGE,
restriction, restriction,
"checks assertions without a custom panic message" "checks assertions without a custom panic message"

View File

@ -0,0 +1,234 @@
use std::ops::ControlFlow;
use clippy_utils::{
diagnostics::span_lint_and_then,
is_path_lang_item, paths,
ty::match_type,
visitors::{for_each_expr, Visitable},
};
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::Block;
use rustc_hir::{
def::{DefKind, Res},
Expr, ImplItemKind, LangItem, Node,
};
use rustc_hir::{ExprKind, Impl, ItemKind, QPath, TyKind};
use rustc_hir::{ImplItem, Item, VariantData};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_middle::ty::TypeckResults;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span, Symbol};
declare_clippy_lint! {
/// ### What it does
/// Checks for manual [`core::fmt::Debug`](https://doc.rust-lang.org/core/fmt/trait.Debug.html) implementations that do not use all fields.
///
/// ### Why is this bad?
/// A common mistake is to forget to update manual `Debug` implementations when adding a new field
/// to a struct or a new variant to an enum.
///
/// At the same time, it also acts as a style lint to suggest using [`core::fmt::DebugStruct::finish_non_exhaustive`](https://doc.rust-lang.org/core/fmt/struct.DebugStruct.html#method.finish_non_exhaustive)
/// for the times when the user intentionally wants to leave out certain fields (e.g. to hide implementation details).
///
/// ### Known problems
/// This lint works based on the `DebugStruct` helper types provided by the `Formatter`,
/// so this won't detect `Debug` impls that use the `write!` macro.
/// Oftentimes there is more logic to a `Debug` impl if it uses `write!` macro, so it tries
/// to be on the conservative side and not lint in those cases in an attempt to prevent false positives.
///
/// This lint also does not look through function calls, so calling a function does not consider fields
/// used inside of that function as used by the `Debug` impl.
///
/// Lastly, it also ignores tuple structs as their `DebugTuple` formatter does not have a `finish_non_exhaustive`
/// method, as well as enums because their exhaustiveness is already checked by the compiler when matching on the enum,
/// making it much less likely to accidentally forget to update the `Debug` impl when adding a new variant.
///
/// ### Example
/// ```rust
/// use std::fmt;
/// struct Foo {
/// data: String,
/// // implementation detail
/// hidden_data: i32
/// }
/// impl fmt::Debug for Foo {
/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
/// formatter
/// .debug_struct("Foo")
/// .field("data", &self.data)
/// .finish()
/// }
/// }
/// ```
/// Use instead:
/// ```rust
/// use std::fmt;
/// struct Foo {
/// data: String,
/// // implementation detail
/// hidden_data: i32
/// }
/// impl fmt::Debug for Foo {
/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
/// formatter
/// .debug_struct("Foo")
/// .field("data", &self.data)
/// .finish_non_exhaustive()
/// }
/// }
/// ```
#[clippy::version = "1.70.0"]
pub MISSING_FIELDS_IN_DEBUG,
pedantic,
"missing fields in manual `Debug` implementation"
}
declare_lint_pass!(MissingFieldsInDebug => [MISSING_FIELDS_IN_DEBUG]);
fn report_lints(cx: &LateContext<'_>, span: Span, span_notes: Vec<(Span, &'static str)>) {
span_lint_and_then(
cx,
MISSING_FIELDS_IN_DEBUG,
span,
"manual `Debug` impl does not include all fields",
|diag| {
for (span, note) in span_notes {
diag.span_note(span, note);
}
diag.help("consider including all fields in this `Debug` impl")
.help("consider calling `.finish_non_exhaustive()` if you intend to ignore fields");
},
);
}
/// Checks if we should lint in a block of code
///
/// The way we check for this condition is by checking if there is
/// a call to `Formatter::debug_struct` but no call to `.finish_non_exhaustive()`.
fn should_lint<'tcx>(
cx: &LateContext<'tcx>,
typeck_results: &TypeckResults<'tcx>,
block: impl Visitable<'tcx>,
) -> bool {
// Is there a call to `DebugStruct::finish_non_exhaustive`? Don't lint if there is.
let mut has_finish_non_exhaustive = false;
// Is there a call to `DebugStruct::debug_struct`? Do lint if there is.
let mut has_debug_struct = false;
for_each_expr(block, |expr| {
if let ExprKind::MethodCall(path, recv, ..) = &expr.kind {
let recv_ty = typeck_results.expr_ty(recv).peel_refs();
if path.ident.name == sym::debug_struct && match_type(cx, recv_ty, &paths::FORMATTER) {
has_debug_struct = true;
} else if path.ident.name == sym!(finish_non_exhaustive) && match_type(cx, recv_ty, &paths::DEBUG_STRUCT) {
has_finish_non_exhaustive = true;
}
}
ControlFlow::<!, _>::Continue(())
});
!has_finish_non_exhaustive && has_debug_struct
}
/// Checks if the given expression is a call to `DebugStruct::field`
/// and the first argument to it is a string literal and if so, returns it
///
/// Example: `.field("foo", ....)` returns `Some("foo")`
fn as_field_call<'tcx>(
cx: &LateContext<'tcx>,
typeck_results: &TypeckResults<'tcx>,
expr: &Expr<'_>,
) -> Option<Symbol> {
if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind
&& let recv_ty = typeck_results.expr_ty(recv).peel_refs()
&& match_type(cx, recv_ty, &paths::DEBUG_STRUCT)
&& path.ident.name == sym::field
&& let ExprKind::Lit(lit) = &debug_field.kind
&& let LitKind::Str(sym, ..) = lit.node
{
Some(sym)
} else {
None
}
}
/// Attempts to find unused fields assuming that the item is a struct
fn check_struct<'tcx>(
cx: &LateContext<'tcx>,
typeck_results: &TypeckResults<'tcx>,
block: &'tcx Block<'tcx>,
self_ty: Ty<'tcx>,
item: &'tcx Item<'tcx>,
data: &VariantData<'_>,
) {
// Is there a "direct" field access anywhere (i.e. self.foo)?
// We don't want to lint if there is not, because the user might have
// a newtype struct and use fields from the wrapped type only.
let mut has_direct_field_access = false;
let mut field_accesses = FxHashSet::default();
for_each_expr(block, |expr| {
if let ExprKind::Field(target, ident) = expr.kind
&& let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs()
&& target_ty == self_ty
{
field_accesses.insert(ident.name);
has_direct_field_access = true;
} else if let Some(sym) = as_field_call(cx, typeck_results, expr) {
field_accesses.insert(sym);
}
ControlFlow::<!, _>::Continue(())
});
let span_notes = data
.fields()
.iter()
.filter_map(|field| {
if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) {
None
} else {
Some((field.span, "this field is unused"))
}
})
.collect::<Vec<_>>();
// only lint if there's also at least one direct field access to allow patterns
// where one might have a newtype struct and uses fields from the wrapped type
if !span_notes.is_empty() && has_direct_field_access {
report_lints(cx, item.span, span_notes);
}
}
impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) {
// is this an `impl Debug for X` block?
if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind
&& let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
&& let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind
&& cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug])
// don't trigger if this impl was derived
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
&& !item.span.from_expansion()
// find `Debug::fmt` function
&& let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt)
&& let ImplItem { kind: ImplItemKind::Fn(_, body_id), .. } = cx.tcx.hir().impl_item(fmt_item.id)
&& let body = cx.tcx.hir().body(*body_id)
&& let ExprKind::Block(block, _) = body.value.kind
// inspect `self`
&& let self_ty = cx.tcx.type_of(self_path.res.def_id()).skip_binder().peel_refs()
&& let Some(self_adt) = self_ty.ty_adt_def()
&& let Some(self_def_id) = self_adt.did().as_local()
&& let Some(Node::Item(self_item)) = cx.tcx.hir().find_by_def_id(self_def_id)
// NB: can't call cx.typeck_results() as we are not in a body
&& let typeck_results = cx.tcx.typeck_body(*body_id)
&& should_lint(cx, typeck_results, block)
{
// we intentionally only lint structs, see lint description
if let ItemKind::Struct(data, _) = &self_item.kind {
check_struct(cx, typeck_results, block, self_ty, item, data);
}
}
}
}

View File

@ -0,0 +1,61 @@
use clippy_utils::source::snippet_opt;
use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span};
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for empty `else` branches.
///
/// ### Why is this bad?
/// An empty else branch does nothing and can be removed.
///
/// ### Example
/// ```rust
///# fn check() -> bool { true }
/// if check() {
/// println!("Check successful!");
/// } else {
/// }
/// ```
/// Use instead:
/// ```rust
///# fn check() -> bool { true }
/// if check() {
/// println!("Check successful!");
/// }
/// ```
#[clippy::version = "1.71.0"]
pub NEEDLESS_ELSE,
style,
"empty else branch"
}
declare_lint_pass!(NeedlessElse => [NEEDLESS_ELSE]);
impl EarlyLintPass for NeedlessElse {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if let ExprKind::If(_, then_block, Some(else_clause)) = &expr.kind
&& let ExprKind::Block(block, _) = &else_clause.kind
&& !expr.span.from_expansion()
&& !else_clause.span.from_expansion()
&& block.stmts.is_empty()
&& let Some(trimmed) = expr.span.trim_start(then_block.span)
&& let span = trim_span(cx.sess().source_map(), trimmed)
&& let Some(else_snippet) = snippet_opt(cx, span)
// Ignore else blocks that contain comments or #[cfg]s
&& !else_snippet.contains(['/', '#'])
{
span_lint_and_sugg(
cx,
NEEDLESS_ELSE,
span,
"this else branch is empty",
"you can remove it",
String::new(),
Applicability::MachineApplicable,
);
}
}
}

View File

@ -241,7 +241,7 @@ impl<'de> Deserialize<'de> for MacroMatcher {
V: de::MapAccess<'de>, V: de::MapAccess<'de>,
{ {
let mut name = None; let mut name = None;
let mut brace: Option<&str> = None; let mut brace: Option<String> = None;
while let Some(key) = map.next_key()? { while let Some(key) = map.next_key()? {
match key { match key {
Field::Name => { Field::Name => {

View File

@ -35,7 +35,7 @@ declare_clippy_lint! {
/// }; /// };
/// let fut = f; /// let fut = f;
/// ``` /// ```
#[clippy::version = "1.69.0"] #[clippy::version = "1.70.0"]
pub REDUNDANT_ASYNC_BLOCK, pub REDUNDANT_ASYNC_BLOCK,
complexity, complexity,
"`async { future.await }` can be replaced by `future`" "`async { future.await }` can be replaced by `future`"

View File

@ -177,7 +177,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
} }
fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(!utf8).build(); let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(utf8).build();
if let ExprKind::Lit(lit) = expr.kind { if let ExprKind::Lit(lit) = expr.kind {
if let LitKind::Str(ref r, style) = lit.node { if let LitKind::Str(ref r, style) = lit.node {

View File

@ -70,7 +70,7 @@ declare_clippy_lint! {
"using a return statement like `return expr;` where an expression would suffice" "using a return statement like `return expr;` where an expression would suffice"
} }
#[derive(PartialEq, Eq, Clone)] #[derive(PartialEq, Eq)]
enum RetReplacement<'tcx> { enum RetReplacement<'tcx> {
Empty, Empty,
Block, Block,
@ -80,7 +80,7 @@ enum RetReplacement<'tcx> {
} }
impl<'tcx> RetReplacement<'tcx> { impl<'tcx> RetReplacement<'tcx> {
fn sugg_help(self) -> &'static str { fn sugg_help(&self) -> &'static str {
match self { match self {
Self::Empty | Self::Expr(..) => "remove `return`", Self::Empty | Self::Expr(..) => "remove `return`",
Self::Block => "replace `return` with an empty block", Self::Block => "replace `return` with an empty block",
@ -88,10 +88,11 @@ impl<'tcx> RetReplacement<'tcx> {
Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses", Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses",
} }
} }
fn applicability(&self) -> Option<Applicability> {
fn applicability(&self) -> Applicability {
match self { match self {
Self::Expr(_, ap) | Self::IfSequence(_, ap) => Some(*ap), Self::Expr(_, ap) | Self::IfSequence(_, ap) => *ap,
_ => None, _ => Applicability::MachineApplicable,
} }
} }
} }
@ -271,7 +272,7 @@ fn check_final_expr<'tcx>(
return; return;
} }
emit_return_lint(cx, ret_span, semi_spans, replacement); emit_return_lint(cx, ret_span, semi_spans, &replacement);
}, },
ExprKind::If(_, then, else_clause_opt) => { ExprKind::If(_, then, else_clause_opt) => {
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
@ -306,20 +307,17 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
contains_if(expr, false) contains_if(expr, false)
} }
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: RetReplacement<'_>) { fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: &RetReplacement<'_>) {
if ret_span.from_expansion() { if ret_span.from_expansion() {
return; return;
} }
let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable);
let return_replacement = replacement.to_string();
let sugg_help = replacement.sugg_help();
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability); let suggestions = std::iter::once((ret_span, replacement.to_string()))
// for each parent statement, we need to remove the semicolon .chain(semi_spans.into_iter().map(|span| (span, String::new())))
for semi_stmt_span in semi_spans { .collect();
diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability);
} diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
}); });
} }

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug
use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_type_lang_item; use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq}; use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq};
use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; use clippy_utils::{get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -255,7 +255,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
if_chain! { if_chain! {
// Find std::str::converts::from_utf8 // Find std::str::converts::from_utf8
if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); if let ExprKind::Call(fun, args) = e.kind;
if is_path_diagnostic_item(cx, fun, sym::str_from_utf8);
// Find string::as_bytes // Find string::as_bytes
if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind;

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
@ -42,6 +42,10 @@ declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
struct AsyncFnVisitor<'a, 'tcx> { struct AsyncFnVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
found_await: bool, found_await: bool,
/// Also keep track of `await`s in nested async blocks so we can mention
/// it in a note
await_in_async_block: Option<Span>,
async_depth: usize,
} }
impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
@ -49,7 +53,11 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind {
self.found_await = true; if self.async_depth == 1 {
self.found_await = true;
} else if self.await_in_async_block.is_none() {
self.await_in_async_block = Some(ex.span);
}
} }
walk_expr(self, ex); walk_expr(self, ex);
} }
@ -57,6 +65,20 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
fn nested_visit_map(&mut self) -> Self::Map { fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir() self.cx.tcx.hir()
} }
fn visit_body(&mut self, b: &'tcx Body<'tcx>) {
let is_async_block = matches!(b.generator_kind, Some(rustc_hir::GeneratorKind::Async(_)));
if is_async_block {
self.async_depth += 1;
}
walk_body(self, b);
if is_async_block {
self.async_depth -= 1;
}
}
} }
impl<'tcx> LateLintPass<'tcx> for UnusedAsync { impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
@ -70,16 +92,30 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
def_id: LocalDefId, def_id: LocalDefId,
) { ) {
if !span.from_expansion() && fn_kind.asyncness().is_async() { if !span.from_expansion() && fn_kind.asyncness().is_async() {
let mut visitor = AsyncFnVisitor { cx, found_await: false }; let mut visitor = AsyncFnVisitor {
cx,
found_await: false,
async_depth: 0,
await_in_async_block: None,
};
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id);
if !visitor.found_await { if !visitor.found_await {
span_lint_and_help( span_lint_and_then(
cx, cx,
UNUSED_ASYNC, UNUSED_ASYNC,
span, span,
"unused `async` for function with no await statements", "unused `async` for function with no await statements",
None, |diag| {
"consider removing the `async` from this function", diag.help("consider removing the `async` from this function");
if let Some(span) = visitor.await_in_async_block {
diag.span_note(
span,
"`await` used in an async block, which does not require \
the enclosing function to be `async`",
);
}
},
); );
} }
} }

View File

@ -2,12 +2,15 @@
#![allow(clippy::module_name_repetitions)] #![allow(clippy::module_name_repetitions)]
use rustc_session::Session;
use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext};
use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
use serde::Deserialize; use serde::Deserialize;
use std::error::Error; use std::fmt::{Debug, Display, Formatter};
use std::ops::Range;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::{cmp, env, fmt, fs, io, iter}; use std::{cmp, env, fmt, fs, io};
#[rustfmt::skip] #[rustfmt::skip]
const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
@ -67,33 +70,70 @@ impl DisallowedPath {
#[derive(Default)] #[derive(Default)]
pub struct TryConf { pub struct TryConf {
pub conf: Conf, pub conf: Conf,
pub errors: Vec<Box<dyn Error>>, pub errors: Vec<ConfError>,
pub warnings: Vec<Box<dyn Error>>, pub warnings: Vec<ConfError>,
} }
impl TryConf { impl TryConf {
fn from_error(error: impl Error + 'static) -> Self { fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self {
ConfError::from_toml(file, error).into()
}
}
impl From<ConfError> for TryConf {
fn from(value: ConfError) -> Self {
Self { Self {
conf: Conf::default(), conf: Conf::default(),
errors: vec![Box::new(error)], errors: vec![value],
warnings: vec![], warnings: vec![],
} }
} }
} }
#[derive(Debug)] impl From<io::Error> for TryConf {
struct ConfError(String); fn from(value: io::Error) -> Self {
ConfError::from(value).into()
impl fmt::Display for ConfError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<String as fmt::Display>::fmt(&self.0, f)
} }
} }
impl Error for ConfError {} #[derive(Debug)]
pub struct ConfError {
pub message: String,
pub span: Option<Span>,
}
fn conf_error(s: impl Into<String>) -> Box<dyn Error> { impl ConfError {
Box::new(ConfError(s.into())) fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self {
if let Some(span) = error.span() {
Self::spanned(file, error.message(), span)
} else {
Self {
message: error.message().to_string(),
span: None,
}
}
}
fn spanned(file: &SourceFile, message: impl Into<String>, span: Range<usize>) -> Self {
Self {
message: message.into(),
span: Some(Span::new(
file.start_pos + BytePos::from_usize(span.start),
file.start_pos + BytePos::from_usize(span.end),
SyntaxContext::root(),
None,
)),
}
}
}
impl From<io::Error> for ConfError {
fn from(value: io::Error) -> Self {
Self {
message: value.to_string(),
span: None,
}
}
} }
macro_rules! define_Conf { macro_rules! define_Conf {
@ -117,20 +157,14 @@ macro_rules! define_Conf {
} }
} }
impl<'de> Deserialize<'de> for TryConf {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
deserializer.deserialize_map(ConfVisitor)
}
}
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(field_identifier, rename_all = "kebab-case")] #[serde(field_identifier, rename_all = "kebab-case")]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
enum Field { $($name,)* third_party, } enum Field { $($name,)* third_party, }
struct ConfVisitor; struct ConfVisitor<'a>(&'a SourceFile);
impl<'de> Visitor<'de> for ConfVisitor { impl<'de> Visitor<'de> for ConfVisitor<'_> {
type Value = TryConf; type Value = TryConf;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -141,32 +175,38 @@ macro_rules! define_Conf {
let mut errors = Vec::new(); let mut errors = Vec::new();
let mut warnings = Vec::new(); let mut warnings = Vec::new();
$(let mut $name = None;)* $(let mut $name = None;)*
// could get `Field` here directly, but get `str` first for diagnostics // could get `Field` here directly, but get `String` first for diagnostics
while let Some(name) = map.next_key::<&str>()? { while let Some(name) = map.next_key::<toml::Spanned<String>>()? {
match Field::deserialize(name.into_deserializer())? { match Field::deserialize(name.get_ref().as_str().into_deserializer()) {
$(Field::$name => { Err(e) => {
$(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)? let e: FieldError = e;
match map.next_value() { errors.push(ConfError::spanned(self.0, e.0, name.span()));
Err(e) => errors.push(conf_error(e.to_string())), }
$(Ok(Field::$name) => {
$(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), name.span()));)?
let raw_value = map.next_value::<toml::Spanned<toml::Value>>()?;
let value_span = raw_value.span();
match <$ty>::deserialize(raw_value.into_inner()) {
Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), value_span)),
Ok(value) => match $name { Ok(value) => match $name {
Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))), Some(_) => errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), name.span())),
None => { None => {
$name = Some(value); $name = Some(value);
// $new_conf is the same as one of the defined `$name`s, so // $new_conf is the same as one of the defined `$name`s, so
// this variable is defined in line 2 of this function. // this variable is defined in line 2 of this function.
$(match $new_conf { $(match $new_conf {
Some(_) => errors.push(conf_error(concat!( Some(_) => errors.push(ConfError::spanned(self.0, concat!(
"duplicate field `", stringify!($new_conf), "duplicate field `", stringify!($new_conf),
"` (provided as `", stringify!($name), "`)" "` (provided as `", stringify!($name), "`)"
))), ), name.span())),
None => $new_conf = $name.clone(), None => $new_conf = $name.clone(),
})? })?
}, },
} }
} }
})* })*
// white-listed; ignore // ignore contents of the third_party key
Field::third_party => drop(map.next_value::<IgnoredAny>()) Ok(Field::third_party) => drop(map.next_value::<IgnoredAny>())
} }
} }
let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
@ -486,7 +526,7 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
// Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR. // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
// If neither of those exist, use ".". // If neither of those exist, use ".". (Update documentation if this priority changes)
let mut current = env::var_os("CLIPPY_CONF_DIR") let mut current = env::var_os("CLIPPY_CONF_DIR")
.or_else(|| env::var_os("CARGO_MANIFEST_DIR")) .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
.map_or_else(|| PathBuf::from("."), PathBuf::from) .map_or_else(|| PathBuf::from("."), PathBuf::from)
@ -532,19 +572,19 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
/// Read the `toml` configuration file. /// Read the `toml` configuration file.
/// ///
/// In case of error, the function tries to continue as much as possible. /// In case of error, the function tries to continue as much as possible.
pub fn read(path: &Path) -> TryConf { pub fn read(sess: &Session, path: &Path) -> TryConf {
let content = match fs::read_to_string(path) { let file = match sess.source_map().load_file(path) {
Err(e) => return TryConf::from_error(e), Err(e) => return e.into(),
Ok(content) => content, Ok(file) => file,
}; };
match toml::from_str::<TryConf>(&content) { match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(&file)) {
Ok(mut conf) => { Ok(mut conf) => {
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
conf conf
}, },
Err(e) => TryConf::from_error(e), Err(e) => TryConf::from_toml_error(&file, &e),
} }
} }
@ -556,65 +596,42 @@ fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
const SEPARATOR_WIDTH: usize = 4; const SEPARATOR_WIDTH: usize = 4;
// Check whether the error is "unknown field" and, if so, list the available fields sorted and at #[derive(Debug)]
// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it. struct FieldError(String);
pub fn format_error(error: Box<dyn Error>) -> String {
let s = error.to_string();
if_chain! { impl std::error::Error for FieldError {}
if error.downcast::<toml::de::Error>().is_ok();
if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s);
then {
use fmt::Write;
fields.sort_unstable(); impl Display for FieldError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let (rows, column_widths) = calculate_dimensions(&fields); f.pad(&self.0)
let mut msg = String::from(prefix);
for row in 0..rows {
writeln!(msg).unwrap();
for (column, column_width) in column_widths.iter().copied().enumerate() {
let index = column * rows + row;
let field = fields.get(index).copied().unwrap_or_default();
write!(
msg,
"{:SEPARATOR_WIDTH$}{field:column_width$}",
" "
)
.unwrap();
}
}
write!(msg, "\n{suffix}").unwrap();
msg
} else {
s
}
} }
} }
// `parse_unknown_field_message` will become unnecessary if impl serde::de::Error for FieldError {
// https://github.com/alexcrichton/toml-rs/pull/364 is merged. fn custom<T: Display>(msg: T) -> Self {
fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> { Self(msg.to_string())
// An "unknown field" message has the following form: }
// unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y
// ^^ ^^^^ ^^ fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
if_chain! { // List the available fields sorted and at least one per line, more if `CLIPPY_TERMINAL_WIDTH` is
if s.starts_with("unknown field"); // set and allows it.
let slices = s.split("`, `").collect::<Vec<_>>(); use fmt::Write;
let n = slices.len();
if n >= 2; let mut expected = expected.to_vec();
if let Some((prefix, first_field)) = slices[0].rsplit_once(" `"); expected.sort_unstable();
if let Some((last_field, suffix)) = slices[n - 1].split_once("` ");
then { let (rows, column_widths) = calculate_dimensions(&expected);
let fields = iter::once(first_field)
.chain(slices[1..n - 1].iter().copied()) let mut msg = format!("unknown field `{field}`, expected one of");
.chain(iter::once(last_field)) for row in 0..rows {
.collect::<Vec<_>>(); writeln!(msg).unwrap();
Some((prefix, fields, suffix)) for (column, column_width) in column_widths.iter().copied().enumerate() {
} else { let index = column * rows + row;
None let field = expected.get(index).copied().unwrap_or_default();
write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap();
}
} }
Self(msg)
} }
} }

View File

@ -65,8 +65,9 @@ declare_clippy_lint! {
/// This can lead to confusing error messages at best and to unexpected behavior at worst. /// This can lead to confusing error messages at best and to unexpected behavior at worst.
/// ///
/// ### Exceptions /// ### Exceptions
/// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) /// Wildcard imports are allowed from modules that their name contains `prelude`. Many crates
/// provide modules named "prelude" specifically designed for wildcard import. /// (including the standard library) provide modules named "prelude" specifically designed
/// for wildcard import.
/// ///
/// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
/// ///
@ -212,7 +213,9 @@ impl WildcardImports {
// Allow "...prelude::..::*" imports. // Allow "...prelude::..::*" imports.
// Many crates have a prelude, and it is imported as a glob by design. // Many crates have a prelude, and it is imported as a glob by design.
fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
segments.iter().any(|ps| ps.ident.name == sym::prelude) segments
.iter()
.any(|ps| ps.ident.name.as_str().contains(sym::prelude.as_str()))
} }
// Allow "super::*" imports in tests. // Allow "super::*" imports in tests.

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_utils" name = "clippy_utils"
version = "0.1.71" version = "0.1.72"
edition = "2021" edition = "2021"
publish = false publish = false

View File

@ -287,7 +287,7 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
/// Checks if the given `QPath` belongs to a type alias. /// Checks if the given `QPath` belongs to a type alias.
pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
match *qpath { match *qpath {
QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias, ..)), QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) }, QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) },
_ => false, _ => false,
} }

View File

@ -20,7 +20,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items // names may refer to stabilized feature flags or library items
msrv_aliases! { msrv_aliases! {
1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
1,55,0 { SEEK_REWIND } 1,55,0 { SEEK_REWIND }

View File

@ -125,8 +125,6 @@ pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"]; pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"]; pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"]; pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
@ -163,3 +161,5 @@ pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"];
pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"]; pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"];
pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"]; pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"];
pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"];
pub const FORMATTER: [&str; 3] = ["core", "fmt", "Formatter"];
pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"];

View File

@ -4,7 +4,7 @@
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_session::Session; use rustc_session::Session;
use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::source_map::{original_sp, SourceMap};
@ -71,11 +71,16 @@ pub fn expr_block<T: LintContext>(
app: &mut Applicability, app: &mut Applicability,
) -> String { ) -> String {
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
if from_macro { if !from_macro &&
format!("{{ {code} }}") let ExprKind::Block(block, _) = expr.kind &&
} else if let ExprKind::Block(_, _) = expr.kind { block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
{
format!("{code}") format!("{code}")
} else { } else {
// FIXME: add extra indent for the unsafe blocks:
// original code: unsafe { ... }
// result code: { unsafe { ... } }
// desired code: {\n unsafe { ... }\n}
format!("{{ {code} }}") format!("{{ {code} }}")
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "declare_clippy_lint" name = "declare_clippy_lint"
version = "0.1.71" version = "0.1.72"
edition = "2021" edition = "2021"
publish = false publish = false

View File

@ -22,7 +22,7 @@ rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85" serde_json = "1.0.85"
tar = "0.4" tar = "0.4"
toml = "0.5" toml = "0.7.3"
ureq = "2.2" ureq = "2.2"
walkdir = "2.3" walkdir = "2.3"

View File

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly-2023-05-20" channel = "nightly-2023-06-02"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View File

@ -70,7 +70,7 @@ fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option<String>)
/// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy /// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy
/// when any of them are modified /// when any of them are modified
fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) { fn track_files(parse_sess: &mut ParseSess) {
let file_depinfo = parse_sess.file_depinfo.get_mut(); let file_depinfo = parse_sess.file_depinfo.get_mut();
// Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver`
@ -79,10 +79,7 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
file_depinfo.insert(Symbol::intern("Cargo.toml")); file_depinfo.insert(Symbol::intern("Cargo.toml"));
} }
// `clippy.toml` // `clippy.toml` will be automatically tracked as it's loaded with `sess.source_map().load_file()`
if let Some(path) = conf_path_string {
file_depinfo.insert(Symbol::intern(&path));
}
// During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
// it is rebuilt // it is rebuilt
@ -126,17 +123,11 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
#[allow(rustc::bad_opt_access)] #[allow(rustc::bad_opt_access)]
fn config(&mut self, config: &mut interface::Config) { fn config(&mut self, config: &mut interface::Config) {
let conf_path = clippy_lints::lookup_conf_file(); let conf_path = clippy_lints::lookup_conf_file();
let conf_path_string = if let Ok((Some(path), _)) = &conf_path {
path.to_str().map(String::from)
} else {
None
};
let previous = config.register_lints.take(); let previous = config.register_lints.take();
let clippy_args_var = self.clippy_args_var.take(); let clippy_args_var = self.clippy_args_var.take();
config.parse_sess_created = Some(Box::new(move |parse_sess| { config.parse_sess_created = Some(Box::new(move |parse_sess| {
track_clippy_args(parse_sess, &clippy_args_var); track_clippy_args(parse_sess, &clippy_args_var);
track_files(parse_sess, conf_path_string); track_files(parse_sess);
})); }));
config.register_lints = Some(Box::new(move |sess, lint_store| { config.register_lints = Some(Box::new(move |sess, lint_store| {
// technically we're ~guaranteed that this is none but might as well call anything that // technically we're ~guaranteed that this is none but might as well call anything that

View File

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.11.0" version = "0.11.0"
@ -9,71 +11,14 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "ctrlc"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c"
dependencies = [
"kernel32-sys",
"nix",
"winapi 0.2.8",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "libc"
version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
[[package]] [[package]]
name = "multiple_crate_versions" name = "multiple_crate_versions"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"ctrlc", "winapi 0.2.8",
] ]
[[package]]
name = "nix"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"void",
]
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.2.8" version = "0.2.8"
@ -90,12 +35,6 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu",
] ]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View File

@ -6,5 +6,5 @@ publish = false
[workspace] [workspace]
[dependencies] [dependencies]
ctrlc = "=3.1.0" winapi = "0.2"
ansi_term = "=0.11.0" ansi_term = "=0.11.0"

View File

@ -3,7 +3,7 @@
//@normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs" //@normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs"
//@normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" //@normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
//@normalize-stderr-test: "'rustc'" -> "'<unnamed>'" //@normalize-stderr-test: "'rustc'" -> "'<unnamed>'"
//@normalize-stderr-test: "running on .*" -> "running on <target>" //@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc <version> running on <target>"
//@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> ""
#![deny(clippy::internal)] #![deny(clippy::internal)]

View File

@ -5,10 +5,9 @@ error: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new
note: rustc 1.71.0-nightly (521f4dae1 2023-05-19) running on <target> note: rustc <version> running on <target>
note: compiler flags: -C prefer-dynamic -Z ui-testing note: compiler flags: -C prefer-dynamic -Z ui-testing
note: Clippy version: foo query stack during panic:
thread panicked while processing panic. aborting.
thread panicked while panicking. aborting.

View File

@ -1,4 +1,8 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4 error: error reading Clippy's configuration file: expected `.`, `=`
--> $DIR/clippy.toml:1:4
|
LL | fn this_is_obviously(not: a, toml: file) {
| ^
error: aborting due to previous error error: aborting due to previous error

View File

@ -1,4 +1,8 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `disallowed-names` error: error reading Clippy's configuration file: invalid type: integer `42`, expected a sequence
--> $DIR/clippy.toml:1:20
|
LL | disallowed-names = 42
| ^^
error: aborting due to previous error error: aborting due to previous error

View File

@ -1,6 +1,14 @@
warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
--> $DIR/clippy.toml:2:1
|
LL | cyclomatic-complexity-threshold = 2
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead warning: error reading Clippy's configuration file: deprecated field `blacklisted-names`. Please use `disallowed-names` instead
--> $DIR/clippy.toml:3:1
|
LL | blacklisted-names = [ "..", "wibble" ]
| ^^^^^^^^^^^^^^^^^
error: the function has a cognitive complexity of (3/2) error: the function has a cognitive complexity of (3/2)
--> $DIR/conf_deprecated_key.rs:6:4 --> $DIR/conf_deprecated_key.rs:6:4

View File

@ -1,5 +1,2 @@
cognitive-complexity-threshold = 2 cognitive-complexity-threshold = 2
# This is the deprecated name for the same key
cyclomatic-complexity-threshold = 3
# Check we get duplication warning regardless of order
cognitive-complexity-threshold = 4 cognitive-complexity-threshold = 4

View File

@ -1,8 +1,8 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`) error: error reading Clippy's configuration file: duplicate key `cognitive-complexity-threshold` in document root
--> $DIR/clippy.toml:2:1
|
LL | cognitive-complexity-threshold = 4
| ^
error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive-complexity-threshold` error: aborting due to previous error
warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
error: aborting due to 2 previous errors; 1 warning emitted

View File

@ -0,0 +1,3 @@
cognitive-complexity-threshold = 2
# This is the deprecated name for the same key
cyclomatic-complexity-threshold = 3

View File

@ -0,0 +1 @@
fn main() {}

View File

@ -0,0 +1,14 @@
error: error reading Clippy's configuration file: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`)
--> $DIR/clippy.toml:3:1
|
LL | cyclomatic-complexity-threshold = 3
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
--> $DIR/clippy.toml:3:1
|
LL | cyclomatic-complexity-threshold = 3
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error; 1 warning emitted

View File

@ -0,0 +1,4 @@
# This is the deprecated name for cognitive-complexity-threshold
cyclomatic-complexity-threshold = 3
# Check we get duplication warning regardless of order
cognitive-complexity-threshold = 4

View File

@ -0,0 +1 @@
fn main() {}

View File

@ -0,0 +1,14 @@
error: error reading Clippy's configuration file: duplicate field `cognitive-complexity-threshold`
--> $DIR/clippy.toml:4:1
|
LL | cognitive-complexity-threshold = 4
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
--> $DIR/clippy.toml:2:1
|
LL | cyclomatic-complexity-threshold = 3
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error; 1 warning emitted

View File

@ -1,5 +1,5 @@
#![warn(clippy::ifs_same_cond)] #![warn(clippy::ifs_same_cond)]
#![allow(clippy::if_same_then_else, clippy::comparison_chain)] #![allow(clippy::if_same_then_else, clippy::comparison_chain, clippy::needless_else)]
fn main() {} fn main() {}

View File

@ -1,6 +1,8 @@
# that one is an error # that one is an error
foobar = 42 foobar = 42
# so is this one
barfoo = 53
# that one is white-listed # that one is ignored
[third-party] [third-party]
clippy-feature = "nightly" clippy-feature = "nightly"

View File

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of error: error reading Clippy's configuration file: unknown field `foobar`, expected one of
allow-dbg-in-tests allow-dbg-in-tests
allow-expect-in-tests allow-expect-in-tests
allow-mixed-uninlined-format-args allow-mixed-uninlined-format-args
@ -54,7 +54,71 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
vec-box-size-threshold vec-box-size-threshold
verbose-bit-mask-threshold verbose-bit-mask-threshold
warn-on-all-wildcard-imports warn-on-all-wildcard-imports
at line 5 column 1 --> $DIR/clippy.toml:2:1
|
LL | foobar = 42
| ^^^^^^
error: aborting due to previous error error: error reading Clippy's configuration file: unknown field `barfoo`, expected one of
allow-dbg-in-tests
allow-expect-in-tests
allow-mixed-uninlined-format-args
allow-print-in-tests
allow-unwrap-in-tests
allowed-scripts
arithmetic-side-effects-allowed
arithmetic-side-effects-allowed-binary
arithmetic-side-effects-allowed-unary
array-size-threshold
avoid-breaking-exported-api
await-holding-invalid-types
blacklisted-names
cargo-ignore-publish
cognitive-complexity-threshold
cyclomatic-complexity-threshold
disallowed-macros
disallowed-methods
disallowed-names
disallowed-types
doc-valid-idents
enable-raw-pointer-heuristic-for-send
enforced-import-renames
enum-variant-name-threshold
enum-variant-size-threshold
future-size-threshold
ignore-interior-mutability
large-error-threshold
literal-representation-threshold
matches-for-let-else
max-fn-params-bools
max-include-file-size
max-struct-bools
max-suggested-slice-pattern-length
max-trait-bounds
missing-docs-in-crate-items
msrv
pass-by-value-size-limit
semicolon-inside-block-ignore-singleline
semicolon-outside-block-ignore-multiline
single-char-binding-names-threshold
standard-macro-braces
suppress-restriction-lint-in-const
third-party
too-large-for-stack
too-many-arguments-threshold
too-many-lines-threshold
trivial-copy-size-limit
type-complexity-threshold
unnecessary-box-size
unreadable-literal-lint-fractions
upper-case-acronyms-aggressive
vec-box-size-threshold
verbose-bit-mask-threshold
warn-on-all-wildcard-imports
--> $DIR/clippy.toml:4:1
|
LL | barfoo = 53
| ^^^^^^
error: aborting due to 2 previous errors

View File

@ -25,3 +25,9 @@ pub mod prelude {
pub struct PreludeModAnywhere; pub struct PreludeModAnywhere;
} }
} }
pub mod extern_prelude {
pub mod v1 {
pub struct ExternPreludeModAnywhere;
}
}

View File

@ -1,6 +1,10 @@
#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)]
#![allow(dead_code)] #![allow(dead_code)]
#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] #![allow(
clippy::mixed_read_write_in_expression,
clippy::uninlined_format_args,
clippy::needless_else
)]
// This tests valid if blocks that shouldn't trigger the lint // This tests valid if blocks that shouldn't trigger the lint

View File

@ -1,5 +1,5 @@
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/valid_if_blocks.rs:105:14 --> $DIR/valid_if_blocks.rs:109:14
| |
LL | if false { LL | if false {
| ______________^ | ______________^
@ -7,7 +7,7 @@ LL | | } else {
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/valid_if_blocks.rs:106:12 --> $DIR/valid_if_blocks.rs:110:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -20,7 +20,7 @@ LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/valid_if_blocks.rs:116:15 --> $DIR/valid_if_blocks.rs:120:15
| |
LL | if x == 0 { LL | if x == 0 {
| _______________^ | _______________^
@ -31,7 +31,7 @@ LL | | } else {
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/valid_if_blocks.rs:120:12 --> $DIR/valid_if_blocks.rs:124:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -42,19 +42,19 @@ LL | | }
| |_____^ | |_____^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/valid_if_blocks.rs:127:23 --> $DIR/valid_if_blocks.rs:131:23
| |
LL | let _ = if x == 6 { 7 } else { 7 }; LL | let _ = if x == 6 { 7 } else { 7 };
| ^^^^^ | ^^^^^
| |
note: same as this note: same as this
--> $DIR/valid_if_blocks.rs:127:34 --> $DIR/valid_if_blocks.rs:131:34
| |
LL | let _ = if x == 6 { 7 } else { 7 }; LL | let _ = if x == 6 { 7 } else { 7 };
| ^^^^^ | ^^^^^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/valid_if_blocks.rs:133:23 --> $DIR/valid_if_blocks.rs:137:23
| |
LL | } else if x == 68 { LL | } else if x == 68 {
| _______________________^ | _______________________^
@ -66,7 +66,7 @@ LL | | } else {
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/valid_if_blocks.rs:138:12 --> $DIR/valid_if_blocks.rs:142:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -78,7 +78,7 @@ LL | | };
| |_____^ | |_____^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/valid_if_blocks.rs:147:23 --> $DIR/valid_if_blocks.rs:151:23
| |
LL | } else if x == 68 { LL | } else if x == 68 {
| _______________________^ | _______________________^
@ -88,7 +88,7 @@ LL | | } else {
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/valid_if_blocks.rs:150:12 --> $DIR/valid_if_blocks.rs:154:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^

View File

@ -6,6 +6,7 @@
#![no_std] #![no_std]
#![allow(clippy::if_same_then_else)] #![allow(clippy::if_same_then_else)]
#![allow(clippy::redundant_pattern_matching)] #![allow(clippy::redundant_pattern_matching)]
#![allow(clippy::needless_else)]
use core::panic::PanicInfo; use core::panic::PanicInfo;

View File

@ -101,6 +101,28 @@ struct EmptyStruct {}
#[non_exhaustive] #[non_exhaustive]
struct NonExhaustiveStruct; struct NonExhaustiveStruct;
mod issue_10755 {
struct Sqlite {}
trait HasArguments<'q> {
type Arguments;
}
impl<'q> HasArguments<'q> for Sqlite {
type Arguments = std::marker::PhantomData<&'q ()>;
}
type SqliteArguments<'q> = <Sqlite as HasArguments<'q>>::Arguments;
fn foo() {
// should not lint
// type alias cannot be used as a constructor
let _ = <Sqlite as HasArguments>::Arguments::default();
let _ = SqliteArguments::default();
}
}
fn main() { fn main() {
// should lint // should lint
let _ = PhantomData::<usize>; let _ = PhantomData::<usize>;

View File

@ -101,6 +101,28 @@ struct EmptyStruct {}
#[non_exhaustive] #[non_exhaustive]
struct NonExhaustiveStruct; struct NonExhaustiveStruct;
mod issue_10755 {
struct Sqlite {}
trait HasArguments<'q> {
type Arguments;
}
impl<'q> HasArguments<'q> for Sqlite {
type Arguments = std::marker::PhantomData<&'q ()>;
}
type SqliteArguments<'q> = <Sqlite as HasArguments<'q>>::Arguments;
fn foo() {
// should not lint
// type alias cannot be used as a constructor
let _ = <Sqlite as HasArguments>::Arguments::default();
let _ = SqliteArguments::default();
}
}
fn main() { fn main() {
// should lint // should lint
let _ = PhantomData::<usize>::default(); let _ = PhantomData::<usize>::default();

View File

@ -13,25 +13,25 @@ LL | inner: PhantomData::default(),
| ^^^^^^^^^^^ help: remove this call to `default` | ^^^^^^^^^^^ help: remove this call to `default`
error: use of `default` to create a unit struct error: use of `default` to create a unit struct
--> $DIR/default_constructed_unit_structs.rs:106:33 --> $DIR/default_constructed_unit_structs.rs:128:33
| |
LL | let _ = PhantomData::<usize>::default(); LL | let _ = PhantomData::<usize>::default();
| ^^^^^^^^^^^ help: remove this call to `default` | ^^^^^^^^^^^ help: remove this call to `default`
error: use of `default` to create a unit struct error: use of `default` to create a unit struct
--> $DIR/default_constructed_unit_structs.rs:107:42 --> $DIR/default_constructed_unit_structs.rs:129:42
| |
LL | let _: PhantomData<i32> = PhantomData::default(); LL | let _: PhantomData<i32> = PhantomData::default();
| ^^^^^^^^^^^ help: remove this call to `default` | ^^^^^^^^^^^ help: remove this call to `default`
error: use of `default` to create a unit struct error: use of `default` to create a unit struct
--> $DIR/default_constructed_unit_structs.rs:108:55 --> $DIR/default_constructed_unit_structs.rs:130:55
| |
LL | let _: PhantomData<i32> = std::marker::PhantomData::default(); LL | let _: PhantomData<i32> = std::marker::PhantomData::default();
| ^^^^^^^^^^^ help: remove this call to `default` | ^^^^^^^^^^^ help: remove this call to `default`
error: use of `default` to create a unit struct error: use of `default` to create a unit struct
--> $DIR/default_constructed_unit_structs.rs:109:23 --> $DIR/default_constructed_unit_structs.rs:131:23
| |
LL | let _ = UnitStruct::default(); LL | let _ = UnitStruct::default();
| ^^^^^^^^^^^ help: remove this call to `default` | ^^^^^^^^^^^ help: remove this call to `default`

View File

@ -1,5 +1,5 @@
#![warn(clippy::ifs_same_cond)] #![warn(clippy::ifs_same_cond)]
#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks #![allow(clippy::if_same_then_else, clippy::comparison_chain, clippy::needless_else)] // all empty blocks
fn ifs_same_cond() { fn ifs_same_cond() {
let a = 0; let a = 0;

View File

@ -18,6 +18,19 @@ pub static DOESNOTLINT2: [u8; 512_001] = {
[x; 512_001] [x; 512_001]
}; };
fn issue_10741() {
#[derive(Copy, Clone)]
struct Large([u32; 100_000]);
fn build() -> Large {
Large([0; 100_000])
}
let _x = [build(); 3];
let _y = [build(), build(), build()];
}
fn main() { fn main() {
let bad = ( let bad = (
[0u32; 20_000_000], [0u32; 20_000_000],

View File

@ -1,14 +1,30 @@
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:23:9 --> $DIR/large_stack_arrays.rs:29:14
|
LL | let _x = [build(); 3];
| ^^^^^^^^^^^^
|
= help: consider allocating on the heap with `vec![build(); 3].into_boxed_slice()`
= note: `-D clippy::large-stack-arrays` implied by `-D warnings`
error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:31:14
|
LL | let _y = [build(), build(), build()];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:36:9
| |
LL | [0u32; 20_000_000], LL | [0u32; 20_000_000],
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
| |
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
= note: `-D clippy::large-stack-arrays` implied by `-D warnings`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:24:9 --> $DIR/large_stack_arrays.rs:37:9
| |
LL | [S { data: [0; 32] }; 5000], LL | [S { data: [0; 32] }; 5000],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -16,7 +32,7 @@ LL | [S { data: [0; 32] }; 5000],
= help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:25:9 --> $DIR/large_stack_arrays.rs:38:9
| |
LL | [Some(""); 20_000_000], LL | [Some(""); 20_000_000],
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@ -24,7 +40,7 @@ LL | [Some(""); 20_000_000],
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:26:9 --> $DIR/large_stack_arrays.rs:39:9
| |
LL | [E::T(0); 5000], LL | [E::T(0); 5000],
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@ -32,12 +48,12 @@ LL | [E::T(0); 5000],
= help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:27:9 --> $DIR/large_stack_arrays.rs:40:9
| |
LL | [0u8; usize::MAX], LL | [0u8; usize::MAX],
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()` = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()`
error: aborting due to 5 previous errors error: aborting due to 7 previous errors

View File

@ -146,10 +146,20 @@ fn fire() {
Variant::A(0, 0) Variant::A(0, 0)
} }
// Should not be renamed
let v = if let Variant::A(a, 0) = e() { a } else { return }; let v = if let Variant::A(a, 0) = e() { a } else { return };
// Should be renamed
let v = if let Variant::B(b) = e() { b } else { return }; // `mut v` is inserted into the pattern
let mut v = if let Variant::B(b) = e() { b } else { return };
// Nesting works
let nested = Ok(Some(e()));
let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
b
} else {
return;
};
// dot dot works
let v = if let Variant::A(.., a) = e() { a } else { return };
} }
fn not_fire() { fn not_fire() {

View File

@ -260,19 +260,42 @@ LL | create_binding_if_some!(w, g());
= note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this could be rewritten as `let...else` error: this could be rewritten as `let...else`
--> $DIR/manual_let_else.rs:150:5 --> $DIR/manual_let_else.rs:149:5
| |
LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; LL | let v = if let Variant::A(a, 0) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(a, 0) = e() else { return };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };`
error: this could be rewritten as `let...else` error: this could be rewritten as `let...else`
--> $DIR/manual_let_else.rs:152:5 --> $DIR/manual_let_else.rs:152:5
| |
LL | let v = if let Variant::B(b) = e() { b } else { return }; LL | let mut v = if let Variant::B(b) = e() { b } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(v) = e() else { return };` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };`
error: this could be rewritten as `let...else` error: this could be rewritten as `let...else`
--> $DIR/manual_let_else.rs:262:5 --> $DIR/manual_let_else.rs:156:5
|
LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
LL | | b
LL | | } else {
LL | | return;
LL | | };
| |______^
|
help: consider writing
|
LL ~ let (Ok(Some(Variant::B(v))) | Err(Some(Variant::A(v, _)))) = nested else {
LL + return;
LL + };
|
error: this could be rewritten as `let...else`
--> $DIR/manual_let_else.rs:162:5
|
LL | let v = if let Variant::A(.., a) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };`
error: this could be rewritten as `let...else`
--> $DIR/manual_let_else.rs:272:5
| |
LL | / let _ = match ff { LL | / let _ = match ff {
LL | | Some(value) => value, LL | | Some(value) => value,
@ -280,5 +303,5 @@ LL | | _ => macro_call!(),
LL | | }; LL | | };
| |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };`
error: aborting due to 20 previous errors error: aborting due to 22 previous errors

View File

@ -68,7 +68,7 @@ fn fire() {
let f = Variant::Bar(1); let f = Variant::Bar(1);
let _value = match f { let _value = match f {
Variant::Bar(_) | Variant::Baz(_) => (), Variant::Bar(v) | Variant::Baz(v) => v,
_ => return, _ => return,
}; };

View File

@ -58,10 +58,10 @@ error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:70:5 --> $DIR/manual_let_else_match.rs:70:5
| |
LL | / let _value = match f { LL | / let _value = match f {
LL | | Variant::Bar(_) | Variant::Baz(_) => (), LL | | Variant::Bar(v) | Variant::Baz(v) => v,
LL | | _ => return, LL | | _ => return,
LL | | }; LL | | };
| |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };` | |______^ help: consider writing: `let (Variant::Bar(_value) | Variant::Baz(_value)) = f else { return };`
error: this could be rewritten as `let...else` error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:76:5 --> $DIR/manual_let_else_match.rs:76:5

View File

@ -1,7 +1,20 @@
#![feature(exclusive_range_pattern)] #![feature(exclusive_range_pattern)]
#![allow(clippy::match_same_arms)] #![allow(clippy::match_same_arms, dead_code)]
#![warn(clippy::match_wild_err_arm)] #![warn(clippy::match_wild_err_arm)]
fn issue_10635() {
enum Error {
A,
B,
}
// Don't trigger in const contexts. Const unwrap is not yet stable
const X: () = match Ok::<_, Error>(()) {
Ok(x) => x,
Err(_) => panic!(),
};
}
fn match_wild_err_arm() { fn match_wild_err_arm() {
let x: Result<i32, &str> = Ok(3); let x: Result<i32, &str> = Ok(3);

View File

@ -1,5 +1,5 @@
error: `Err(_)` matches all errors error: `Err(_)` matches all errors
--> $DIR/match_wild_err_arm.rs:11:9 --> $DIR/match_wild_err_arm.rs:24:9
| |
LL | Err(_) => panic!("err"), LL | Err(_) => panic!("err"),
| ^^^^^^ | ^^^^^^
@ -8,7 +8,7 @@ LL | Err(_) => panic!("err"),
= note: `-D clippy::match-wild-err-arm` implied by `-D warnings` = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
error: `Err(_)` matches all errors error: `Err(_)` matches all errors
--> $DIR/match_wild_err_arm.rs:17:9 --> $DIR/match_wild_err_arm.rs:30:9
| |
LL | Err(_) => panic!(), LL | Err(_) => panic!(),
| ^^^^^^ | ^^^^^^
@ -16,7 +16,7 @@ LL | Err(_) => panic!(),
= note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
error: `Err(_)` matches all errors error: `Err(_)` matches all errors
--> $DIR/match_wild_err_arm.rs:23:9 --> $DIR/match_wild_err_arm.rs:36:9
| |
LL | Err(_) => { LL | Err(_) => {
| ^^^^^^ | ^^^^^^
@ -24,7 +24,7 @@ LL | Err(_) => {
= note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
error: `Err(_e)` matches all errors error: `Err(_e)` matches all errors
--> $DIR/match_wild_err_arm.rs:31:9 --> $DIR/match_wild_err_arm.rs:44:9
| |
LL | Err(_e) => panic!(), LL | Err(_e) => panic!(),
| ^^^^^^^ | ^^^^^^^

View File

@ -0,0 +1,191 @@
#![allow(unused)]
#![warn(clippy::missing_fields_in_debug)]
use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
struct NamedStruct1Ignored {
data: u8,
hidden: u32,
}
impl fmt::Debug for NamedStruct1Ignored {
// unused field: hidden
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("NamedStruct1Ignored")
.field("data", &self.data)
.finish()
}
}
struct NamedStructMultipleIgnored {
data: u8,
hidden: u32,
hidden2: String,
hidden3: Vec<Vec<i32>>,
hidden4: ((((u8), u16), u32), u64),
}
impl fmt::Debug for NamedStructMultipleIgnored {
// unused fields: hidden, hidden2, hidden4
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("NamedStructMultipleIgnored")
.field("data", &self.data)
.field("hidden3", &self.hidden3)
.finish()
}
}
struct Unit;
// ok
impl fmt::Debug for Unit {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("Unit").finish()
}
}
struct UnnamedStruct1Ignored(String);
impl fmt::Debug for UnnamedStruct1Ignored {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_tuple("UnnamedStruct1Ignored").finish()
}
}
struct UnnamedStructMultipleIgnored(String, Vec<u8>, i32);
// tuple structs are not linted
impl fmt::Debug for UnnamedStructMultipleIgnored {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_tuple("UnnamedStructMultipleIgnored")
.field(&self.1)
.finish()
}
}
struct NamedStructNonExhaustive {
a: u8,
b: String,
}
// ok
impl fmt::Debug for NamedStructNonExhaustive {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("NamedStructNonExhaustive")
.field("a", &self.a)
.finish_non_exhaustive() // should not warn here
}
}
struct MultiExprDebugImpl {
a: u8,
b: String,
}
// ok
impl fmt::Debug for MultiExprDebugImpl {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = formatter.debug_struct("MultiExprDebugImpl");
f.field("a", &self.a);
f.finish()
}
}
#[derive(Debug)]
struct DerivedStruct {
a: u8,
b: i32,
}
// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1166846953
struct Inner {
a: usize,
b: usize,
}
struct HasInner {
inner: Inner,
}
impl HasInner {
fn get(&self) -> &Inner {
&self.inner
}
}
impl fmt::Debug for HasInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = self.get();
f.debug_struct("HasInner")
.field("a", &inner.a)
.field("b", &inner.b)
.finish()
}
}
// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1170306053
struct Foo {
a: u8,
b: u8,
}
impl fmt::Debug for Foo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Foo").field("a", &self.a).field("b", &()).finish()
}
}
// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175473620
mod comment1175473620 {
use super::*;
struct Inner {
a: usize,
b: usize,
}
struct Wrapper(Inner);
impl Deref for Wrapper {
type Target = Inner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Debug for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Wrapper")
.field("a", &self.a)
.field("b", &self.b)
.finish()
}
}
}
// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175488757
// PhantomData is an exception and does not need to be included
struct WithPD {
a: u8,
b: u8,
c: PhantomData<String>,
}
impl fmt::Debug for WithPD {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WithPD")
.field("a", &self.a)
.field("b", &self.b)
.finish()
}
}
fn main() {}

View File

@ -0,0 +1,73 @@
error: manual `Debug` impl does not include all fields
--> $DIR/missing_fields_in_debug.rs:13:1
|
LL | / impl fmt::Debug for NamedStruct1Ignored {
LL | | // unused field: hidden
LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
LL | | formatter
... |
LL | | }
LL | | }
| |_^
|
note: this field is unused
--> $DIR/missing_fields_in_debug.rs:10:5
|
LL | hidden: u32,
| ^^^^^^^^^^^
= help: consider including all fields in this `Debug` impl
= help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields
= note: `-D clippy::missing-fields-in-debug` implied by `-D warnings`
error: manual `Debug` impl does not include all fields
--> $DIR/missing_fields_in_debug.rs:31:1
|
LL | / impl fmt::Debug for NamedStructMultipleIgnored {
LL | | // unused fields: hidden, hidden2, hidden4
LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
LL | | formatter
... |
LL | | }
LL | | }
| |_^
|
note: this field is unused
--> $DIR/missing_fields_in_debug.rs:25:5
|
LL | hidden: u32,
| ^^^^^^^^^^^
note: this field is unused
--> $DIR/missing_fields_in_debug.rs:26:5
|
LL | hidden2: String,
| ^^^^^^^^^^^^^^^
note: this field is unused
--> $DIR/missing_fields_in_debug.rs:28:5
|
LL | hidden4: ((((u8), u16), u32), u64),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider including all fields in this `Debug` impl
= help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields
error: manual `Debug` impl does not include all fields
--> $DIR/missing_fields_in_debug.rs:92:1
|
LL | / impl fmt::Debug for MultiExprDebugImpl {
LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
LL | | let mut f = formatter.debug_struct("MultiExprDebugImpl");
LL | | f.field("a", &self.a);
LL | | f.finish()
LL | | }
LL | | }
| |_^
|
note: this field is unused
--> $DIR/missing_fields_in_debug.rs:88:5
|
LL | b: String,
| ^^^^^^^^^
= help: consider including all fields in this `Debug` impl
= help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields
error: aborting due to 3 previous errors

View File

@ -0,0 +1,57 @@
//@run-rustfix
#![allow(unused)]
#![warn(clippy::needless_else)]
#![allow(clippy::suspicious_else_formatting)]
macro_rules! mac {
($test:expr) => {
if $test {
println!("Test successful!");
} else {
}
};
}
macro_rules! empty_expansion {
() => {};
}
fn main() {
let b = std::hint::black_box(true);
if b {
println!("Foobar");
}
if b {
println!("Foobar");
} else {
// Do not lint because this comment might be important
}
if b {
println!("Foobar");
} else
/* Do not lint because this comment might be important */
{
}
// Do not lint because of the expression
let _ = if b { 1 } else { 2 };
// Do not lint because inside a macro
mac!(b);
if b {
println!("Foobar");
} else {
#[cfg(foo)]
"Do not lint cfg'd out code"
}
if b {
println!("Foobar");
} else {
empty_expansion!();
}
}

View File

@ -0,0 +1,58 @@
//@run-rustfix
#![allow(unused)]
#![warn(clippy::needless_else)]
#![allow(clippy::suspicious_else_formatting)]
macro_rules! mac {
($test:expr) => {
if $test {
println!("Test successful!");
} else {
}
};
}
macro_rules! empty_expansion {
() => {};
}
fn main() {
let b = std::hint::black_box(true);
if b {
println!("Foobar");
} else {
}
if b {
println!("Foobar");
} else {
// Do not lint because this comment might be important
}
if b {
println!("Foobar");
} else
/* Do not lint because this comment might be important */
{
}
// Do not lint because of the expression
let _ = if b { 1 } else { 2 };
// Do not lint because inside a macro
mac!(b);
if b {
println!("Foobar");
} else {
#[cfg(foo)]
"Do not lint cfg'd out code"
}
if b {
println!("Foobar");
} else {
empty_expansion!();
}
}

View File

@ -0,0 +1,12 @@
error: this else branch is empty
--> $DIR/needless_else.rs:24:7
|
LL | } else {
| _______^
LL | | }
| |_____^ help: you can remove it
|
= note: `-D clippy::needless-else` implied by `-D warnings`
error: aborting due to previous error

View File

@ -7,7 +7,8 @@
clippy::if_same_then_else, clippy::if_same_then_else,
clippy::single_match, clippy::single_match,
clippy::needless_bool, clippy::needless_bool,
clippy::equatable_if_let clippy::equatable_if_let,
clippy::needless_else
)] )]
#![warn(clippy::needless_return)] #![warn(clippy::needless_return)]

View File

@ -7,7 +7,8 @@
clippy::if_same_then_else, clippy::if_same_then_else,
clippy::single_match, clippy::single_match,
clippy::needless_bool, clippy::needless_bool,
clippy::equatable_if_let clippy::equatable_if_let,
clippy::needless_else
)] )]
#![warn(clippy::needless_return)] #![warn(clippy::needless_return)]

View File

@ -1,390 +1,582 @@
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:27:5 --> $DIR/needless_return.rs:28:5
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= note: `-D clippy::needless-return` implied by `-D warnings` = note: `-D clippy::needless-return` implied by `-D warnings`
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:31:5 --> $DIR/needless_return.rs:32:5
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:36:5 --> $DIR/needless_return.rs:37:5
| |
LL | return true;;; LL | return true;;;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;;;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:41:5 --> $DIR/needless_return.rs:42:5
| |
LL | return true;; ; ; LL | return true;; ; ;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;; ; ;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:46:9 --> $DIR/needless_return.rs:47:9
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:48:9 --> $DIR/needless_return.rs:49:9
| |
LL | return false; LL | return false;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return false;
LL + false
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:54:17 --> $DIR/needless_return.rs:55:17
| |
LL | true => return false, LL | true => return false,
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL | true => false,
| ~~~~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:56:13 --> $DIR/needless_return.rs:57:13
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:63:9 --> $DIR/needless_return.rs:64:9
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:65:16 --> $DIR/needless_return.rs:66:16
| |
LL | let _ = || return true; LL | let _ = || return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL | let _ = || true;
| ~~~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:69:5 --> $DIR/needless_return.rs:70:5
| |
LL | return the_answer!(); LL | return the_answer!();
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return the_answer!();
LL + the_answer!()
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:72:21 --> $DIR/needless_return.rs:73:21
| |
LL | fn test_void_fun() { LL | fn test_void_fun() {
| _____________________^ | _____________________^
LL | | return; LL | | return;
| |__________^ | |__________^
| |
= help: remove `return` help: remove `return`
|
LL - fn test_void_fun() {
LL - return;
LL + fn test_void_fun() {
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:77:11 --> $DIR/needless_return.rs:78:11
| |
LL | if b { LL | if b {
| ___________^ | ___________^
LL | | return; LL | | return;
| |______________^ | |______________^
| |
= help: remove `return` help: remove `return`
|
LL - if b {
LL - return;
LL + if b {
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:79:13 --> $DIR/needless_return.rs:80:13
| |
LL | } else { LL | } else {
| _____________^ | _____________^
LL | | return; LL | | return;
| |______________^ | |______________^
| |
= help: remove `return` help: remove `return`
|
LL - } else {
LL - return;
LL + } else {
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:87:14 --> $DIR/needless_return.rs:88:14
| |
LL | _ => return, LL | _ => return,
| ^^^^^^ | ^^^^^^
| |
= help: replace `return` with a unit value help: replace `return` with a unit value
|
LL | _ => (),
| ~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:95:24 --> $DIR/needless_return.rs:96:24
| |
LL | let _ = 42; LL | let _ = 42;
| ________________________^ | ________________________^
LL | | return; LL | | return;
| |__________________^ | |__________________^
| |
= help: remove `return` help: remove `return`
|
LL - let _ = 42;
LL - return;
LL + let _ = 42;
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:98:14 --> $DIR/needless_return.rs:99:14
| |
LL | _ => return, LL | _ => return,
| ^^^^^^ | ^^^^^^
| |
= help: replace `return` with a unit value help: replace `return` with a unit value
|
LL | _ => (),
| ~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:111:9 --> $DIR/needless_return.rs:112:9
| |
LL | return String::from("test"); LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return String::from("test");
LL + String::from("test")
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:113:9 --> $DIR/needless_return.rs:114:9
| |
LL | return String::new(); LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return String::new();
LL + String::new()
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:135:32 --> $DIR/needless_return.rs:136:32
| |
LL | bar.unwrap_or_else(|_| return) LL | bar.unwrap_or_else(|_| return)
| ^^^^^^ | ^^^^^^
| |
= help: replace `return` with an empty block help: replace `return` with an empty block
|
LL | bar.unwrap_or_else(|_| {})
| ~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:139:21 --> $DIR/needless_return.rs:140:21
| |
LL | let _ = || { LL | let _ = || {
| _____________________^ | _____________________^
LL | | return; LL | | return;
| |__________________^ | |__________________^
| |
= help: remove `return` help: remove `return`
|
LL - let _ = || {
LL - return;
LL + let _ = || {
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:142:20 --> $DIR/needless_return.rs:143:20
| |
LL | let _ = || return; LL | let _ = || return;
| ^^^^^^ | ^^^^^^
| |
= help: replace `return` with an empty block help: replace `return` with an empty block
|
LL | let _ = || {};
| ~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:148:32 --> $DIR/needless_return.rs:149:32
| |
LL | res.unwrap_or_else(|_| return Foo) LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^ | ^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL | res.unwrap_or_else(|_| Foo)
| ~~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:157:5 --> $DIR/needless_return.rs:158:5
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:161:5 --> $DIR/needless_return.rs:162:5
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:166:9 --> $DIR/needless_return.rs:167:9
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:168:9 --> $DIR/needless_return.rs:169:9
| |
LL | return false; LL | return false;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return false;
LL + false
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:174:17 --> $DIR/needless_return.rs:175:17
| |
LL | true => return false, LL | true => return false,
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL | true => false,
| ~~~~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:176:13 --> $DIR/needless_return.rs:177:13
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:183:9 --> $DIR/needless_return.rs:184:9
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return true;
LL + true
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:185:16 --> $DIR/needless_return.rs:186:16
| |
LL | let _ = || return true; LL | let _ = || return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL | let _ = || true;
| ~~~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:189:5 --> $DIR/needless_return.rs:190:5
| |
LL | return the_answer!(); LL | return the_answer!();
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return the_answer!();
LL + the_answer!()
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:192:33 --> $DIR/needless_return.rs:193:33
| |
LL | async fn async_test_void_fun() { LL | async fn async_test_void_fun() {
| _________________________________^ | _________________________________^
LL | | return; LL | | return;
| |__________^ | |__________^
| |
= help: remove `return` help: remove `return`
|
LL - async fn async_test_void_fun() {
LL - return;
LL + async fn async_test_void_fun() {
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:197:11 --> $DIR/needless_return.rs:198:11
| |
LL | if b { LL | if b {
| ___________^ | ___________^
LL | | return; LL | | return;
| |______________^ | |______________^
| |
= help: remove `return` help: remove `return`
|
LL - if b {
LL - return;
LL + if b {
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:199:13 --> $DIR/needless_return.rs:200:13
| |
LL | } else { LL | } else {
| _____________^ | _____________^
LL | | return; LL | | return;
| |______________^ | |______________^
| |
= help: remove `return` help: remove `return`
|
LL - } else {
LL - return;
LL + } else {
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:207:14 --> $DIR/needless_return.rs:208:14
| |
LL | _ => return, LL | _ => return,
| ^^^^^^ | ^^^^^^
| |
= help: replace `return` with a unit value help: replace `return` with a unit value
|
LL | _ => (),
| ~~
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:220:9 --> $DIR/needless_return.rs:221:9
| |
LL | return String::from("test"); LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return String::from("test");
LL + String::from("test")
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:222:9 --> $DIR/needless_return.rs:223:9
| |
LL | return String::new(); LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return String::new();
LL + String::new()
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:238:5 --> $DIR/needless_return.rs:239:5
| |
LL | return format!("Hello {}", "world!"); LL | return format!("Hello {}", "world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return format!("Hello {}", "world!");
LL + format!("Hello {}", "world!")
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:250:9 --> $DIR/needless_return.rs:251:9
| |
LL | return true; LL | return true;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL ~ true
LL | } else {
LL | return false;
LL ~ }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:252:9 --> $DIR/needless_return.rs:253:9
| |
LL | return false; LL | return false;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL ~ false
LL ~ }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:259:13 --> $DIR/needless_return.rs:260:13
| |
LL | return 10; LL | return 10;
| ^^^^^^^^^ | ^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL ~ 10
LL | },
...
LL | },
LL ~ }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:262:13 --> $DIR/needless_return.rs:263:13
| |
LL | return 100; LL | return 100;
| ^^^^^^^^^^ | ^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL ~ 100
LL | },
LL ~ }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:270:9 --> $DIR/needless_return.rs:271:9
| |
LL | return 0; LL | return 0;
| ^^^^^^^^ | ^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL ~ 0
LL ~ }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:277:13 --> $DIR/needless_return.rs:278:13
| |
LL | return *(x as *const isize); LL | return *(x as *const isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL ~ *(x as *const isize)
LL | } else {
LL | return !*(x as *const isize);
LL ~ }
LL ~ }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:279:13 --> $DIR/needless_return.rs:280:13
| |
LL | return !*(x as *const isize); LL | return !*(x as *const isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL ~ !*(x as *const isize)
LL ~ }
LL ~ }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:286:20 --> $DIR/needless_return.rs:287:20
| |
LL | let _ = 42; LL | let _ = 42;
| ____________________^ | ____________________^
@ -392,47 +584,73 @@ LL | |
LL | | return; LL | | return;
| |______________^ | |______________^
| |
= help: remove `return` help: remove `return`
|
LL - let _ = 42;
LL -
LL - return;
LL + let _ = 42;
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:293:20 --> $DIR/needless_return.rs:294:20
| |
LL | let _ = 42; return; LL | let _ = 42; return;
| ^^^^^^^ | ^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - let _ = 42; return;
LL + let _ = 42;
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:305:9 --> $DIR/needless_return.rs:306:9
| |
LL | return Ok(format!("ok!")); LL | return Ok(format!("ok!"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return Ok(format!("ok!"));
LL + Ok(format!("ok!"))
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:307:9 --> $DIR/needless_return.rs:308:9
| |
LL | return Err(format!("err!")); LL | return Err(format!("err!"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return Err(format!("err!"));
LL + Err(format!("err!"))
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:313:9 --> $DIR/needless_return.rs:314:9
| |
LL | return if true { 1 } else { 2 }; LL | return if true { 1 } else { 2 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` help: remove `return`
|
LL - return if true { 1 } else { 2 };
LL + if true { 1 } else { 2 }
|
error: unneeded `return` statement error: unneeded `return` statement
--> $DIR/needless_return.rs:317:9 --> $DIR/needless_return.rs:318:9
| |
LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: remove `return` and wrap the sequence with parentheses help: remove `return` and wrap the sequence with parentheses
|
LL - return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 })
|
error: aborting due to 52 previous errors error: aborting due to 52 previous errors

View File

@ -110,3 +110,17 @@ fn issue_10435() {
println!("{}", line!()); println!("{}", line!());
} }
} }
fn issue10836() {
struct Foo(bool);
impl std::ops::Not for Foo {
type Output = bool;
fn not(self) -> Self::Output {
!self.0
}
}
// Should not lint
let _: bool = !!Foo(true);
}

View File

@ -0,0 +1,55 @@
//@run-rustfix
//@aux-build:proc_macros.rs
#![warn(clippy::ptr_cast_constness)]
extern crate proc_macros;
use proc_macros::{external, inline_macros};
#[inline_macros]
fn main() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = ptr as *const i32;
let _ = mut_ptr as *mut i32;
// Make sure the lint can handle the difference in their operator precedences.
unsafe {
let ptr_ptr: *const *const u32 = &ptr;
let _ = (*ptr_ptr).cast_mut();
}
let _ = ptr.cast_mut();
let _ = mut_ptr.cast_const();
// Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized
let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
let _ = ptr_of_array as *const [u32];
let _ = ptr_of_array as *const dyn std::fmt::Debug;
// Make sure the lint is triggered inside a macro
let _ = inline!($ptr as *const i32);
// Do not lint inside macros from external crates
let _ = external!($ptr as *const i32);
}
#[clippy::msrv = "1.64"]
fn _msrv_1_64() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
// `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this
let _ = ptr as *mut i32;
let _ = mut_ptr as *const i32;
}
#[clippy::msrv = "1.65"]
fn _msrv_1_65() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = ptr.cast_mut();
let _ = mut_ptr.cast_const();
}

View File

@ -0,0 +1,55 @@
//@run-rustfix
//@aux-build:proc_macros.rs
#![warn(clippy::ptr_cast_constness)]
extern crate proc_macros;
use proc_macros::{external, inline_macros};
#[inline_macros]
fn main() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = ptr as *const i32;
let _ = mut_ptr as *mut i32;
// Make sure the lint can handle the difference in their operator precedences.
unsafe {
let ptr_ptr: *const *const u32 = &ptr;
let _ = *ptr_ptr as *mut i32;
}
let _ = ptr as *mut i32;
let _ = mut_ptr as *const i32;
// Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized
let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
let _ = ptr_of_array as *const [u32];
let _ = ptr_of_array as *const dyn std::fmt::Debug;
// Make sure the lint is triggered inside a macro
let _ = inline!($ptr as *const i32);
// Do not lint inside macros from external crates
let _ = external!($ptr as *const i32);
}
#[clippy::msrv = "1.64"]
fn _msrv_1_64() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
// `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this
let _ = ptr as *mut i32;
let _ = mut_ptr as *const i32;
}
#[clippy::msrv = "1.65"]
fn _msrv_1_65() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = ptr as *mut i32;
let _ = mut_ptr as *const i32;
}

View File

@ -0,0 +1,34 @@
error: `as` casting between raw pointers while changing its constness
--> $DIR/ptr_cast_constness.rs:20:17
|
LL | let _ = *ptr_ptr as *mut i32;
| ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()`
|
= note: `-D clippy::ptr-cast-constness` implied by `-D warnings`
error: `as` casting between raw pointers while changing its constness
--> $DIR/ptr_cast_constness.rs:23:13
|
LL | let _ = ptr as *mut i32;
| ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()`
error: `as` casting between raw pointers while changing its constness
--> $DIR/ptr_cast_constness.rs:24:13
|
LL | let _ = mut_ptr as *const i32;
| ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()`
error: `as` casting between raw pointers while changing its constness
--> $DIR/ptr_cast_constness.rs:53:13
|
LL | let _ = ptr as *mut i32;
| ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()`
error: `as` casting between raw pointers while changing its constness
--> $DIR/ptr_cast_constness.rs:54:13
|
LL | let _ = mut_ptr as *const i32;
| ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()`
error: aborting due to 5 previous errors

View File

@ -2,7 +2,7 @@
// Issue #5746 // Issue #5746
#![warn(clippy::redundant_pattern_matching)] #![warn(clippy::redundant_pattern_matching)]
#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_else)]
use std::task::Poll::{Pending, Ready}; use std::task::Poll::{Pending, Ready};
fn main() { fn main() {

View File

@ -2,7 +2,7 @@
// Issue #5746 // Issue #5746
#![warn(clippy::redundant_pattern_matching)] #![warn(clippy::redundant_pattern_matching)]
#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_else)]
use std::task::Poll::{Pending, Ready}; use std::task::Poll::{Pending, Ready};
fn main() { fn main() {

View File

@ -47,6 +47,7 @@ fn main() {
issue6067(); issue6067();
issue10726(); issue10726();
issue10803();
let _ = if gen_opt().is_some() { let _ = if gen_opt().is_some() {
1 1
@ -107,3 +108,14 @@ fn issue10726() {
_ => false, _ => false,
}; };
} }
fn issue10803() {
let x = Some(42);
let _ = x.is_some();
let _ = x.is_none();
// Don't lint
let _ = matches!(x, Some(16));
}

View File

@ -56,6 +56,7 @@ fn main() {
issue6067(); issue6067();
issue10726(); issue10726();
issue10803();
let _ = if let Some(_) = gen_opt() { let _ = if let Some(_) = gen_opt() {
1 1
@ -134,3 +135,14 @@ fn issue10726() {
_ => false, _ => false,
}; };
} }
fn issue10803() {
let x = Some(42);
let _ = matches!(x, Some(_));
let _ = matches!(x, None);
// Don't lint
let _ = matches!(x, Some(16));
}

View File

@ -77,49 +77,49 @@ LL | let _ = if let Some(_) = opt { true } else { false };
| -------^^^^^^^------ help: try this: `if opt.is_some()` | -------^^^^^^^------ help: try this: `if opt.is_some()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:60:20 --> $DIR/redundant_pattern_matching_option.rs:61:20
| |
LL | let _ = if let Some(_) = gen_opt() { LL | let _ = if let Some(_) = gen_opt() {
| -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:62:19 --> $DIR/redundant_pattern_matching_option.rs:63:19
| |
LL | } else if let None = gen_opt() { LL | } else if let None = gen_opt() {
| -------^^^^------------ help: try this: `if gen_opt().is_none()` | -------^^^^------------ help: try this: `if gen_opt().is_none()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:68:12 --> $DIR/redundant_pattern_matching_option.rs:69:12
| |
LL | if let Some(..) = gen_opt() {} LL | if let Some(..) = gen_opt() {}
| -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()` | -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:83:12 --> $DIR/redundant_pattern_matching_option.rs:84:12
| |
LL | if let Some(_) = Some(42) {} LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()` | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:85:12 --> $DIR/redundant_pattern_matching_option.rs:86:12
| |
LL | if let None = None::<()> {} LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()` | -------^^^^------------- help: try this: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:87:15 --> $DIR/redundant_pattern_matching_option.rs:88:15
| |
LL | while let Some(_) = Some(42) {} LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:89:15 --> $DIR/redundant_pattern_matching_option.rs:90:15
| |
LL | while let None = None::<()> {} LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()` | ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:91:5 --> $DIR/redundant_pattern_matching_option.rs:92:5
| |
LL | / match Some(42) { LL | / match Some(42) {
LL | | Some(_) => true, LL | | Some(_) => true,
@ -128,7 +128,7 @@ LL | | };
| |_____^ help: try this: `Some(42).is_some()` | |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:96:5 --> $DIR/redundant_pattern_matching_option.rs:97:5
| |
LL | / match None::<()> { LL | / match None::<()> {
LL | | Some(_) => false, LL | | Some(_) => false,
@ -137,19 +137,19 @@ LL | | };
| |_____^ help: try this: `None::<()>.is_none()` | |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:104:12 --> $DIR/redundant_pattern_matching_option.rs:105:12
| |
LL | if let None = *(&None::<()>) {} LL | if let None = *(&None::<()>) {}
| -------^^^^----------------- help: try this: `if (&None::<()>).is_none()` | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:105:12 --> $DIR/redundant_pattern_matching_option.rs:106:12
| |
LL | if let None = *&None::<()> {} LL | if let None = *&None::<()> {}
| -------^^^^--------------- help: try this: `if (&None::<()>).is_none()` | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:111:5 --> $DIR/redundant_pattern_matching_option.rs:112:5
| |
LL | / match x { LL | / match x {
LL | | Some(_) => true, LL | | Some(_) => true,
@ -158,7 +158,7 @@ LL | | };
| |_____^ help: try this: `x.is_some()` | |_____^ help: try this: `x.is_some()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:116:5 --> $DIR/redundant_pattern_matching_option.rs:117:5
| |
LL | / match x { LL | / match x {
LL | | None => true, LL | | None => true,
@ -167,7 +167,7 @@ LL | | };
| |_____^ help: try this: `x.is_none()` | |_____^ help: try this: `x.is_none()`
error: redundant pattern matching, consider using `is_none()` error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:121:5 --> $DIR/redundant_pattern_matching_option.rs:122:5
| |
LL | / match x { LL | / match x {
LL | | Some(_) => false, LL | | Some(_) => false,
@ -176,7 +176,7 @@ LL | | };
| |_____^ help: try this: `x.is_none()` | |_____^ help: try this: `x.is_none()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:126:5 --> $DIR/redundant_pattern_matching_option.rs:127:5
| |
LL | / match x { LL | / match x {
LL | | None => false, LL | | None => false,
@ -184,5 +184,17 @@ LL | | _ => true,
LL | | }; LL | | };
| |_____^ help: try this: `x.is_some()` | |_____^ help: try this: `x.is_some()`
error: aborting due to 26 previous errors error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_option.rs:142:13
|
LL | let _ = matches!(x, Some(_));
| ^^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_some()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching_option.rs:144:13
|
LL | let _ = matches!(x, None);
| ^^^^^^^^^^^^^^^^^ help: try this: `x.is_none()`
error: aborting due to 28 previous errors

View File

@ -44,6 +44,7 @@ fn main() {
issue6067(); issue6067();
issue6065(); issue6065();
issue10726(); issue10726();
issue10803();
let _ = if gen_res().is_ok() { let _ = if gen_res().is_ok() {
1 1
@ -133,3 +134,17 @@ fn issue10726() {
_ => true, _ => true,
}; };
} }
fn issue10803() {
let x: Result<i32, i32> = Ok(42);
let _ = x.is_ok();
let _ = x.is_err();
// Don't lint
let _ = matches!(x, Ok(16));
// Don't lint
let _ = matches!(x, Err(16));
}

View File

@ -56,6 +56,7 @@ fn main() {
issue6067(); issue6067();
issue6065(); issue6065();
issue10726(); issue10726();
issue10803();
let _ = if let Ok(_) = gen_res() { let _ = if let Ok(_) = gen_res() {
1 1
@ -163,3 +164,17 @@ fn issue10726() {
_ => true, _ => true,
}; };
} }
fn issue10803() {
let x: Result<i32, i32> = Ok(42);
let _ = matches!(x, Ok(_));
let _ = matches!(x, Err(_));
// Don't lint
let _ = matches!(x, Ok(16));
// Don't lint
let _ = matches!(x, Err(16));
}

View File

@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
| -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()` | -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
error: redundant pattern matching, consider using `is_ok()` error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching_result.rs:60:20 --> $DIR/redundant_pattern_matching_result.rs:61:20
| |
LL | let _ = if let Ok(_) = gen_res() { LL | let _ = if let Ok(_) = gen_res() {
| -------^^^^^------------ help: try this: `if gen_res().is_ok()` | -------^^^^^------------ help: try this: `if gen_res().is_ok()`
error: redundant pattern matching, consider using `is_err()` error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching_result.rs:62:19 --> $DIR/redundant_pattern_matching_result.rs:63:19
| |
LL | } else if let Err(_) = gen_res() { LL | } else if let Err(_) = gen_res() {
| -------^^^^^^------------ help: try this: `if gen_res().is_err()` | -------^^^^^^------------ help: try this: `if gen_res().is_err()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:85:19 --> $DIR/redundant_pattern_matching_result.rs:86:19
| |
LL | while let Some(_) = r#try!(result_opt()) {} LL | while let Some(_) = r#try!(result_opt()) {}
| ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:86:16 --> $DIR/redundant_pattern_matching_result.rs:87:16
| |
LL | if let Some(_) = r#try!(result_opt()) {} LL | if let Some(_) = r#try!(result_opt()) {}
| -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:92:12 --> $DIR/redundant_pattern_matching_result.rs:93:12
| |
LL | if let Some(_) = m!() {} LL | if let Some(_) = m!() {}
| -------^^^^^^^------- help: try this: `if m!().is_some()` | -------^^^^^^^------- help: try this: `if m!().is_some()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:93:15 --> $DIR/redundant_pattern_matching_result.rs:94:15
| |
LL | while let Some(_) = m!() {} LL | while let Some(_) = m!() {}
| ----------^^^^^^^------- help: try this: `while m!().is_some()` | ----------^^^^^^^------- help: try this: `while m!().is_some()`
error: redundant pattern matching, consider using `is_ok()` error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching_result.rs:111:12 --> $DIR/redundant_pattern_matching_result.rs:112:12
| |
LL | if let Ok(_) = Ok::<i32, i32>(42) {} LL | if let Ok(_) = Ok::<i32, i32>(42) {}
| -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()` | -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()` error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching_result.rs:113:12 --> $DIR/redundant_pattern_matching_result.rs:114:12
| |
LL | if let Err(_) = Err::<i32, i32>(42) {} LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()` | -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()` error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching_result.rs:115:15 --> $DIR/redundant_pattern_matching_result.rs:116:15
| |
LL | while let Ok(_) = Ok::<i32, i32>(10) {} LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()` | ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()` error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching_result.rs:117:15 --> $DIR/redundant_pattern_matching_result.rs:118:15
| |
LL | while let Err(_) = Ok::<i32, i32>(10) {} LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()` | ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_ok()` error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching_result.rs:119:5 --> $DIR/redundant_pattern_matching_result.rs:120:5
| |
LL | / match Ok::<i32, i32>(42) { LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true, LL | | Ok(_) => true,
@ -142,7 +142,7 @@ LL | | };
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()` | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()` error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching_result.rs:124:5 --> $DIR/redundant_pattern_matching_result.rs:125:5
| |
LL | / match Err::<i32, i32>(42) { LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false, LL | | Ok(_) => false,
@ -151,7 +151,7 @@ LL | | };
| |_____^ help: try this: `Err::<i32, i32>(42).is_err()` | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()` error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching_result.rs:134:5 --> $DIR/redundant_pattern_matching_result.rs:135:5
| |
LL | / match x { LL | / match x {
LL | | Ok(_) => true, LL | | Ok(_) => true,
@ -160,7 +160,7 @@ LL | | };
| |_____^ help: try this: `x.is_ok()` | |_____^ help: try this: `x.is_ok()`
error: redundant pattern matching, consider using `is_err()` error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching_result.rs:139:5 --> $DIR/redundant_pattern_matching_result.rs:140:5
| |
LL | / match x { LL | / match x {
LL | | Ok(_) => false, LL | | Ok(_) => false,
@ -169,7 +169,7 @@ LL | | };
| |_____^ help: try this: `x.is_err()` | |_____^ help: try this: `x.is_err()`
error: redundant pattern matching, consider using `is_err()` error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching_result.rs:144:5 --> $DIR/redundant_pattern_matching_result.rs:145:5
| |
LL | / match x { LL | / match x {
LL | | Err(_) => true, LL | | Err(_) => true,
@ -178,7 +178,7 @@ LL | | };
| |_____^ help: try this: `x.is_err()` | |_____^ help: try this: `x.is_err()`
error: redundant pattern matching, consider using `is_ok()` error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching_result.rs:149:5 --> $DIR/redundant_pattern_matching_result.rs:150:5
| |
LL | / match x { LL | / match x {
LL | | Err(_) => false, LL | | Err(_) => false,
@ -186,5 +186,17 @@ LL | | _ => true,
LL | | }; LL | | };
| |_____^ help: try this: `x.is_ok()` | |_____^ help: try this: `x.is_ok()`
error: aborting due to 26 previous errors error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching_result.rs:171:13
|
LL | let _ = matches!(x, Ok(_));
| ^^^^^^^^^^^^^^^^^^ help: try this: `x.is_ok()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching_result.rs:173:13
|
LL | let _ = matches!(x, Err(_));
| ^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_err()`
error: aborting due to 28 previous errors

View File

@ -42,6 +42,11 @@ fn syntax_error() {
let escaped_string_span = Regex::new("\\b\\c"); let escaped_string_span = Regex::new("\\b\\c");
let aux_span = Regex::new("(?ixi)"); let aux_span = Regex::new("(?ixi)");
let should_not_lint = Regex::new("(?u).");
let should_not_lint = BRegex::new("(?u).");
let invalid_utf8_should_not_lint = BRegex::new("(?-u).");
let invalid_utf8_should_lint = Regex::new("(?-u).");
} }
fn trivial_regex() { fn trivial_regex() {
@ -71,6 +76,8 @@ fn trivial_regex() {
// non-trivial regexes // non-trivial regexes
let non_trivial_dot = Regex::new("a.b"); let non_trivial_dot = Regex::new("a.b");
let non_trivial_dot_builder = RegexBuilder::new("a.b"); let non_trivial_dot_builder = RegexBuilder::new("a.b");
let non_trivial_dot = Regex::new(".");
let non_trivial_dot = BRegex::new(".");
let non_trivial_eq = Regex::new("^foo|bar$"); let non_trivial_eq = Regex::new("^foo|bar$");
let non_trivial_starts_with = Regex::new("^foo|bar"); let non_trivial_starts_with = Regex::new("^foo|bar");
let non_trivial_ends_with = Regex::new("^foo|bar"); let non_trivial_ends_with = Regex::new("^foo|bar");

View File

@ -99,8 +99,14 @@ error: regex syntax error: duplicate flag
LL | let aux_span = Regex::new("(?ixi)"); LL | let aux_span = Regex::new("(?ixi)");
| ^ ^ | ^ ^
error: regex syntax error: pattern can match invalid UTF-8
--> $DIR/regex.rs:49:53
|
LL | let invalid_utf8_should_lint = Regex::new("(?-u).");
| ^
error: trivial regex error: trivial regex
--> $DIR/regex.rs:48:33 --> $DIR/regex.rs:53:33
| |
LL | let trivial_eq = Regex::new("^foobar$"); LL | let trivial_eq = Regex::new("^foobar$");
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -108,7 +114,7 @@ LL | let trivial_eq = Regex::new("^foobar$");
= help: consider using `==` on `str`s = help: consider using `==` on `str`s
error: trivial regex error: trivial regex
--> $DIR/regex.rs:50:48 --> $DIR/regex.rs:55:48
| |
LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -116,7 +122,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
= help: consider using `==` on `str`s = help: consider using `==` on `str`s
error: trivial regex error: trivial regex
--> $DIR/regex.rs:52:42 --> $DIR/regex.rs:57:42
| |
LL | let trivial_starts_with = Regex::new("^foobar"); LL | let trivial_starts_with = Regex::new("^foobar");
| ^^^^^^^^^ | ^^^^^^^^^
@ -124,7 +130,7 @@ LL | let trivial_starts_with = Regex::new("^foobar");
= help: consider using `str::starts_with` = help: consider using `str::starts_with`
error: trivial regex error: trivial regex
--> $DIR/regex.rs:54:40 --> $DIR/regex.rs:59:40
| |
LL | let trivial_ends_with = Regex::new("foobar$"); LL | let trivial_ends_with = Regex::new("foobar$");
| ^^^^^^^^^ | ^^^^^^^^^
@ -132,7 +138,7 @@ LL | let trivial_ends_with = Regex::new("foobar$");
= help: consider using `str::ends_with` = help: consider using `str::ends_with`
error: trivial regex error: trivial regex
--> $DIR/regex.rs:56:39 --> $DIR/regex.rs:61:39
| |
LL | let trivial_contains = Regex::new("foobar"); LL | let trivial_contains = Regex::new("foobar");
| ^^^^^^^^ | ^^^^^^^^
@ -140,7 +146,7 @@ LL | let trivial_contains = Regex::new("foobar");
= help: consider using `str::contains` = help: consider using `str::contains`
error: trivial regex error: trivial regex
--> $DIR/regex.rs:58:39 --> $DIR/regex.rs:63:39
| |
LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
@ -148,7 +154,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
= help: consider using `str::contains` = help: consider using `str::contains`
error: trivial regex error: trivial regex
--> $DIR/regex.rs:60:40 --> $DIR/regex.rs:65:40
| |
LL | let trivial_backslash = Regex::new("a/.b"); LL | let trivial_backslash = Regex::new("a/.b");
| ^^^^^^^ | ^^^^^^^
@ -156,7 +162,7 @@ LL | let trivial_backslash = Regex::new("a/.b");
= help: consider using `str::contains` = help: consider using `str::contains`
error: trivial regex error: trivial regex
--> $DIR/regex.rs:63:36 --> $DIR/regex.rs:68:36
| |
LL | let trivial_empty = Regex::new(""); LL | let trivial_empty = Regex::new("");
| ^^ | ^^
@ -164,7 +170,7 @@ LL | let trivial_empty = Regex::new("");
= help: the regex is unlikely to be useful as it is = help: the regex is unlikely to be useful as it is
error: trivial regex error: trivial regex
--> $DIR/regex.rs:65:36 --> $DIR/regex.rs:70:36
| |
LL | let trivial_empty = Regex::new("^"); LL | let trivial_empty = Regex::new("^");
| ^^^ | ^^^
@ -172,7 +178,7 @@ LL | let trivial_empty = Regex::new("^");
= help: the regex is unlikely to be useful as it is = help: the regex is unlikely to be useful as it is
error: trivial regex error: trivial regex
--> $DIR/regex.rs:67:36 --> $DIR/regex.rs:72:36
| |
LL | let trivial_empty = Regex::new("^$"); LL | let trivial_empty = Regex::new("^$");
| ^^^^ | ^^^^
@ -180,12 +186,12 @@ LL | let trivial_empty = Regex::new("^$");
= help: consider using `str::is_empty` = help: consider using `str::is_empty`
error: trivial regex error: trivial regex
--> $DIR/regex.rs:69:44 --> $DIR/regex.rs:74:44
| |
LL | let binary_trivial_empty = BRegex::new("^$"); LL | let binary_trivial_empty = BRegex::new("^$");
| ^^^^ | ^^^^
| |
= help: consider using `str::is_empty` = help: consider using `str::is_empty`
error: aborting due to 23 previous errors error: aborting due to 24 previous errors

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