diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 61efaa3bf3e6d..b6033de935012 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5380,6 +5380,7 @@ Released 2018-09-13 [`arc_with_non_send_sync`]: https://rust-lang.github.io/rust-clippy/master/index.html#arc_with_non_send_sync [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_pointer_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_pointer_underscore [`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut [`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants @@ -5490,6 +5491,7 @@ Released 2018-09-13 [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown +[`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg @@ -5685,6 +5687,7 @@ Released 2018-09-13 [`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist [`lint_groups_priority`]: https://rust-lang.github.io/rust-clippy/master/index.html#lint_groups_priority +[`literal_string_with_formatting_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_args [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal @@ -5966,6 +5969,7 @@ Released 2018-09-13 [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts +[`repr_packed_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#repr_packed_without_abi [`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used @@ -6210,6 +6214,7 @@ Released 2018-09-13 [`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero [`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests +[`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests [`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 77910917963ee..c354e8914f5db 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -37,7 +37,7 @@ impl LateLintPass<'_> for MyStructLint { // Get type of `expr` let ty = cx.typeck_results().expr_ty(expr); // Match its kind to enter its type - match ty.kind { + match ty.kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"), _ => () } diff --git a/src/tools/clippy/book/src/development/infrastructure/backport.md b/src/tools/clippy/book/src/development/infrastructure/backport.md index 6920c4e465619..9526d8af1c9dc 100644 --- a/src/tools/clippy/book/src/development/infrastructure/backport.md +++ b/src/tools/clippy/book/src/development/infrastructure/backport.md @@ -5,68 +5,108 @@ Backports in Clippy are rare and should be approved by the Clippy team. For example, a backport is done, if a crucial ICE was fixed or a lint is broken to a point, that it has to be disabled, before landing on stable. -Backports are done to the `beta` branch of Clippy. Backports to stable Clippy -releases basically don't exist, since this would require a Rust point release, -which is almost never justifiable for a Clippy fix. +> Note: If you think a PR should be backported you can label it with +> `beta-nominated`. This has to be done before the Thursday the week before the +> release. +## Filtering PRs to backport -## Backport the changes +First, find all labeled PRs using [this filter][beta-accepted-prs]. + +Next, look at each PR individually. There are a few things to check. Those need +some explanation and are quite subjective. Good judgement is required. + +1. **Is the fix worth a backport?** + + This is really subjective. An ICE fix usually is. Moving a lint to a _lower_ + group (from warn- to allow-by-default) usually as well. An FP fix usually not + (on its own). If a backport is done anyway, FP fixes might also be included. + If the PR has a lot of changes, backports must be considered more carefully. + +2. **Is the problem that was fixed by the PR already in `beta`?** + + It could be that the problem that was fixed by the PR hasn't made it to the + `beta` branch of the Rust repo yet. If that's the case, and the fix is + already synced to the Rust repo, the fix doesn't need to be backported, as it + will hit stable together with the commit that introduced the problem. If the + fix PR is not synced yet, the fix PR either needs to be "backported" to the + Rust `master` branch or to `beta` in the next backport cycle. + +3. **Make sure that the fix is on `master` before porting to `beta`** + + The fix must already be synced to the Rust `master` branch. Otherwise, the + next `beta` will be missing this fix again. If it is not yet in `master` it + should probably not be backported. If the backport is really important, do an + out-of-cycle sync first. However, the out-of-cycle sync should be small, + because the changes in that sync will get right into `beta`, without being + tested in `nightly` first. + +[beta-accepted-prs]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-nominated + +## Preparation + +> Note: All commands in this chapter will be run in the Rust clone. + +Follow the instructions in [defining remotes] to define the `clippy-upstream` +remote in the Rust repository. -Backports are done on the beta branch of the Clippy repository. +After that, fetch the remote with ```bash -# Assuming the current directory corresponds to the Clippy repository -$ git checkout beta -$ git checkout -b backport -$ git cherry-pick # `` is the commit hash of the commit(s), that should be backported -$ git push origin backport +git fetch clippy-upstream master ``` -Now you should test that the backport passes all the tests in the Rust -repository. You can do this with: +Then, switch to the `beta` branch: ```bash -# Assuming the current directory corresponds to the Rust repository -$ git checkout beta -# Make sure to change `your-github-name` to your github name in the following command -$ git subtree pull -p src/tools/clippy https://github.com//rust-clippy backport -$ ./x.py test src/tools/clippy +git switch beta +git fetch upstream +git reset --hard upstream/beta ``` -Should the test fail, you can fix Clippy directly in the Rust repository. This -has to be first applied to the Clippy beta branch and then again synced to the -Rust repository, though. The easiest way to do this is: +[defining remotes]: release.md#defining-remotes + +## Backport the changes + +When a PR is merged with the GitHub merge queue, the PR is closed with the message + +> \ (#\) + +This commit needs to be backported. To do that, find the `` of that commit +and run the following command in the clone of the **Rust repository**: ```bash -# In the Rust repository -$ git diff --patch --relative=src/tools/clippy > clippy.patch -# In the Clippy repository -$ git apply /path/to/clippy.patch -$ git add -u -$ git commit -m "Fix rustup fallout" -$ git push origin backport +git cherry-pick -m 1 `` ``` -After this, you can open a PR to the `beta` branch of the Clippy repository. +Do this for all PRs that should be backported. +## Open PR in the Rust repository -## Update Clippy in the Rust Repository +Next, open the PR for the backport. Make sure, the PR is opened towards the +`beta` branch and not the `master` branch. The PR description should look like +this: -This step must be done, **after** the PR of the previous step was merged. +``` +[beta] Clippy backports -After the backport landed in the Clippy repository, the branch has to be synced -back to the beta branch of the Rust repository. +r? @Mark-Simulacrum -```bash -# Assuming the current directory corresponds to the Rust repository -$ git checkout beta -$ git checkout -b clippy_backport -$ git subtree pull -p src/tools/clippy https://github.com/rust-lang/rust-clippy beta -$ git push origin clippy_backport +Backports: +- +- ... + + ``` -Make sure to test the backport in the Rust repository before opening a PR. This -is done with `./x.py test src/tools/clippy`. If that passes all tests, open a PR -to the `beta` branch of the Rust repository. In this PR you should tag the -Clippy team member, that agreed to the backport or the `@rust-lang/clippy` team. -Make sure to add `[beta]` to the title of the PR. +Mark is from the release team and they ultimately have to merge the PR before +branching a new `beta` version. Tag them to take care of the backport. Next, +list all the backports and give a short summary what's backported and why it is +worth backporting this. + +## Relabel backported PRs + +When a PR is backported to Rust `beta`, label the PR with `beta-accepted`. This +will then get picked up when [writing the changelog]. + +[writing the changelog]: changelog_update.md#31-include-beta-accepted-prs diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md index 98fabf8e89aee..20b870eb69a8e 100644 --- a/src/tools/clippy/book/src/development/infrastructure/release.md +++ b/src/tools/clippy/book/src/development/infrastructure/release.md @@ -7,112 +7,114 @@ Clippy is released together with stable Rust releases. The dates for these releases can be found at the [Rust Forge]. This document explains the necessary steps to create a Clippy release. -1. [Remerge the `beta` branch](#remerge-the-beta-branch) -2. [Update the `beta` branch](#update-the-beta-branch) -3. [Find the Clippy commit](#find-the-clippy-commit) -4. [Tag the stable commit](#tag-the-stable-commit) -5. [Update `CHANGELOG.md`](#update-changelogmd) - -> _NOTE:_ This document is for stable Rust releases, not for point releases. For -> point releases, step 1. and 2. should be enough. +1. [Defining Remotes](#defining-remotes) +1. [Bump Version](#bump-version) +1. [Find the Clippy commit](#find-the-clippy-commit) +1. [Update the `beta` branch](#update-the-beta-branch) +1. [Update the `stable` branch](#update-the-stable-branch) +1. [Tag the stable commit](#tag-the-stable-commit) +1. [Update `CHANGELOG.md`](#update-changelogmd) [Rust Forge]: https://forge.rust-lang.org/ -## Remerge the `beta` branch +## Defining Remotes + +You may want to define the `upstream` remote of the Clippy project to simplify +the following steps. However, this is optional and you can replace `upstream` +with the full URL instead. + +```bash +git remote add upstream git@github.com:rust-lang/rust-clippy +``` -This step is only necessary, if since the last release something was backported -to the beta Rust release. The remerge is then necessary, to make sure that the -Clippy commit, that was used by the now stable Rust release, persists in the -tree of the Clippy repository. +## Bump Version -To find out if this step is necessary run +When a release needs to be done, `cargo test` will fail, if the versions in the +`Cargo.toml` are not correct. During that sync, the versions need to be bumped. +This is done by running: ```bash -# Assumes that the local master branch of rust-lang/rust-clippy is up-to-date -$ git fetch upstream -$ git branch master --contains upstream/beta +cargo dev release bump_version ``` -If this command outputs `master`, this step is **not** necessary. +This will increase the version number of each relevant `Cargo.toml` file. After +that, just commit the updated files with: ```bash -# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy -$ git checkout -b backport_remerge -$ git merge upstream/beta -$ git diff # This diff has to be empty, otherwise something with the remerge failed -$ git push origin backport_remerge # This can be pushed to your fork +git commit -m "Bump Clippy version -> 0.1.XY" **/*Cargo.toml ``` -After this, open a PR to the master branch. In this PR, the commit hash of the -`HEAD` of the `beta` branch must exist. In addition to that, no files should be -changed by this PR. +`XY` should be exchanged with the corresponding version -## Update the `beta` branch +## Find the Clippy commit -This step must be done **after** the PR of the previous step was merged. +For both updating the `beta` and the `stable` branch, the first step is to find +the Clippy commit of the last Clippy sync done in the respective Rust branch. -First, the Clippy commit of the `beta` branch of the Rust repository has to be -determined. +Running the following commands _in the Rust repo_ will get the commit for the +specified ``: ```bash -# Assuming the current directory corresponds to the Rust repository -$ git fetch upstream -$ git checkout upstream/beta -$ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") +git switch +SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") ``` -After finding the Clippy commit, the `beta` branch in the Clippy repository can -be updated. +Where `` is one of `stable`, `beta`, or `master`. + +## Update the `beta` branch + +After getting the commit of the `beta` branch, the `beta` branch in the Clippy +repository can be updated. ```bash -# Assuming the current directory corresponds to the Clippy repository -$ git checkout beta -$ git reset --hard $BETA_SHA -$ git push upstream beta +git checkout beta +git reset --hard $SHA +git push upstream beta ``` -## Find the Clippy commit +## Update the `stable` branch -The first step is to tag the Clippy commit, that is included in the stable Rust -release. This commit can be found in the Rust repository. +After getting the commit of the `stable` branch, the `stable` branch in the +Clippy repository can be updated. ```bash -# Assuming the current directory corresponds to the Rust repository -$ git fetch upstream # `upstream` is the `rust-lang/rust` remote -$ git checkout 1.XX.0 # XX should be exchanged with the corresponding version -$ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") +git checkout stable +git reset --hard $SHA +git push upstream stable ``` -## Tag the stable commit +## Tag the `stable` commit -After finding the Clippy commit, it can be tagged with the release number. +After updating the `stable` branch, tag the HEAD commit and push it to the +Clippy repo. + +> Note: Only push the tag once the Deploy GitHub action of the `beta` branch is +> finished. Otherwise the deploy for the tag might fail. ```bash -# Assuming the current directory corresponds to the Clippy repository -$ git checkout $SHA -$ git tag rust-1.XX.0 # XX should be exchanged with the corresponding version -$ git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote +git tag rust-1.XX.0 # XX should be exchanged with the corresponding version +git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote ``` After this, the release should be available on the Clippy [release page]. [release page]: https://github.com/rust-lang/rust-clippy/releases -## Update the `stable` branch +## Publish `clippy_utils` + +The `clippy_utils` crate is published to `crates.io` without any stability +guarantees. To do this, after the [sync] and the release is done, switch back to +the `upstream/master` branch and publish `clippy_utils`: -At this step you should have already checked out the commit of the `rust-1.XX.0` -tag. Updating the stable branch from here is as easy as: +> Note: The Rustup PR bumping the nightly and Clippy version **must** be merged +> before doing this. ```bash -# Assuming the current directory corresponds to the Clippy repository and the -# commit of the just created rust-1.XX.0 tag is checked out. -$ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote +git switch master && git pull upstream master +cargo publish --manifest-path clippy_utils/Cargo.toml ``` -> _NOTE:_ Usually there are no stable backports for Clippy, so this update -> should be possible without force pushing or anything like this. If there -> should have happened a stable backport, make sure to re-merge those changes -> just as with the `beta` branch. +[sync]: sync.md ## Update `CHANGELOG.md` diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md index e1fe92f95250d..da1ad586607f9 100644 --- a/src/tools/clippy/book/src/development/infrastructure/sync.md +++ b/src/tools/clippy/book/src/development/infrastructure/sync.md @@ -21,6 +21,8 @@ to beta. For reference, the first sync following this cadence was performed the This process is described in detail in the following sections. For general information about `subtree`s in the Rust repository see [the rustc-dev-guide][subtree]. +[subtree]: https://rustc-dev-guide.rust-lang.org/external-repos.html#external-dependencies-subtree + ## Patching git-subtree to work with big repos Currently, there's a bug in `git-subtree` that prevents it from working properly @@ -50,23 +52,11 @@ sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subt > `bash` instead. You can do this by editing the first line of the `git-subtree` > script and changing `sh` to `bash`. -## Defining remotes - -You may want to define remotes, so you don't have to type out the remote -addresses on every sync. You can do this with the following commands (these -commands still have to be run inside the `rust` directory): - -```bash -# Set clippy-upstream remote for pulls -$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy -# Make sure to not push to the upstream repo -$ git remote set-url --push clippy-upstream DISABLED -# Set a local remote -$ git remote add clippy-local /path/to/rust-clippy -``` +> Note: The following sections assume that you have set up remotes following the +> instructions in [defining remotes]. -> Note: The following sections assume that you have set those remotes with the -> above remote names. +[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 +[defining remotes]: release.md#defining-remotes ## Performing the sync from [`rust-lang/rust`] to Clippy @@ -78,9 +68,9 @@ to be run inside the `rust` directory): `rustup check`. 3. Sync the changes to the rust-copy of Clippy to your Clippy fork: ```bash - # Be sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand + # Be sure to either use a net-new branch, e.g. `rustup`, or delete the branch beforehand # because changes cannot be fast forwarded and you have to run this command again. - git subtree push -P src/tools/clippy clippy-local sync-from-rust + git subtree push -P src/tools/clippy clippy-local rustup ``` > _Note:_ Most of the time you have to create a merge commit in the @@ -88,21 +78,22 @@ to be run inside the `rust` directory): > rust-copy of Clippy): ```bash git fetch upstream # assuming upstream is the rust-lang/rust remote - git checkout sync-from-rust + git switch rustup git merge upstream/master --no-ff ``` > Note: This is one of the few instances where a merge commit is allowed in > a PR. -4. Bump the nightly version in the Clippy repository by changing the date in the - rust-toolchain file to the current date and committing it with the message: +4. Bump the nightly version in the Clippy repository by running these commands: ```bash - git commit -m "Bump nightly version -> YYYY-MM-DD" + cargo dev sync update_nightly + git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain clippy_utils/README.md ``` 5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ask them in the [Zulip] stream.) [Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy +[`rust-lang/rust`]: https://github.com/rust-lang/rust ## Performing the sync from Clippy to [`rust-lang/rust`] @@ -111,11 +102,7 @@ All the following commands have to be run inside the `rust` directory. 1. Make sure you have checked out the latest `master` of `rust-lang/rust`. 2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash - git checkout -b sync-from-clippy + git switch -c clippy-subtree-update git subtree pull -P src/tools/clippy clippy-upstream master ``` 3. Open a PR to [`rust-lang/rust`] - -[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 -[subtree]: https://rustc-dev-guide.rust-lang.org/external-repos.html#external-dependencies-subtree -[`rust-lang/rust`]: https://github.com/rust-lang/rust diff --git a/src/tools/clippy/book/src/development/the_team.md b/src/tools/clippy/book/src/development/the_team.md index 10341791cec4b..6bc0783b166a9 100644 --- a/src/tools/clippy/book/src/development/the_team.md +++ b/src/tools/clippy/book/src/development/the_team.md @@ -72,7 +72,7 @@ you to the alumni group. You're always welcome to come back. ## The Clippy Team -[The Clippy team](https://www.rust-lang.org/governance/teams/dev-tools#Clippy%20team) +[The Clippy team](https://www.rust-lang.org/governance/teams/dev-tools#team-clippy) is responsible for maintaining Clippy. ### Duties diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md index e6da4322a1793..578836ecc5686 100644 --- a/src/tools/clippy/book/src/development/type_checking.md +++ b/src/tools/clippy/book/src/development/type_checking.md @@ -94,7 +94,7 @@ impl LateLintPass<'_> for MyStructLint { // Get type of `expr` let ty = cx.typeck_results().expr_ty(expr); // Match its kind to enter the type - match ty.kind { + match ty.kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"), _ => () } diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 275d125096e95..ea1d7d11389d1 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -81,6 +81,16 @@ Whether `expect` should be allowed in test functions or `#[cfg(test)]` * [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) +## `allow-indexing-slicing-in-tests` +Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]` + +**Default Value:** `false` + +--- +**Affected lints:** +* [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) + + ## `allow-mixed-uninlined-format-args` Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 41b56b45d9aef..bffa04f6f090b 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -291,6 +291,9 @@ define_Conf! { /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` #[lints(expect_used)] allow_expect_in_tests: bool = false, + /// Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]` + #[lints(indexing_slicing)] + allow_indexing_slicing_in_tests: bool = false, /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` #[lints(uninlined_format_args)] allow_mixed_uninlined_format_args: bool = true, diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 8719f61a89034..cf33e1444e47d 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -126,7 +126,7 @@ declare_clippy_lint! { /// /// [cargo-pgo]: https://github.com/Kobzol/cargo-pgo/blob/main/README.md /// - #[clippy::version = "1.82.0"] + #[clippy::version = "1.84.0"] pub ARBITRARY_SOURCE_ITEM_ORDERING, restriction, "arbitrary source item ordering" diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index a9766597d50d7..92efd1a4ddcda 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -7,6 +7,7 @@ mod duplicated_attributes; mod inline_always; mod mixed_attributes_style; mod non_minimal_cfg; +mod repr_attributes; mod should_panic_without_expect; mod unnecessary_clippy_cfg; mod useless_attribute; @@ -272,6 +273,44 @@ declare_clippy_lint! { "ensures that all `should_panic` attributes specify its expected panic message" } +declare_clippy_lint! { + /// ### What it does + /// Checks for items with `#[repr(packed)]`-attribute without ABI qualification + /// + /// ### Why is this bad? + /// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable. + /// While this is fine as long as the type is accessed correctly within Rust-code, most uses + /// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or + /// other external specifications. In such situations, the unstable Rust-ABI implied in + /// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change. + /// + /// In case you are relying on a well defined and stable memory layout, qualify the type's + /// representation using the `C`-ABI. Otherwise, if the type in question is only ever + /// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly. + /// + /// ### Example + /// ```no_run + /// #[repr(packed)] + /// struct NetworkPacketHeader { + /// header_length: u8, + /// header_version: u16 + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[repr(C, packed)] + /// struct NetworkPacketHeader { + /// header_length: u8, + /// header_version: u16 + /// } + /// ``` + #[clippy::version = "1.84.0"] + pub REPR_PACKED_WITHOUT_ABI, + suspicious, + "ensures that `repr(packed)` always comes with a qualified ABI" +} + declare_clippy_lint! { /// ### What it does /// Checks for `any` and `all` combinators in `cfg` with only one condition. @@ -415,6 +454,7 @@ pub struct Attributes { impl_lint_pass!(Attributes => [ INLINE_ALWAYS, + REPR_PACKED_WITHOUT_ABI, ]); impl Attributes { @@ -431,6 +471,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { if is_relevant_item(cx, item) { inline_always::check(cx, item.span, item.ident.name, attrs); } + repr_attributes::check(cx, item.span, attrs, &self.msrv); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs new file mode 100644 index 0000000000000..6d1ab46aa0c1f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs @@ -0,0 +1,43 @@ +use rustc_hir::Attribute; +use rustc_lint::LateContext; +use rustc_span::{Span, sym}; + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs; + +use super::REPR_PACKED_WITHOUT_ABI; + +pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: &msrvs::Msrv) { + if msrv.meets(msrvs::REPR_RUST) { + check_packed(cx, item_span, attrs); + } +} + +fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) { + if let Some(items) = attrs.iter().find_map(|attr| { + if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) { + attr.meta_item_list() + } else { + None + } + }) && let Some(packed) = items + .iter() + .find(|item| item.ident().is_some_and(|ident| matches!(ident.name, sym::packed))) + && !items.iter().any(|item| { + item.ident() + .is_some_and(|ident| matches!(ident.name, sym::C | sym::Rust)) + }) + { + span_lint_and_then( + cx, + REPR_PACKED_WITHOUT_ABI, + item_span, + "item uses `packed` representation without ABI-qualification", + |diag| { + diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI") + .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") + .span_label(packed.span(), "`packed` representation set here"); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index e21853598c3b5..e7158a6a6b6c7 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -1,8 +1,8 @@ use super::USELESS_ATTRIBUTE; -use super::utils::{extract_clippy_lint, is_lint_level, is_word}; +use super::utils::{is_lint_level, is_word, namespace_and_lint}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, first_line_of_span}; -use rustc_ast::{Attribute, Item, ItemKind, MetaItemInner}; +use rustc_ast::{Attribute, Item, ItemKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -20,11 +20,13 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { for lint in lint_list { match item.kind { ItemKind::Use(..) => { - if let MetaItemInner::MetaItem(meta_item) = lint - && meta_item.is_word() - && let Some(ident) = meta_item.ident() + let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else { + return; + }; + + if namespace.is_none() && matches!( - ident.name.as_str(), + name.as_str(), "ambiguous_glob_reexports" | "dead_code" | "deprecated" @@ -39,9 +41,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { return; } - if extract_clippy_lint(lint).is_some_and(|symbol| { - matches!( - symbol.as_str(), + if namespace == Some(sym::clippy) + && matches!( + name.as_str(), "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate" @@ -52,7 +54,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { | "disallowed_types" | "unused_trait_names" ) - }) { + { return; } }, diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index 3bb02688bf22c..96de064290400 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -75,13 +75,18 @@ fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_> /// Returns the lint name if it is clippy lint. pub(super) fn extract_clippy_lint(lint: &MetaItemInner) -> Option { - if let Some(meta_item) = lint.meta_item() - && meta_item.path.segments.len() > 1 - && let tool_name = meta_item.path.segments[0].ident - && tool_name.name == sym::clippy - { - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - return Some(lint_name); + match namespace_and_lint(lint) { + (Some(sym::clippy), name) => name, + _ => None, + } +} + +/// Returns the lint namespace, if any, as well as the lint name. (`None`, `None`) means +/// the lint had less than 1 or more than 2 segments. +pub(super) fn namespace_and_lint(lint: &MetaItemInner) -> (Option, Option) { + match lint.meta_item().map(|m| m.path.segments.as_slice()).unwrap_or_default() { + [name] => (None, Some(name.ident.name)), + [namespace, name] => (Some(namespace.ident.name), Some(name.ident.name)), + _ => (None, None), } - None } diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index f68a7a89b3994..f8c30d1c881d0 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -5,11 +5,11 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_ast::ast::LitKind; +use rustc_attr_parsing::RustcVersion; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; -use rustc_attr_parsing::RustcVersion; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs b/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs new file mode 100644 index 0000000000000..536126fd02b3a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs @@ -0,0 +1,19 @@ +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +pub fn check<'tcx>(cx: &LateContext<'tcx>, ty_into: Ty<'_>, cast_to_hir: &'tcx rustc_hir::Ty<'tcx>) { + if let rustc_hir::TyKind::Ptr(rustc_hir::MutTy { ty, .. }) = cast_to_hir.kind + && matches!(ty.kind, rustc_hir::TyKind::Infer) + { + clippy_utils::diagnostics::span_lint_and_sugg( + cx, + super::AS_POINTER_UNDERSCORE, + cast_to_hir.span, + "using inferred pointer cast", + "use explicit type", + ty_into.to_string(), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index 4dd51dcbc9a20..67aa33ca06c31 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; -use clippy_utils::std_or_core; +use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -13,15 +14,12 @@ pub(super) fn check<'tcx>( expr: &'tcx Expr<'_>, cast_expr: &'tcx Expr<'_>, cast_to: &'tcx Ty<'_>, -) { + msrv: &Msrv, +) -> bool { if matches!(cast_to.kind, TyKind::Ptr(_)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind - && let Some(std_or_core) = std_or_core(cx) + && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id) { - let macro_name = match mutability { - Mutability::Not => "addr_of", - Mutability::Mut => "addr_of_mut", - }; let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0; // Fix #9884 @@ -31,17 +29,36 @@ pub(super) fn check<'tcx>( .get(base.hir_id) .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) }) { - return; + return false; } + let suggestion = if msrv.meets(msrvs::RAW_REF_OP) { + let operator_kind = match mutability { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + format!("&raw {operator_kind} {snip}") + } else { + let Some(std_or_core) = std_or_core(cx) else { + return false; + }; + let macro_name = match mutability { + Mutability::Not => "addr_of", + Mutability::Mut => "addr_of_mut", + }; + format!("{std_or_core}::ptr::{macro_name}!({snip})") + }; + span_lint_and_sugg( cx, BORROW_AS_PTR, expr.span, "borrow as raw pointer", "try", - format!("{std_or_core}::ptr::{macro_name}!({snip})"), + suggestion, Applicability::MachineApplicable, ); + return true; } + false } diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 8b884399f9234..c64c0e15144de 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod as_pointer_underscore; mod as_ptr_cast_mut; mod as_underscore; mod borrow_as_ptr; @@ -574,13 +575,13 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for the usage of `&expr as *const T` or - /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or - /// `ptr::addr_of_mut` instead. + /// `&mut expr as *mut T`, and suggest using `&raw const` or + /// `&raw mut` instead. /// /// ### Why is this bad? /// This would improve readability and avoid creating a reference /// that points to an uninitialized value or unaligned place. - /// Read the `ptr::addr_of` docs for more information. + /// Read the `&raw` explanation in the Reference for more information. /// /// ### Example /// ```no_run @@ -593,10 +594,10 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// let val = 1; - /// let p = std::ptr::addr_of!(val); + /// let p = &raw const val; /// /// let mut val_mut = 1; - /// let p_mut = std::ptr::addr_of_mut!(val_mut); + /// let p_mut = &raw mut val_mut; /// ``` #[clippy::version = "1.60.0"] pub BORROW_AS_PTR, @@ -726,6 +727,33 @@ declare_clippy_lint! { "using `as` to cast a reference to pointer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type. + /// + /// ### Why restrict this? + /// The conversion might include a dangerous cast that might go undetected due to the type being inferred. + /// + /// ### Example + /// ```no_run + /// fn as_usize(t: &T) -> usize { + /// // BUG: `t` is already a reference, so we will here + /// // return a dangling pointer to a temporary value instead + /// &t as *const _ as usize + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn as_usize(t: &T) -> usize { + /// t as *const T as usize + /// } + /// ``` + #[clippy::version = "1.81.0"] + pub AS_POINTER_UNDERSCORE, + restriction, + "detects `as *mut _` and `as *const _` conversion" +} + pub struct Casts { msrv: Msrv, } @@ -763,6 +791,7 @@ impl_lint_pass!(Casts => [ CAST_NAN_TO_INT, ZERO_PTR, REF_AS_PTR, + AS_POINTER_UNDERSCORE, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -805,11 +834,15 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } as_underscore::check(cx, expr, cast_to_hir); - - if self.msrv.meets(msrvs::PTR_FROM_REF) { + as_pointer_underscore::check(cx, cast_to, cast_to_hir); + + let was_borrow_as_ptr_emitted = if self.msrv.meets(msrvs::BORROW_AS_PTR) { + borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv) + } else { + false + }; + if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted { ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); - } else if self.msrv.meets(msrvs::BORROW_AS_PTR) { - borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } } diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index c85e3500ebdc9..61c92d441d097 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,8 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{SpanlessEq, if_sequence, is_else_clause, is_in_const_context}; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -120,13 +122,19 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } } - span_lint_and_help( + let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else { + unreachable!(); + }; + let lhs = Sugg::hir(cx, lhs, "..").maybe_par(); + let rhs = Sugg::hir(cx, rhs, "..").addr(); + span_lint_and_sugg( cx, COMPARISON_CHAIN, expr.span, "`if` chain can be rewritten with `match`", - None, - "consider rewriting the `if` chain to use `cmp` and `match`", + "consider rewriting the `if` chain with `match`", + format!("match {lhs}.cmp({rhs}) {{...}}"), + Applicability::HasPlaceholders, ); } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 022ed180ed8ca..7451fb909ef84 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -55,6 +55,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, + crate::attrs::REPR_PACKED_WITHOUT_ABI_INFO, crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO, crate::attrs::UNNECESSARY_CLIPPY_CFG_INFO, crate::attrs::USELESS_ATTRIBUTE_INFO, @@ -75,6 +76,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::cargo::NEGATIVE_FEATURE_NAMES_INFO, crate::cargo::REDUNDANT_FEATURE_NAMES_INFO, crate::cargo::WILDCARD_DEPENDENCIES_INFO, + crate::casts::AS_POINTER_UNDERSCORE_INFO, crate::casts::AS_PTR_CAST_MUT_INFO, crate::casts::AS_UNDERSCORE_INFO, crate::casts::BORROW_AS_PTR_INFO, @@ -139,6 +141,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, + crate::doc::DOC_NESTED_REFDEFS_INFO, crate::doc::EMPTY_DOCS_INFO, crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, @@ -277,6 +280,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO, crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, + crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 6819ad547f876..3b3a78cb11530 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -26,7 +26,8 @@ declare_clippy_lint! { /// To ensure that every numeric type is chosen explicitly rather than implicitly. /// /// ### Known problems - /// This lint can only be allowed at the function level or above. + /// This lint is implemented using a custom algorithm independent of rustc's inference, + /// which results in many false positives and false negatives. /// /// ### Example /// ```no_run @@ -36,8 +37,8 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// let i = 10i32; - /// let f = 1.23f64; + /// let i = 10_i32; + /// let f = 1.23_f64; /// ``` #[clippy::version = "1.52.0"] pub DEFAULT_NUMERIC_FALLBACK, diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 821312a9e401c..653726872c645 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1004,7 +1004,10 @@ fn report<'tcx>( let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { Node::Expr(e) => match e.kind { ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false, - ExprKind::Call(..) => expr.precedence() < ExprPrecedence::Unambiguous || matches!(expr.kind, ExprKind::Field(..)), + ExprKind::Call(..) => { + expr.precedence() < ExprPrecedence::Unambiguous + || matches!(expr.kind, ExprKind::Field(..)) + }, _ => expr.precedence() < e.precedence(), }, _ => false, @@ -1017,11 +1020,7 @@ fn report<'tcx>( }) ); - let sugg = if !snip_is_macro - && needs_paren - && !has_enclosing_paren(&snip) - && !is_in_tuple - { + let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) && !is_in_tuple { format!("({snip})") } else { snip.into() diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index f65edd3625308..7c2f5efd8dd44 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -6,9 +6,7 @@ use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, pat use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{ - self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource, -}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{ @@ -453,7 +451,7 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) && !has_non_exhaustive_attr(cx.tcx, *adt) && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) - && let typing_env = typing_env_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) && let Some(local_def_id) = adt.did().as_local() // If all of our fields implement `Eq`, we can implement `Eq` too && adt @@ -484,7 +482,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De } /// Creates the `ParamEnv` used for the give type's derived `Eq` impl. -fn typing_env_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { +fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { // Initial map from generic index to param def. // Vec<(param_def, needs_eq)> let mut params = tcx diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index a0cb36f88dc05..a78c392e20866 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -43,7 +43,6 @@ declare_clippy_lint! { /// ```no_run /// use serde::Serialize; /// - /// // Example code where clippy issues a warning /// println!("warns"); /// /// // The diagnostic will contain the message "no serializing" diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 1e660b1957a44..c4ed118b7c9fc 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -35,7 +35,6 @@ declare_clippy_lint! { /// ``` /// /// ```rust,ignore - /// // Example code where clippy issues a warning /// let xs = vec![1, 2, 3, 4]; /// xs.leak(); // Vec::leak is disallowed in the config. /// // The diagnostic contains the message "no leaking memory". @@ -47,7 +46,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```rust,ignore - /// // Example code which does not raise clippy warning /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. /// xs.push(123); // Vec::push is _not_ disallowed in the config. /// ``` diff --git a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs index 0bb16a0c77d5c..4b40fc0b1ee2c 100644 --- a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::{AttrStyle}; +use rustc_ast::AttrStyle; use rustc_errors::Applicability; +use rustc_hir::{AttrArgs, AttrKind, Attribute}; use rustc_lint::LateContext; -use rustc_hir::{Attribute, AttrKind, AttrArgs}; use super::DOC_INCLUDE_WITHOUT_CFG; diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index f65acd7978a74..b2135fe18bd9e 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -5,7 +5,7 @@ mod too_long_first_doc_paragraph; use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; @@ -17,6 +17,7 @@ use pulldown_cmark::Event::{ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -563,6 +564,32 @@ declare_clippy_lint! { "check if files included in documentation are behind `cfg(doc)`" } +declare_clippy_lint! { + /// ### What it does + /// Warns if a link reference definition appears at the start of a + /// list item or quote. + /// + /// ### Why is this bad? + /// This is probably intended as an intra-doc link. If it is really + /// supposed to be a reference definition, it can be written outside + /// of the list item or quote. + /// + /// ### Example + /// ```no_run + /// //! - [link]: description + /// ``` + /// Use instead: + /// ```no_run + /// //! - [link][]: description (for intra-doc link) + /// //! + /// //! [link]: destination (for link reference definition) + /// ``` + #[clippy::version = "1.84.0"] + pub DOC_NESTED_REFDEFS, + suspicious, + "link reference defined in list item or quote" +} + pub struct Documentation { valid_idents: FxHashSet, check_private_items: bool, @@ -580,6 +607,7 @@ impl Documentation { impl_lint_pass!(Documentation => [ DOC_LINK_WITH_QUOTES, DOC_MARKDOWN, + DOC_NESTED_REFDEFS, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, @@ -831,6 +859,31 @@ fn check_doc<'a, Events: Iterator, Range { blockquote_level += 1; containers.push(Container::Blockquote); + if let Some((next_event, next_range)) = events.peek() { + let next_start = match next_event { + End(TagEnd::BlockQuote) => next_range.end, + _ => next_range.start, + }; + if let Some(refdefrange) = looks_like_refdef(doc, range.start..next_start) && + let Some(refdefspan) = fragments.span(cx, refdefrange.clone()) + { + span_lint_and_then( + cx, + DOC_NESTED_REFDEFS, + refdefspan, + "link reference defined in quote", + |diag| { + diag.span_suggestion_short( + refdefspan.shrink_to_hi(), + "for an intra-doc link, add `[]` between the label and the colon", + "[]", + Applicability::MaybeIncorrect, + ); + diag.help("link definitions are not shown in rendered documentation"); + } + ); + } + } }, End(TagEnd::BlockQuote) => { blockquote_level -= 1; @@ -869,11 +922,42 @@ fn check_doc<'a, Events: Iterator, Range next_range.end, + _ => next_range.start, + }; + if let Some(refdefrange) = looks_like_refdef(doc, range.start..next_start) && + let Some(refdefspan) = fragments.span(cx, refdefrange.clone()) + { + span_lint_and_then( + cx, + DOC_NESTED_REFDEFS, + refdefspan, + "link reference defined in list item", + |diag| { + diag.span_suggestion_short( + refdefspan.shrink_to_hi(), + "for an intra-doc link, add `[]` between the label and the colon", + "[]", + Applicability::MaybeIncorrect, + ); + diag.help("link definitions are not shown in rendered documentation"); + } + ); + refdefrange.start - range.start + } else { + let mut start = next_range.start; + if start > 0 && doc.as_bytes().get(start - 1) == Some(&b'\\') { + // backslashes aren't in the event stream... + start -= 1; + } + start - range.start + } } else { - containers.push(Container::List(0)); - } + 0 + }; + containers.push(Container::List(indent)); } ticks_unbalanced = false; paragraph_range = range; @@ -1045,3 +1129,25 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { self.cx.tcx.hir() } } + +#[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type +fn looks_like_refdef(doc: &str, range: Range) -> Option> { + let offset = range.start; + let mut iterator = doc.as_bytes()[range].iter().copied().enumerate(); + let mut start = None; + while let Some((i, byte)) = iterator.next() { + match byte { + b'\\' => { + iterator.next(); + }, + b'[' => { + start = Some(i + offset); + }, + b']' if let Some(start) = start => { + return Some(start..i + offset + 1); + }, + _ => {}, + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 0550c22761a56..a5a4e05b3a6d2 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { // match call to write_fmt && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) && let ExprKind::Call(write_recv_path, []) = write_recv.kind - && write_fun.ident.name.as_str() == "write_fmt" + && write_fun.ident.name == sym::write_fmt && let Some(def_id) = path_def_id(cx, write_recv_path) { // match calls to std::io::stdout() / std::io::stderr () diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index be3d0f7ad6343..243eb5cbfd40a 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -10,6 +10,7 @@ mod too_many_lines; use clippy_config::Conf; use clippy_utils::def_path_def_ids; +use clippy_utils::msrvs::Msrv; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; @@ -455,6 +456,7 @@ pub struct Functions { /// A set of resolved `def_id` of traits that are configured to allow /// function params renaming. trait_ids: DefIdSet, + msrv: Msrv, } impl Functions { @@ -469,6 +471,7 @@ impl Functions { .iter() .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::>())) .collect(), + msrv: conf.msrv.clone(), } } } @@ -518,12 +521,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { must_use::check_item(cx, item); - result::check_item(cx, item, self.large_error_threshold); + result::check_item(cx, item, self.large_error_threshold, &self.msrv); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { must_use::check_impl_item(cx, item); - result::check_impl_item(cx, item, self.large_error_threshold); + result::check_impl_item(cx, item, self.large_error_threshold, &self.msrv); impl_trait_in_params::check_impl_item(cx, item); renamed_function_params::check_impl_item(cx, item, &self.trait_ids); } @@ -532,8 +535,10 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); not_unsafe_ptr_arg_deref::check_trait_item(cx, item); must_use::check_trait_item(cx, item); - result::check_trait_item(cx, item, self.large_error_threshold); + result::check_trait_item(cx, item, self.large_error_threshold, &self.msrv); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api); } + + extract_msrv_attr!(LateContext); } diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 2b26285429af5..afdb5d5306a6b 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -28,7 +28,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig); + check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { check_must_use_candidate( cx, @@ -50,7 +50,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig); + check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { check_must_use_candidate( cx, @@ -73,7 +73,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig); + check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); } else if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); if attr.is_none() && is_public && !is_proc_macro(attrs) { @@ -91,6 +91,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr } } +#[allow(clippy::too_many_arguments)] fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, @@ -98,21 +99,54 @@ fn check_needless_must_use( item_span: Span, fn_header_span: Span, attr: &Attribute, + attrs: &[Attribute], sig: &FnSig<'_>, ) { if in_external_macro(cx.sess(), item_span) { return; } if returns_unit(decl) { - span_lint_and_then( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - |diag| { - diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); - }, - ); + if attrs.len() == 1 { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); + }, + ); + } else { + // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see + // issue #12320. + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + let mut attrs_without_must_use = attrs.to_vec(); + attrs_without_must_use.retain(|a| a.id != attr.id); + let sugg_str = attrs_without_must_use + .iter() + .map(|a| { + if a.value_str().is_none() { + return a.name_or_empty().to_string(); + } + format!("{} = \"{}\"", a.name_or_empty(), a.value_str().unwrap()) + }) + .collect::>() + .join(", "); + + diag.span_suggestion( + attrs[0].span.with_hi(attrs[attrs.len() - 1].span.hi()), + "change these attributes to", + sugg_str, + Applicability::MachineApplicable, + ); + }, + ); + } } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type if sig.header.is_async() { diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index d4eaa1663208c..674d78eaae76a 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -1,3 +1,4 @@ +use clippy_utils::msrvs::{self, Msrv}; use rustc_errors::Diag; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; @@ -6,8 +7,8 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::trait_ref_of_method; use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::{is_no_std_crate, trait_ref_of_method}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -34,19 +35,24 @@ fn result_err_ty<'tcx>( } } -pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) { +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64, msrv: &Msrv) { if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) { if cx.effective_visibilities.is_exported(item.owner_id.def_id) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - check_result_unit_err(cx, err_ty, fn_header_span); + check_result_unit_err(cx, err_ty, fn_header_span, msrv); } check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); } } -pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) { +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + item: &hir::ImplItem<'tcx>, + large_err_threshold: u64, + msrv: &Msrv, +) { // Don't lint if method is a trait's implementation, we can't do anything about those if let hir::ImplItemKind::Fn(ref sig, _) = item.kind && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) @@ -54,26 +60,31 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem { if cx.effective_visibilities.is_exported(item.owner_id.def_id) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - check_result_unit_err(cx, err_ty, fn_header_span); + check_result_unit_err(cx, err_ty, fn_header_span, msrv); } check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); } } -pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) { +pub(super) fn check_trait_item<'tcx>( + cx: &LateContext<'tcx>, + item: &hir::TraitItem<'tcx>, + large_err_threshold: u64, + msrv: &Msrv, +) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) { if cx.effective_visibilities.is_exported(item.owner_id.def_id) { - check_result_unit_err(cx, err_ty, fn_header_span); + check_result_unit_err(cx, err_ty, fn_header_span, msrv); } check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); } } } -fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) { - if err_ty.is_unit() { +fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span, msrv: &Msrv) { + if err_ty.is_unit() && (!is_no_std_crate(cx) || msrv.meets(msrvs::ERROR_IN_CORE)) { span_lint_and_help( cx, RESULT_UNIT_ERR, diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index 120c5396a1cd5..2806d4d0e5d61 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -1,9 +1,13 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_else_clause; +use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet}; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -54,7 +58,7 @@ fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { impl LateLintPass<'_> for IfNotElse { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { - if let ExprKind::If(cond, _, Some(els)) = e.kind + if let ExprKind::If(cond, cond_inner, Some(els)) = e.kind && let ExprKind::DropTemps(cond) = cond.kind && let ExprKind::Block(..) = els.kind { @@ -79,8 +83,52 @@ impl LateLintPass<'_> for IfNotElse { // } // ``` if !e.span.from_expansion() && !is_else_clause(cx.tcx, e) { - span_lint_and_help(cx, IF_NOT_ELSE, e.span, msg, None, help); + match cond.kind { + ExprKind::Unary(UnOp::Not, _) | ExprKind::Binary(_, _, _) => span_lint_and_sugg( + cx, + IF_NOT_ELSE, + e.span, + msg, + "try", + make_sugg(cx, &cond.kind, cond_inner.span, els.span, "..", Some(e.span)).to_string(), + Applicability::MachineApplicable, + ), + _ => span_lint_and_help(cx, IF_NOT_ELSE, e.span, msg, None, help), + } } } } } + +fn make_sugg<'a>( + sess: &impl HasSession, + cond_kind: &'a ExprKind<'a>, + cond_inner: Span, + els_span: Span, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { + let cond_inner_snip = snippet(sess, cond_inner, default); + let els_snip = snippet(sess, els_span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + + let suggestion = match cond_kind { + ExprKind::Unary(UnOp::Not, cond_rest) => { + format!( + "if {} {} else {}", + snippet(sess, cond_rest.span, default), + els_snip, + cond_inner_snip + ) + }, + ExprKind::Binary(_, lhs, rhs) => { + let lhs_snip = snippet(sess, lhs.span, default); + let rhs_snip = snippet(sess, rhs.span, default); + + format!("if {lhs_snip} == {rhs_snip} {els_snip} else {cond_inner_snip}") + }, + _ => String::new(), + }; + + reindent_multiline(suggestion.into(), true, indent) +} diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 6cee7cfaca233..b10206dcd05ec 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; -use rustc_attr_parsing::{StabilityLevel, StableSince, RustcVersion}; +use rustc_attr_parsing::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index c2030a5ab090b..15650c4f73295 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -135,7 +135,7 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) { .map(|(index, _)| *index) .collect::>(); - let value_name = |index| format!("{}_{index}", slice.ident.name); + let value_name = |index| format!("{}_{}", slice.ident.name, index); if let Some(max_index) = used_indices.iter().max() { let opt_ref = if slice.needs_ref { "ref " } else { "" }; @@ -150,6 +150,18 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) { .collect::>(); let pat_sugg = format!("[{}, ..]", pat_sugg_idents.join(", ")); + let mut suggestions = Vec::new(); + + // Add the binding pattern suggestion + if !slice.pattern_spans.is_empty() { + suggestions.extend(slice.pattern_spans.iter().map(|span| (*span, pat_sugg.clone()))); + } + + // Add the index replacement suggestions + if !slice.index_use.is_empty() { + suggestions.extend(slice.index_use.iter().map(|(index, span)| (*span, value_name(*index)))); + } + span_lint_and_then( cx, INDEX_REFUTABLE_SLICE, @@ -157,28 +169,10 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) { "this binding can be a slice pattern to avoid indexing", |diag| { diag.multipart_suggestion( - "try using a slice pattern here", - slice - .pattern_spans - .iter() - .map(|span| (*span, pat_sugg.clone())) - .collect(), + "replace the binding and indexed access with a slice pattern", + suggestions, Applicability::MaybeIncorrect, ); - - diag.multipart_suggestion( - "and replace the index expressions here", - slice - .index_use - .iter() - .map(|(index, span)| (*span, value_name(*index))) - .collect(), - Applicability::MaybeIncorrect, - ); - - // The lint message doesn't contain a warning about the removed index expression, - // since `filter_lintable_slices` will only return slices where all access indices - // are known at compile time. Therefore, they can be removed without side effects. }, ); } diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index ae2c3e0491f2d..f666ed0a440bf 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; -use clippy_utils::{higher, is_from_proc_macro}; +use clippy_utils::{higher, is_from_proc_macro, is_in_test}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -42,39 +42,50 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of indexing or slicing. Arrays are special cases, this lint - /// does report on arrays if we can tell that slicing operations are in bounds and does not - /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint. + /// Checks for usage of indexing or slicing that may panic at runtime. + /// + /// This lint does not report on indexing or slicing operations + /// that always panic, clippy's `out_of_bound_indexing` already + /// handles those cases. /// /// ### Why restrict this? /// To avoid implicit panics from indexing and slicing. + /// /// There are “checked” alternatives which do not panic, and can be used with `unwrap()` to make /// an explicit panic when it is desired. /// + /// ### Limitations + /// This lint does not check for the usage of indexing or slicing on strings. These are covered + /// by the more specific `string_slice` lint. + /// /// ### Example /// ```rust,no_run /// // Vector - /// let x = vec![0; 5]; + /// let x = vec![0, 1, 2, 3]; /// /// x[2]; + /// x[100]; /// &x[2..100]; /// /// // Array /// let y = [0, 1, 2, 3]; /// - /// &y[10..100]; - /// &y[10..]; + /// let i = 10; // Could be a runtime value + /// let j = 20; + /// &y[i..j]; /// ``` /// /// Use instead: /// ```no_run - /// # let x = vec![0; 5]; - /// # let y = [0, 1, 2, 3]; + /// # let x = vec![0, 1, 2, 3]; /// x.get(2); + /// x.get(100); /// x.get(2..100); /// - /// y.get(10); - /// y.get(10..100); + /// # let y = [0, 1, 2, 3]; + /// let i = 10; + /// let j = 20; + /// y.get(i..j); /// ``` #[clippy::version = "pre 1.29.0"] pub INDEXING_SLICING, @@ -85,12 +96,14 @@ declare_clippy_lint! { impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); pub struct IndexingSlicing { + allow_indexing_slicing_in_tests: bool, suppress_restriction_lint_in_const: bool, } impl IndexingSlicing { pub fn new(conf: &'static Conf) -> Self { Self { + allow_indexing_slicing_in_tests: conf.allow_indexing_slicing_in_tests, suppress_restriction_lint_in_const: conf.suppress_restriction_lint_in_const, } } @@ -111,6 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); + let allowed_in_tests = self.allow_indexing_slicing_in_tests && is_in_test(cx.tcx, expr.hir_id); if let Some(range) = higher::Range::hir(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { @@ -160,6 +174,10 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { (None, None) => return, // [..] is ok. }; + if allowed_in_tests { + return; + } + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "slicing may panic", |diag| { diag.help(help_msg); @@ -198,6 +216,10 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } } + if allowed_in_tests { + return; + } + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "indexing may panic", |diag| { diag.help("consider using `.get(n)` or `.get_mut(n)` instead"); diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 66d4c40ab5e4e..f3d62b513e840 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -2,8 +2,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_opt; -use rustc_ast::{LitKind}; -use rustc_hir::{Expr, ExprKind, Attribute, AttrArgs, AttrKind}; +use rustc_ast::LitKind; +use rustc_hir::{AttrArgs, AttrKind, Attribute, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 3e8315588cc74..d33013ba6638c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -9,6 +9,7 @@ #![feature(iter_partition_in_place)] #![feature(let_chains)] #![feature(never_type)] +#![feature(round_char_boundary)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] @@ -17,7 +18,8 @@ clippy::missing_docs_in_private_items, clippy::must_use_candidate, rustc::diagnostic_outside_of_impl, - rustc::untranslatable_diagnostic + rustc::untranslatable_diagnostic, + clippy::literal_string_with_formatting_args )] #![warn( trivial_casts, @@ -49,6 +51,7 @@ extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_parse; +extern crate rustc_parse_format; extern crate rustc_resolve; extern crate rustc_session; extern crate rustc_span; @@ -196,6 +199,7 @@ mod let_with_type_underscore; mod lifetimes; mod lines_filter_map_ok; mod literal_representation; +mod literal_string_with_formatting_args; mod loops; mod macro_metavars_in_unsafe; mod macro_use; @@ -957,6 +961,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); + store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 35b14776e59f7..8b2eee34a972a 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -643,8 +643,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' // An `impl` lifetime is elidable if it satisfies the following conditions: // - It is used exactly once. -// - That single use is not in a bounded type or `GenericArgs` in a `WherePredicate`. (Note that -// `GenericArgs` are different from `GenericParam`s.) +// - That single use is not in a `WherePredicate`. fn report_elidable_impl_lifetimes<'tcx>( cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>, @@ -658,12 +657,6 @@ fn report_elidable_impl_lifetimes<'tcx>( lifetime, in_where_predicate: false, .. - } - | Usage { - lifetime, - in_bounded_ty: false, - in_generics_arg: false, - .. }, ] = usages.as_slice() { diff --git a/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs new file mode 100644 index 0000000000000..49353a1b76bec --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs @@ -0,0 +1,167 @@ +use rustc_ast::{LitKind, StrStyle}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lexer::is_ident; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_parse_format::{ParseMode, Parser, Piece}; +use rustc_session::declare_lint_pass; +use rustc_span::{BytePos, Span}; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::mir::enclosing_mir; + +declare_clippy_lint! { + /// ### What it does + /// Checks if string literals have formatting arguments outside of macros + /// using them (like `format!`). + /// + /// ### Why is this bad? + /// It will likely not generate the expected content. + /// + /// ### Example + /// ```no_run + /// let x: Option = None; + /// let y = "hello"; + /// x.expect("{y:?}"); + /// ``` + /// Use instead: + /// ```no_run + /// let x: Option = None; + /// let y = "hello"; + /// x.expect(&format!("{y:?}")); + /// ``` + #[clippy::version = "1.83.0"] + pub LITERAL_STRING_WITH_FORMATTING_ARGS, + suspicious, + "Checks if string literals have formatting arguments" +} + +declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARGS]); + +fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option)]) { + if !spans.is_empty() + && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) + { + let spans = spans + .iter() + .filter_map(|(span, name)| { + if let Some(name) = name { + // We need to check that the name is a local. + if !mir + .var_debug_info + .iter() + .any(|local| !local.source_info.span.from_expansion() && local.name.as_str() == name) + { + return None; + } + } + Some(*span) + }) + .collect::>(); + match spans.len() { + 0 => {}, + 1 => { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARGS, + spans, + "this looks like a formatting argument but it is not part of a formatting macro", + ); + }, + _ => { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARGS, + spans, + "these look like formatting arguments but are not part of a formatting macro", + ); + }, + } + } +} + +impl LateLintPass<'_> for LiteralStringWithFormattingArg { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::Lit(lit) = expr.kind { + let (add, symbol) = match lit.node { + LitKind::Str(symbol, style) => { + let add = match style { + StrStyle::Cooked => 1, + StrStyle::Raw(nb) => nb as usize + 2, + }; + (add, symbol) + }, + _ => return, + }; + let fmt_str = symbol.as_str(); + let lo = expr.span.lo(); + let mut current = fmt_str; + let mut diff_len = 0; + + let mut parser = Parser::new(current, None, None, false, ParseMode::Format); + let mut spans = Vec::new(); + while let Some(piece) = parser.next() { + if let Some(error) = parser.errors.last() { + // We simply ignore the errors and move after them. + if error.span.end >= current.len() { + break; + } + // We find the closest char to where the error location ends. + let pos = current.floor_char_boundary(error.span.end); + // We get the next character. + current = if let Some((next_char_pos, _)) = current[pos..].char_indices().nth(1) { + // We make the parser start from this new location. + ¤t[pos + next_char_pos..] + } else { + break; + }; + diff_len = fmt_str.len() - current.len(); + parser = Parser::new(current, None, None, false, ParseMode::Format); + } else if let Piece::NextArgument(arg) = piece { + let mut pos = arg.position_span; + pos.start += diff_len; + pos.end += diff_len; + + let start = fmt_str[..pos.start].rfind('{').unwrap_or(pos.start); + // If this is a unicode character escape, we don't want to lint. + if start > 1 && fmt_str[..start].ends_with("\\u") { + continue; + } + + if fmt_str[start + 1..].trim_start().starts_with('}') { + // We ignore `{}`. + continue; + } + + let end = fmt_str[start + 1..] + .find('}') + .map_or(pos.end, |found| start + 1 + found) + + 1; + let ident_start = start + 1; + let colon_pos = fmt_str[ident_start..end].find(':'); + let ident_end = colon_pos.unwrap_or(end - 1); + let mut name = None; + if ident_start < ident_end + && let arg = &fmt_str[ident_start..ident_end] + && !arg.is_empty() + && is_ident(arg) + { + name = Some(arg.to_string()); + } else if colon_pos.is_none() { + // Not a `{:?}`. + continue; + } + spans.push(( + expr.span + .with_hi(lo + BytePos((start + add).try_into().unwrap())) + .with_lo(lo + BytePos((end + add).try_into().unwrap())), + name, + )); + } + } + emit_lint(cx, expr, &spans); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 3c77db84a4054..9f3b0957eab11 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let Some(vis_snip) = vis_span.get_source_text(cx) && let Some(header_snip) = header_span.get_source_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) - && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output) + && let Some((_, ret_snip)) = suggested_ret(cx, output) { let header_snip = if vis_snip.is_empty() { format!("async {}", &header_snip[..ret_pos]) @@ -82,19 +82,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) }; - let help = format!("make the function `async` and {ret_sugg}"); - diag.span_suggestion( - header_span, - help, - format!("{header_snip}{ret_snip}"), - Applicability::MachineApplicable, - ); + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)).to_string(); - let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); - diag.span_suggestion( - block.span, - "move the body of the async block to the enclosing function", - body_snip, + diag.multipart_suggestion( + "make the function `async` and return the output of the future directly", + vec![ + (header_span, format!("{header_snip}{ret_snip}")), + (block.span, body_snip), + ], Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 0e08e2eb83da6..a1951b9da44a9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -5,9 +5,9 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_from_proc_macro, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Constness, Expr, ExprKind}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Constness, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::TyCtxt; @@ -129,9 +129,7 @@ fn is_not_const(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | DefKind::Ctor(..) | DefKind::AssocConst => false, - DefKind::Fn - | DefKind::AssocFn - | DefKind::Closure => tcx.constness(def_id) == Constness::NotConst, + DefKind::Fn | DefKind::AssocFn | DefKind::Closure => tcx.constness(def_id) == Constness::NotConst, } } diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs index 4fee3bf7aa9f5..841adfec4624b 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -43,21 +43,21 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo { && bin_op.node == BinOpKind::Eq { // a.count_ones() == 1 - if let ExprKind::MethodCall(method_name, reciever, [], _) = left.kind + if let ExprKind::MethodCall(method_name, receiver, [], _) = left.kind && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() && check_lit(right, 1) { - build_sugg(cx, expr, reciever, &mut applicability); + build_sugg(cx, expr, receiver, &mut applicability); } // 1 == a.count_ones() - if let ExprKind::MethodCall(method_name, reciever, [], _) = right.kind + if let ExprKind::MethodCall(method_name, receiver, [], _) = right.kind && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() && check_lit(left, 1) { - build_sugg(cx, expr, reciever, &mut applicability); + build_sugg(cx, expr, receiver, &mut applicability); } // a & (a - 1) == 0 @@ -115,8 +115,8 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo { } } -fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, reciever: &Expr<'_>, applicability: &mut Applicability) { - let snippet = snippet_with_applicability(cx, reciever.span, "..", applicability); +fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, applicability: &mut Applicability) { + let snippet = snippet_with_applicability(cx, receiver.span, "..", applicability); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 20984bc40caf3..b72a61a438499 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -74,8 +74,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { // check if using the same bindings as before HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id, } - // the names technically don't have to match; this makes the lint more conservative - && cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id) + // the names technically don't have to match; this makes the lint more conservative + && cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id) && cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b) && pat_contains_local(lhs.pat, a_id) && pat_contains_local(rhs.pat, b_id) @@ -149,16 +149,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "", &mut appl); let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "", &mut appl); - diag.span_suggestion( - keep_arm.pat.span, - "or try merging the arm patterns", - format!("{keep_pat_snip} | {move_pat_snip}"), - appl, - ) - .span_suggestion( - adjusted_arm_span(cx, move_arm.span), - "and remove this obsolete arm", - "", + diag.multipart_suggestion( + "or try merging the arm patterns and removing the obsolete arm", + vec![ + (keep_arm.pat.span, format!("{keep_pat_snip} | {move_pat_snip}")), + (adjusted_arm_span(cx, move_arm.span), String::new()), + ], appl, ) .help("try changing either arm body"); diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 64969271764c4..1fd2ebcb54a68 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -27,7 +27,9 @@ mod wild_in_or_pats; use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::walk_span_to_context; -use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg}; +use clippy_utils::{ + higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, +}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -1059,7 +1061,28 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check_match(cx, expr, ex, arms); - single_match::check(cx, ex, arms, expr); + let source_map = cx.tcx.sess.source_map(); + let mut match_comments = span_extract_comments(source_map, expr.span); + // We remove comments from inside arms block. + if !match_comments.is_empty() { + for arm in arms { + for comment in span_extract_comments(source_map, arm.body.span) { + if let Some(index) = match_comments + .iter() + .enumerate() + .find(|(_, cm)| **cm == comment) + .map(|(index, _)| index) + { + match_comments.remove(index); + } + } + } + } + // If there are still comments, it means they are outside of the arms, therefore + // we should not lint. + if match_comments.is_empty() { + single_match::check(cx, ex, arms, expr); + } match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 6f7d690264049..73822314b4b4a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ - eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, + SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, }; use rustc_errors::Applicability; @@ -90,7 +90,9 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool } // Recursively check for each `else if let` phrase, - if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { + if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) + && SpanlessEq::new(cx).eq_expr(nested_if_let.let_expr, if_let.let_expr) + { return check_if_let_inner(cx, nested_if_let); } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 95a4bf6f60de3..3ca20479f8e00 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -129,7 +129,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. - }) if lit.node.is_str() => pat_ref_count + 1, + }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, _ => pat_ref_count, }; // References are only implicitly added to the pattern, so no overflow here. diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs index cf7f276dabb10..b04d761d4860e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::ExprKind; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -21,6 +22,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: if is_trait_method(cx, expr, sym::Iterator) && let Some(applicability) = is_identity(cx, filter_map_arg) { + // check if the iterator is from an empty array, see issue #12653 + if let ExprKind::MethodCall(_, recv, ..) = expr.kind + && let ExprKind::MethodCall(_, recv2, ..) = recv.kind + && let ExprKind::Array(arr) = recv2.kind + && arr.is_empty() + { + return; + } + span_lint_and_sugg( cx, FILTER_MAP_IDENTITY, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 80703618a1131..1ebb71e251aba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -19,7 +19,7 @@ fn extract_count_with_applicability( ) -> Option { let start = range.start?; let end = range.end?; - // TODO: This doens't handle if either the start or end are negative literals, or if the start is + // TODO: This doesn't handle if either the start or end are negative literals, or if the start is // not a literal. In the first case, we need to be careful about how we handle computing the // count to avoid overflows. In the second, we may need to add parenthesis to make the // suggestion correct. diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 7d1d5d69c9912..810287fa54167 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1864,7 +1864,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning /// let opt: Option = None; /// /// opt.unwrap_or_else(|| 42); @@ -3839,13 +3838,11 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning /// vec![Some(1)].into_iter().filter(Option::is_some); /// /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning /// vec![Some(1)].into_iter().flatten(); /// ``` #[clippy::version = "1.77.0"] @@ -3865,13 +3862,11 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning /// vec![Ok::(1)].into_iter().filter(Result::is_ok); /// /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning /// vec![Ok::(1)].into_iter().flatten(); /// ``` #[clippy::version = "1.77.0"] @@ -3969,7 +3964,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// - /// In the aformentioned cases it is not necessary to call `min()` or `max()` + /// In the aforementioned cases it is not necessary to call `min()` or `max()` /// to compare values, it may even cause confusion. /// /// ### Example @@ -4982,6 +4977,10 @@ impl Methods { } map_identity::check(cx, expr, recv, m_arg, name, span); manual_inspect::check(cx, expr, m_arg, name, span, &self.msrv); + crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); + }, + ("map_break" | "map_continue", [m_arg]) => { + crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); }, ("map_or", [def, map]) => { option_map_or_none::check(cx, expr, recv, def, map); diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs index 829c118d29163..c41ce2481d741 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -1,9 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_def_path; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::ty::is_type_diagnostic_item; -use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -11,20 +8,17 @@ use super::NEEDLESS_OPTION_TAKE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place - if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - NEEDLESS_OPTION_TAKE, - expr.span, - "called `Option::take()` on a temporary value", - "try", - format!( - "{}", - snippet_with_applicability(cx, recv.span, "..", &mut applicability) - ), - applicability, - ); + if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) { + if let Some(function_name) = source_of_temporary_value(recv) { + span_lint_and_note( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "called `Option::take()` on a temporary value", + None, + format!("`{function_name}` creates a temporary value, so calling take() has no effect"), + ); + } } } @@ -33,9 +27,24 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { is_type_diagnostic_item(cx, expr_type, sym::Option) } -fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]); +/// Returns the string of the function call that creates the temporary. +/// When this function is called, we are reasonably certain that the `ExprKind` is either +/// `Call` or `MethodCall` because we already checked that the expression is not +/// `is_syntactic_place_expr()`. +fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> { + match expr.peel_borrows().kind { + ExprKind::Call(function, _) => { + if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind { + if !func_path.segments.is_empty() { + return Some(func_path.segments[0].ident.name.as_str()); + } + } + if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind { + return Some(func_path_segment.ident.name.as_str()); + } + None + }, + ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name.as_str()), + _ => None, } - false } diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index c91be33b1cd09..c6d4ef5911ee2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -129,7 +129,7 @@ fn check_manual_split_once_indirect( let ctxt = expr.span.ctxt(); let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); if let (_, Node::LetStmt(local)) = parents.next()? - && let PatKind::Binding(BindingMode::MUT, iter_binding_id, iter_ident, None) = local.pat.kind + && let PatKind::Binding(BindingMode::MUT, iter_binding_id, _, None) = local.pat.kind && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? && let (_, Node::Block(enclosing_block)) = parents.next()? && let mut stmts = enclosing_block @@ -162,16 +162,20 @@ fn check_manual_split_once_indirect( UnwrapKind::Unwrap => ".unwrap()", UnwrapKind::QuestionMark => "?", }; - diag.span_suggestion_verbose( - local.span, - format!("try `{r}split_once`"), - format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + + // Add a multipart suggestion + diag.multipart_suggestion( + format!("replace with `{r}split_once`"), + vec![ + ( + local.span, + format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + ), + (first.span, String::new()), // Remove the first usage + (second.span, String::new()), // Remove the second usage + ], app, ); - - let remove_msg = format!("remove the `{iter_ident}` usages"); - diag.span_suggestion(first.span, remove_msg.clone(), "", app); - diag.span_suggestion(second.span, remove_msg, "", app); }); } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 029704882dde2..671c189a98e66 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -6,6 +6,7 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; use core::ops::ControlFlow; +use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind}; @@ -122,14 +123,13 @@ pub fn check_for_loop_iter( } else { Applicability::MachineApplicable }; - diag.span_suggestion(expr.span, "use", snippet.to_owned(), applicability); - if !references_to_binding.is_empty() { - diag.multipart_suggestion( - "remove any references to the binding", - references_to_binding, - applicability, - ); - } + + let combined = references_to_binding + .into_iter() + .chain(vec![(expr.span, snippet.to_owned())]) + .collect_vec(); + + diag.multipart_suggestion("remove any references to the binding", combined, applicability); }, ); return true; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index 603916e06c95f..9a45b04d1a624 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; +use clippy_utils::{is_trait_method, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; @@ -211,8 +211,10 @@ pub(super) fn check<'tcx>( trigger.vec_name, if is_unstable { "_unstable" } else { "" }, trigger.closure_arg, - if trigger.reverse { - format!("std::cmp::Reverse({})", trigger.closure_body) + if let Some(std_or_core) = std_or_core(cx) + && trigger.reverse + { + format!("{}::cmp::Reverse({})", std_or_core, trigger.closure_body) } else { trigger.closure_body.to_string() }, diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs index 9a44a3c980c0e..e2ca4458edaa0 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs @@ -27,14 +27,12 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning /// thread_local! { /// static BUF: String = String::new(); /// } /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning /// thread_local! { /// static BUF: String = const { String::new() }; /// } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 9e44bb02c56fa..7feff7e4d3f13 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind, - UnsafeSource, StructTailExpr, is_range_literal, + StructTailExpr, UnsafeSource, is_range_literal, }; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs index d369978b8be80..2083f2bf628d0 100644 --- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs @@ -43,8 +43,8 @@ impl Context { _ => (), } - let (_, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); - if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { + let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); + if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index 37f5dd5583bfb..031f09310590b 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub}; use rustc_ast::ast::{BinOpKind, Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -12,6 +13,7 @@ declare_clippy_lint! { /// and suggests to add parentheses. Currently it catches the following: /// * mixed usage of arithmetic and bit shifting/combining operators without /// parentheses + /// * mixed usage of bitmasking and bit shifting operators without parentheses /// /// ### Why is this bad? /// Not everyone knows the precedence of those operators by @@ -20,6 +22,7 @@ declare_clippy_lint! { /// /// ### Example /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 + /// * `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2 #[clippy::version = "pre 1.29.0"] pub PRECEDENCE, complexity, @@ -51,8 +54,13 @@ impl EarlyLintPass for Precedence { return; } let mut applicability = Applicability::MachineApplicable; - match (is_arith_expr(left), is_arith_expr(right)) { - (true, true) => { + match (op, get_bin_opt(left), get_bin_opt(right)) { + ( + BitAnd | BitOr | BitXor, + Some(Shl | Shr | Add | Div | Mul | Rem | Sub), + Some(Shl | Shr | Add | Div | Mul | Rem | Sub), + ) + | (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), Some(Add | Div | Mul | Rem | Sub)) => { let sugg = format!( "({}) {} ({})", snippet_with_applicability(cx, left.span, "..", &mut applicability), @@ -61,7 +69,8 @@ impl EarlyLintPass for Precedence { ); span_sugg(expr, sugg, applicability); }, - (true, false) => { + (BitAnd | BitOr | BitXor, Some(Shl | Shr | Add | Div | Mul | Rem | Sub), _) + | (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), _) => { let sugg = format!( "({}) {} {}", snippet_with_applicability(cx, left.span, "..", &mut applicability), @@ -70,7 +79,8 @@ impl EarlyLintPass for Precedence { ); span_sugg(expr, sugg, applicability); }, - (false, true) => { + (BitAnd | BitOr | BitXor, _, Some(Shl | Shr | Add | Div | Mul | Rem | Sub)) + | (Shl | Shr, _, Some(Add | Div | Mul | Rem | Sub)) => { let sugg = format!( "{} {} ({})", snippet_with_applicability(cx, left.span, "..", &mut applicability), @@ -79,27 +89,20 @@ impl EarlyLintPass for Precedence { ); span_sugg(expr, sugg, applicability); }, - (false, false) => (), + _ => (), } } } } -fn is_arith_expr(expr: &Expr) -> bool { +fn get_bin_opt(expr: &Expr) -> Option { match expr.kind { - ExprKind::Binary(Spanned { node: op, .. }, _, _) => is_arith_op(op), - _ => false, + ExprKind::Binary(Spanned { node: op, .. }, _, _) => Some(op), + _ => None, } } #[must_use] fn is_bit_op(op: BinOpKind) -> bool { - use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr}; matches!(op, BitXor | BitAnd | BitOr | Shl | Shr) } - -#[must_use] -fn is_arith_op(op: BinOpKind) -> bool { - use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub}; - matches!(op, Add | Sub | Mul | Div | Rem) -} diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 77abe7151f071..ffc3b86c502e3 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -59,7 +59,7 @@ pub struct QuestionMark { /// As for why we need this in the first place: try_block_depth_stack: Vec, /// Keeps track of the number of inferred return type closures we are inside, to avoid problems - /// with the `Err(x.into())` expansion being ambiguious. + /// with the `Err(x.into())` expansion being ambiguous. inferred_ret_closure_stack: u16, } diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 8e3472b1b5a1a..7038b19d27596 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -85,7 +85,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); - let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix); + let needs_parens_for_prefix = + parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix); if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 7ae0310b6d996..83199ba0f7076 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -1,6 +1,9 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::path_to_local_id; use clippy_utils::source::snippet; -use clippy_utils::visitors::is_local_used; +use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; @@ -175,9 +178,31 @@ fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second false } +/// Checks if the given local is used, except for in child expression of `except`. +/// +/// This is a version of [`is_local_used`](clippy_utils::visitors::is_local_used), used to +/// implement the fix for . +pub fn is_local_used_except<'tcx>( + cx: &LateContext<'tcx>, + visitable: impl Visitable<'tcx>, + id: HirId, + except: Option, +) -> bool { + for_each_expr(cx, visitable, |e| { + if except.is_some_and(|it| it == e.hir_id) { + ControlFlow::Continue(Descend::No) + } else if path_to_local_id(e, id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::Yes) + } + }) + .is_some() +} + fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) { let (lint, msg) = match find_init(cx, pat.hir_id) { - Some(expr) if is_self_shadow(cx, pat, expr, shadowed) => { + Some((expr, _)) if is_self_shadow(cx, pat, expr, shadowed) => { let msg = format!( "`{}` is shadowed by itself in `{}`", snippet(cx, pat.span, "_"), @@ -185,7 +210,7 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) ); (SHADOW_SAME, msg) }, - Some(expr) if is_local_used(cx, expr, shadowed) => { + Some((expr, except)) if is_local_used_except(cx, expr, shadowed, except) => { let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_")); (SHADOW_REUSE, msg) }, @@ -232,15 +257,32 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_ /// Finds the "init" expression for a pattern: `let = ;` (or `if let`) or /// `match { .., => .., .. }` -fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { - for (_, node) in cx.tcx.hir().parent_iter(hir_id) { +/// +/// For closure arguments passed to a method call, returns the method call, and the `HirId` of the +/// closure (which will later be skipped). This is for +fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<(&'tcx Expr<'tcx>, Option)> { + for (hir_id, node) in cx.tcx.hir().parent_iter(hir_id) { let init = match node { - Node::Arm(_) | Node::Pat(_) => continue, + Node::Arm(_) | Node::Pat(_) | Node::PatField(_) | Node::Param(_) => continue, Node::Expr(expr) => match expr.kind { - ExprKind::Match(e, _, _) | ExprKind::Let(&LetExpr { init: e, .. }) => Some(e), + ExprKind::Match(e, _, _) | ExprKind::Let(&LetExpr { init: e, .. }) => Some((e, None)), + // If we're a closure argument, then a parent call is also an associated item. + ExprKind::Closure(_) => { + if let Some((_, node)) = cx.tcx.hir().parent_iter(hir_id).next() { + match node { + Node::Expr(expr) => match expr.kind { + ExprKind::MethodCall(_, _, _, _) | ExprKind::Call(_, _) => Some((expr, Some(hir_id))), + _ => None, + }, + _ => None, + } + } else { + None + } + }, _ => None, }, - Node::LetStmt(local) => local.init, + Node::LetStmt(local) => local.init.map(|init| (init, None)), _ => None, }; return init; diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 1a5b958e6a673..c690696aefc19 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -99,16 +99,10 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { snippet(cx, apa.last_bind_ident.span, ".."), ) }; - diag.span_suggestion_verbose( - apa.first_stmt_span, + + diag.multipart_suggestion_verbose( "merge the temporary construction with its single usage", - stmt, - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - apa.last_stmt_span, - "remove separated single usage", - "", + vec![(apa.first_stmt_span, stmt), (apa.last_stmt_span, String::new())], Applicability::MaybeIncorrect, ); }, diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index bb11daecc07da..9737b84cdb9c7 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -86,7 +86,8 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind else { + let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind + else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index e09c07060065c..2925f355d0b6d 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -370,12 +370,10 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning /// let _ = "str".to_string(); /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning /// let _ = "str".to_owned(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -424,13 +422,11 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning /// let msg = String::from("Hello World"); /// let _ = msg.to_string(); /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning /// let msg = String::from("Hello World"); /// let _ = msg.clone(); /// ``` diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index 0702f6d1e74b2..d2727968c0c95 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; -use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source, is_local_used}; +use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -71,25 +71,38 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { local.span, "this let-binding has unit value", |diag| { + let mut suggestions = Vec::new(); + + // Suggest omitting the `let` binding if let Some(expr) = &local.init { let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0; - diag.span_suggestion(local.span, "omit the `let` binding", format!("{snip};"), app); + suggestions.push((local.span, format!("{snip};"))); } - if let PatKind::Binding(_, binding_hir_id, ident, ..) = local.pat.kind + // If this is a binding pattern, we need to add suggestions to remove any usages + // of the variable + if let PatKind::Binding(_, binding_hir_id, ..) = local.pat.kind && let Some(body_id) = cx.enclosing_body.as_ref() - && let body = cx.tcx.hir().body(*body_id) - && is_local_used(cx, body, binding_hir_id) { - let identifier = ident.as_str(); + let body = cx.tcx.hir().body(*body_id); + + // Collect variable usages let mut visitor = UnitVariableCollector::new(binding_hir_id); walk_body(&mut visitor, body); - visitor.spans.into_iter().for_each(|span| { - let msg = - format!("variable `{identifier}` of type `()` can be replaced with explicit `()`"); - diag.span_suggestion(span, msg, "()", Applicability::MachineApplicable); - }); + + // Add suggestions for replacing variable usages + suggestions.extend(visitor.spans.into_iter().map(|span| (span, "()".to_string()))); + } + + // Emit appropriate diagnostic based on whether there are usages of the let binding + if !suggestions.is_empty() { + let message = if suggestions.len() == 1 { + "omit the `let` binding" + } else { + "omit the `let` binding and replace variable usages with `()`" + }; + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); } }, ); diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 0f4bd286145a8..47d6fe7db7667 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -25,13 +25,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { return; } - let (reciever, args) = match expr.kind { + let (receiver, args) = match expr.kind { ExprKind::Call(_, args) => (None, args), ExprKind::MethodCall(_, receiver, args, _) => (Some(receiver), args), _ => return, }; - let args_to_recover = reciever + let args_to_recover = receiver .into_iter() .chain(args) .filter(|arg| { diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs index 80ce67111261d..8165a45bc5baa 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// - /// This leaves the caller unable to use the `&str` as `&'static str`, causing unneccessary allocations or confusion. + /// This leaves the caller unable to use the `&str` as `&'static str`, causing unnecessary allocations or confusion. /// This is also most likely what you meant to write. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs index 0a90d31db7e14..1df229c330eb3 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; use clippy_utils::{get_parent_expr, path_to_local}; -use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp, StructTailExpr}; +use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -63,7 +63,9 @@ impl LateLintPass<'_> for UnnecessaryStruct { // all fields match, no base given path.span }, - (Some(path), StructTailExpr::Base(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => { + (Some(path), StructTailExpr::Base(base)) + if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => + { // all fields match, has base: ensure that the path of the base matches base.span }, diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 3b05abc546f47..7ffab81a5444c 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; -use clippy_utils::sugg::Sugg; +use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, path_to_local}; +use clippy_utils::{ + get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, +}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; @@ -10,7 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -382,3 +384,50 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } } + +/// Check if `arg` is a `Into::into` or `From::from` applied to `receiver` to give `expr`, through a +/// higher-order mapping function. +pub fn check_function_application(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { + if has_eligible_receiver(cx, recv, expr) + && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) + && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() + && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() + && same_type_and_consts(from_ty, to_ty) + { + span_lint_and_then( + cx, + USELESS_CONVERSION, + expr.span.with_lo(recv.span.hi()), + format!("useless conversion to the same type: `{from_ty}`"), + |diag| { + diag.suggest_remove_item( + cx, + expr.span.with_lo(recv.span.hi()), + "consider removing", + Applicability::MachineApplicable, + ); + }, + ); + } +} + +fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { + let recv_ty = cx.typeck_results().expr_ty(recv); + if is_inherent_method_call(cx, expr) + && let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) + { + if let Some(diag_name) = cx.tcx.get_diagnostic_name(recv_ty_defid) + && matches!(diag_name, sym::Option | sym::Result) + { + return true; + } + + if cx.tcx.is_diagnostic_item(sym::ControlFlow, recv_ty_defid) { + return true; + } + } + if is_trait_method(cx, expr, sym::Iterator) { + return true; + } + false +} diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 521bf6a5fede9..d2970c93f8e91 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -3,8 +3,8 @@ use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ - self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, - ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, StructTailExpr, + self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, + FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, StructTailExpr, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -625,7 +625,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }, ExprKind::UnsafeBinderCast(..) => { unimplemented!("unsafe binders are not implemented yet"); - } + }, } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs index 5483e80f932e1..bfcce81c498a8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs @@ -1,8 +1,7 @@ use crate::utils::internal_lints::lint_without_lint_pass::is_lint_ref_type; use clippy_utils::diagnostics::span_lint_and_help; use regex::Regex; -use rustc_ast as ast; -use rustc_hir::{Item, ItemKind, Mutability}; +use rustc_hir::{Attribute, Item, ItemKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -51,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation { .hir() .attrs(item.hir_id()) .iter() - .filter_map(|attr| ast::Attribute::doc_str(attr).map(|sym| (sym, attr))); + .filter_map(|attr| Attribute::doc_str(attr).map(|sym| (sym, attr))); if is_lint_ref_type(cx, ty) { for (line, attr) in lines { let cur_line = line.as_str().trim(); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 9e400d2391f02..e454427adde1b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() - && match_def_path(cx, *def_id, &paths::SYMBOL_INTERN) + && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) && let value = Symbol::intern(&arg).as_u32() && let Some(&def_id) = self.symbol_map.get(&value) diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 496343d82c8d6..dac1951489ca8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -3,6 +3,7 @@ use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::{is_lint_allowed, match_def_path, paths}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; @@ -13,7 +14,6 @@ use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; -use {rustc_ast as ast, rustc_hir as hir}; declare_clippy_lint! { /// ### What it does @@ -249,11 +249,11 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { let attrs = cx.tcx.hir().attrs(item.hir_id()); attrs.iter().find_map(|attr| { - if let ast::AttrKind::Normal(attr_kind) = &attr.kind + if let hir::AttrKind::Normal(attr_kind) = &attr.kind // Identify attribute - && let [tool_name, attr_name] = &attr_kind.item.path.segments[..] - && tool_name.ident.name == sym::clippy - && attr_name.ident.name == sym::version + && let [tool_name, attr_name] = &attr_kind.path.segments[..] + && tool_name.name == sym::clippy + && attr_name.name == sym::version && let Some(version) = attr.value_str() { Some(version) diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs index 3742be0e103d6..49aad881994e9 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs @@ -1,29 +1,29 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; -use clippy_utils::{match_function_call, paths}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does /// - /// Detects symbol comparision using `Symbol::intern`. + /// Detects symbol comparison using `Symbol::intern`. /// /// ### Why is this bad? /// - /// Comparision via `Symbol::as_str()` is faster if the interned symbols are not reused. + /// Comparison via `Symbol::as_str()` is faster if the interned symbols are not reused. /// /// ### Example /// /// None, see suggestion. pub SLOW_SYMBOL_COMPARISONS, internal, - "detects slow comparisions of symbol" + "detects slow comparisons of symbol" } declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]); @@ -34,7 +34,12 @@ fn check_slow_comparison<'tcx>( op2: &'tcx Expr<'tcx>, ) -> Option<(Span, String)> { if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL) - && let Some([symbol_name_expr]) = match_function_call(cx, op2, &paths::SYMBOL_INTERN) + && let ExprKind::Call(fun, args) = op2.kind + && let ExprKind::Path(ref qpath) = fun.kind + && cx + .tcx + .is_diagnostic_item(sym::SymbolIntern, cx.qpath_res(qpath, fun.hir_id).opt_def_id()?) + && let [symbol_name_expr] = args && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr) { Some((op1.span, symbol_name)) diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index 4a13c10166f86..a702e0785a960 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -2,13 +2,14 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_enclosing_block, match_any_def_paths, match_def_path, path_to_local_id, paths}; use rustc_ast::Mutability; +use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::{Span, sym}; use std::ops::ControlFlow; declare_clippy_lint! { @@ -22,6 +23,17 @@ declare_clippy_lint! { /// which can eventually lead to resource exhaustion, so it's recommended to call `wait()` in long-running applications. /// Such processes are called "zombie processes". /// + /// To reduce the rate of false positives, if the spawned process is assigned to a binding, the lint actually works the other way around; it + /// conservatively checks that all uses of a variable definitely don't call `wait()` and only then emits a warning. + /// For that reason, a seemingly unrelated use can get called out as calling `wait()` in help messages. + /// + /// ### Control flow + /// If a `wait()` call exists in an if/then block but not in the else block (or there is no else block), + /// then this still gets linted as not calling `wait()` in all code paths. + /// Likewise, when early-returning from the function, `wait()` calls that appear after the return expression + /// are also not accepted. + /// In other words, the `wait()` call must be unconditionally reachable after the spawn expression. + /// /// ### Example /// ```rust /// use std::process::Command; @@ -53,26 +65,47 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { if let PatKind::Binding(_, local_id, ..) = local.pat.kind && let Some(enclosing_block) = get_enclosing_block(cx, expr.hir_id) => { - let mut vis = WaitFinder::WalkUpTo(cx, local_id); - - // If it does have a `wait()` call, we're done. Don't lint. - if let Break(BreakReason::WaitFound) = walk_block(&mut vis, enclosing_block) { - return; - } + let mut vis = WaitFinder { + cx, + local_id, + state: VisitorState::WalkUpToLocal, + early_return: None, + missing_wait_branch: None, + }; + + let res = ( + walk_block(&mut vis, enclosing_block), + vis.missing_wait_branch, + vis.early_return, + ); + + let cause = match res { + (Break(MaybeWait(wait_span)), _, Some(return_span)) => { + Cause::EarlyReturn { wait_span, return_span } + }, + (Break(MaybeWait(_)), _, None) => return, + (Continue(()), None, _) => Cause::NeverWait, + (Continue(()), Some(MissingWaitBranch::MissingElse { if_span, wait_span }), _) => { + Cause::MissingElse { wait_span, if_span } + }, + (Continue(()), Some(MissingWaitBranch::MissingWaitInBranch { branch_span, wait_span }), _) => { + Cause::MissingWaitInBranch { wait_span, branch_span } + }, + }; // Don't emit a suggestion since the binding is used later - check(cx, expr, false); + check(cx, expr, cause, false); }, Node::LetStmt(&LetStmt { pat, .. }) if let PatKind::Wild = pat.kind => { // `let _ = child;`, also dropped immediately without `wait()`ing - check(cx, expr, true); + check(cx, expr, Cause::NeverWait, true); }, Node::Stmt(&Stmt { kind: StmtKind::Semi(_), .. }) => { // Immediately dropped. E.g. `std::process::Command::new("echo").spawn().unwrap();` - check(cx, expr, true); + check(cx, expr, Cause::NeverWait, true); }, _ => {}, } @@ -80,21 +113,10 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { } } -enum BreakReason { - WaitFound, - EarlyReturn, -} +struct MaybeWait(Span); /// A visitor responsible for finding a `wait()` call on a local variable. /// -/// Conditional `wait()` calls are assumed to not call wait: -/// ```ignore -/// let mut c = Command::new("").spawn().unwrap(); -/// if true { -/// c.wait(); -/// } -/// ``` -/// /// Note that this visitor does NOT explicitly look for `wait()` calls directly, but rather does the /// inverse -- checking if all uses of the local are either: /// - a field access (`child.{stderr,stdin,stdout}`) @@ -104,43 +126,50 @@ enum BreakReason { /// /// None of these are sufficient to prevent zombie processes. /// Doing it like this means more FNs, but FNs are better than FPs. -/// -/// `return` expressions, conditional or not, short-circuit the visitor because -/// if a `wait()` call hadn't been found at that point, it might never reach one at a later point: -/// ```ignore -/// let mut c = Command::new("").spawn().unwrap(); -/// if true { -/// return; // Break(BreakReason::EarlyReturn) -/// } -/// c.wait(); // this might not be reachable -/// ``` -enum WaitFinder<'a, 'tcx> { - WalkUpTo(&'a LateContext<'tcx>, HirId), - Found(&'a LateContext<'tcx>, HirId), +struct WaitFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + local_id: HirId, + state: VisitorState, + early_return: Option, + // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targetted help + // messages + missing_wait_branch: Option, +} + +#[derive(PartialEq)] +enum VisitorState { + WalkUpToLocal, + LocalFound, +} + +#[derive(Copy, Clone)] +enum MissingWaitBranch { + MissingElse { if_span: Span, wait_span: Span }, + MissingWaitInBranch { branch_span: Span, wait_span: Span }, } impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; - type Result = ControlFlow; + type Result = ControlFlow; fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { - if let Self::WalkUpTo(cx, local_id) = *self + if self.state == VisitorState::WalkUpToLocal && let PatKind::Binding(_, pat_id, ..) = l.pat.kind - && local_id == pat_id + && self.local_id == pat_id { - *self = Self::Found(cx, local_id); + self.state = VisitorState::LocalFound; } walk_local(self, l) } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { - let Self::Found(cx, local_id) = *self else { + if self.state != VisitorState::LocalFound { return walk_expr(self, ex); - }; + } - if path_to_local_id(ex, local_id) { - match cx.tcx.parent_hir_node(ex.hir_id) { + if path_to_local_id(ex, self.local_id) { + match self.cx.tcx.parent_hir_node(ex.hir_id) { Node::Stmt(Stmt { kind: StmtKind::Semi(_), .. @@ -148,29 +177,33 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { Node::Expr(expr) if let ExprKind::Field(..) = expr.kind => {}, Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {}, Node::Expr(expr) - if let Some(fn_did) = fn_def_id(cx, expr) - && match_any_def_paths(cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => {}, + if let Some(fn_did) = fn_def_id(self.cx, expr) + && match_any_def_paths(self.cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => { + }, // Conservatively assume that all other kinds of nodes call `.wait()` somehow. - _ => return Break(BreakReason::WaitFound), + _ => return Break(MaybeWait(ex.span)), } } else { match ex.kind { - ExprKind::Ret(..) => return Break(BreakReason::EarlyReturn), + ExprKind::Ret(e) => { + visit_opt!(self, visit_expr, e); + if self.early_return.is_none() { + self.early_return = Some(ex.span); + } + + return Continue(()); + }, ExprKind::If(cond, then, None) => { walk_expr(self, cond)?; - // A `wait()` call in an if expression with no else is not enough: - // - // let c = spawn(); - // if true { - // c.wait(); - // } - // - // This might not call wait(). However, early returns are propagated, - // because they might lead to a later wait() not being called. - if let Break(BreakReason::EarlyReturn) = walk_expr(self, then) { - return Break(BreakReason::EarlyReturn); + if let Break(MaybeWait(wait_span)) = walk_expr(self, then) + && self.missing_wait_branch.is_none() + { + self.missing_wait_branch = Some(MissingWaitBranch::MissingElse { + if_span: ex.span, + wait_span, + }); } return Continue(()); @@ -179,22 +212,31 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { ExprKind::If(cond, then, Some(else_)) => { walk_expr(self, cond)?; - #[expect(clippy::unnested_or_patterns)] match (walk_expr(self, then), walk_expr(self, else_)) { - (Continue(()), Continue(())) + (Continue(()), Continue(())) => {}, // `wait()` in one branch but nothing in the other does not count - | (Continue(()), Break(BreakReason::WaitFound)) - | (Break(BreakReason::WaitFound), Continue(())) => {}, - - // `wait()` in both branches is ok - (Break(BreakReason::WaitFound), Break(BreakReason::WaitFound)) => { - return Break(BreakReason::WaitFound); + (Continue(()), Break(MaybeWait(wait_span))) => { + if self.missing_wait_branch.is_none() { + self.missing_wait_branch = Some(MissingWaitBranch::MissingWaitInBranch { + branch_span: ex.span.shrink_to_lo().to(then.span), + wait_span, + }); + } + }, + (Break(MaybeWait(wait_span)), Continue(())) => { + if self.missing_wait_branch.is_none() { + self.missing_wait_branch = Some(MissingWaitBranch::MissingWaitInBranch { + branch_span: then.span.shrink_to_hi().to(else_.span), + wait_span, + }); + } }, - // Propagate early returns in either branch - (Break(BreakReason::EarlyReturn), _) | (_, Break(BreakReason::EarlyReturn)) => { - return Break(BreakReason::EarlyReturn); + // `wait()` in both branches is ok + (Break(MaybeWait(wait_span)), Break(MaybeWait(_))) => { + self.missing_wait_branch = None; + return Break(MaybeWait(wait_span)); }, } @@ -208,8 +250,40 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { } fn nested_visit_map(&mut self) -> Self::Map { - let (Self::Found(cx, _) | Self::WalkUpTo(cx, _)) = self; - cx.tcx.hir() + self.cx.tcx.hir() + } +} + +#[derive(Copy, Clone)] +enum Cause { + /// No call to `wait()` at all + NeverWait, + /// `wait()` call exists, but not all code paths definitely lead to one due to + /// an early return + EarlyReturn { wait_span: Span, return_span: Span }, + /// `wait()` call exists in some if branches but not this one + MissingWaitInBranch { wait_span: Span, branch_span: Span }, + /// `wait()` call exists in an if/then branch but it is missing an else block + MissingElse { wait_span: Span, if_span: Span }, +} + +impl Cause { + fn message(self) -> &'static str { + match self { + Cause::NeverWait => "spawned process is never `wait()`ed on", + Cause::EarlyReturn { .. } | Cause::MissingWaitInBranch { .. } | Cause::MissingElse { .. } => { + "spawned process is not `wait()`ed on in all code paths" + }, + } + } + + fn fallback_help(self) -> &'static str { + match self { + Cause::NeverWait => "consider calling `.wait()`", + Cause::EarlyReturn { .. } | Cause::MissingWaitInBranch { .. } | Cause::MissingElse { .. } => { + "consider calling `.wait()` in all code paths" + }, + } } } @@ -220,7 +294,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { /// `let _ = ;`. /// /// This checks if the program doesn't unconditionally exit after the spawn expression. -fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_suggestion: bool) { +fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Cause, emit_suggestion: bool) { let Some(block) = get_enclosing_block(cx, spawn_expr.hir_id) else { return; }; @@ -234,27 +308,46 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_sugges return; } - span_lint_and_then( - cx, - ZOMBIE_PROCESSES, - spawn_expr.span, - "spawned process is never `wait()`ed on", - |diag| { - if emit_suggestion { - diag.span_suggestion( - spawn_expr.span.shrink_to_hi(), - "try", - ".wait()", - Applicability::MaybeIncorrect, + span_lint_and_then(cx, ZOMBIE_PROCESSES, spawn_expr.span, cause.message(), |diag| { + match cause { + Cause::EarlyReturn { wait_span, return_span } => { + diag.span_note( + return_span, + "no `wait()` call exists on the code path to this early return", ); - } else { - diag.note("consider calling `.wait()`"); - } + diag.span_note( + wait_span, + "`wait()` call exists, but it is unreachable due to the early return", + ); + }, + Cause::MissingWaitInBranch { wait_span, branch_span } => { + diag.span_note(branch_span, "`wait()` is not called in this if branch"); + diag.span_note(wait_span, "`wait()` is called in the other branch"); + }, + Cause::MissingElse { if_span, wait_span } => { + diag.span_note( + if_span, + "this if expression has a `wait()` call, but it is missing an else block", + ); + diag.span_note(wait_span, "`wait()` called here"); + }, + Cause::NeverWait => {}, + } + + if emit_suggestion { + diag.span_suggestion( + spawn_expr.span.shrink_to_hi(), + "try", + ".wait()", + Applicability::MaybeIncorrect, + ); + } else { + diag.help(cause.fallback_help()); + } - diag.note("not doing so might leave behind zombie processes") - .note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning"); - }, - ); + diag.note("not doing so might leave behind zombie processes") + .note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning"); + }); } /// Checks if the given expression exits the process. diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 61476a82ba006..73fefbcd5705c 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2024-11-28 +nightly-2024-12-26 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs similarity index 100% rename from src/tools/clippy/clippy_utils/src/ast_utils.rs rename to src/tools/clippy/clippy_utils/src/ast_utils/mod.rs diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 922afffb8767d..09de5c055379f 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -133,11 +133,7 @@ fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: } } -pub fn get_unique_attr<'a, A: AttributeExt>( - sess: &'a Session, - attrs: &'a [A], - name: &'static str, -) -> Option<&'a A> { +pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: &'static str) -> Option<&'a A> { let mut unique_attr: Option<&A> = None; for attr in get_attr(sess, attrs, name) { if let Some(duplicate) = unique_attr { diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index d216879cbd25e..4e12577b6df6b 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -8,7 +8,7 @@ use crate::ty::is_type_diagnostic_item; use rustc_ast::ast; use rustc_hir as hir; -use rustc_hir::{Arm, Block, Expr, ExprKind, StructTailExpr, HirId, LoopSource, MatchSource, Node, Pat, QPath}; +use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StructTailExpr}; use rustc_lint::LateContext; use rustc_span::{Span, sym, symbol}; diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 4b604f658b8d6..ed52c481de124 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -7,10 +7,10 @@ use rustc_data_structures::fx::FxHasher; use rustc_hir::MatchSource::TryDesugar; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, - ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, - LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty, - TyKind, StructTailExpr, + AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, + ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, + Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, + TyKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -386,7 +386,7 @@ impl HirEqInterExpr<'_, '_, '_> { self.eq_qpath(l_path, r_path) && match (lo, ro) { (StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r), - (StructTailExpr::None, StructTailExpr::None) => true, + (StructTailExpr::None, StructTailExpr::None) | (StructTailExpr::DefaultFields(_), StructTailExpr::DefaultFields(_)) => true, _ => false, } @@ -473,10 +473,10 @@ impl HirEqInterExpr<'_, '_, '_> { (ConstArgKind::Anon(l_an), ConstArgKind::Anon(r_an)) => self.eq_body(l_an.body, r_an.body), (ConstArgKind::Infer(..), ConstArgKind::Infer(..)) => true, // Use explicit match for now since ConstArg is undergoing flux. - (ConstArgKind::Path(..), ConstArgKind::Anon(..)) | (ConstArgKind::Anon(..), ConstArgKind::Path(..)) - | (ConstArgKind::Infer(..), _) | (_, ConstArgKind::Infer(..)) => { - false - }, + (ConstArgKind::Path(..), ConstArgKind::Anon(..)) + | (ConstArgKind::Anon(..), ConstArgKind::Path(..)) + | (ConstArgKind::Infer(..), _) + | (_, ConstArgKind::Infer(..)) => false, } } @@ -1043,7 +1043,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { if let Some(ty) = ty { self.hash_ty(ty); } - } + }, ExprKind::Err(_) => {}, } } @@ -1255,7 +1255,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { }, TyKind::UnsafeBinder(binder) => { self.hash_ty(binder.inner_ty); - } + }, TyKind::Err(_) | TyKind::Infer | TyKind::Never diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 02bbddb413a9a..77c597f853489 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1960,43 +1960,6 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { }) } -/// Matches a function call with the given path and returns the arguments. -/// -/// Usage: -/// -/// ```rust,ignore -/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX); -/// ``` -/// This function is deprecated. Use [`match_function_call_with_def_id`]. -pub fn match_function_call<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - path: &[&str], -) -> Option<&'tcx [Expr<'tcx>]> { - if let ExprKind::Call(fun, args) = expr.kind - && let ExprKind::Path(ref qpath) = fun.kind - && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() - && match_def_path(cx, fun_def_id, path) - { - return Some(args); - }; - None -} - -pub fn match_function_call_with_def_id<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - fun_def_id: DefId, -) -> Option<&'tcx [Expr<'tcx>]> { - if let ExprKind::Call(fun, args) = expr.kind - && let ExprKind::Path(ref qpath) = fun.kind - && cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id) - { - return Some(args); - }; - None -} - /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if /// any. /// @@ -2273,15 +2236,19 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { } pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { - cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| { - attr.name_or_empty() == sym::no_std - }) + cx.tcx + .hir() + .attrs(hir::CRATE_HIR_ID) + .iter() + .any(|attr| attr.name_or_empty() == sym::no_std) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { - cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| { - attr.name_or_empty() == sym::no_core - }) + cx.tcx + .hir() + .attrs(hir::CRATE_HIR_ID) + .iter() + .any(|attr| attr.name_or_empty() == sym::no_core) } /// Check if parent of a hir node is a trait implementation block. @@ -2980,12 +2947,18 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { /// /// Comments are returned wrapped with their relevant delimiters pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { + span_extract_comments(sm, span).join("\n") +} + +/// Returns all the comments a given span contains. +/// +/// Comments are returned wrapped with their relevant delimiters. +pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec { let snippet = sm.span_to_snippet(span).unwrap_or_default(); - let res = tokenize_with_text(&snippet) + tokenize_with_text(&snippet) .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) - .map(|(_, s, _)| s) - .join("\n"); - res + .map(|(_, s, _)| s.to_string()) + .collect::>() } pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 1e6368fab368d..98bcedecccc65 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,5 +1,5 @@ use rustc_ast::attr::AttributeExt; -use rustc_attr_parsing::{parse_version, RustcVersion}; +use rustc_attr_parsing::{RustcVersion, parse_version}; use rustc_session::Session; use rustc_span::{Symbol, sym}; use serde::Deserialize; @@ -19,11 +19,12 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } - 1,82,0 { IS_NONE_OR, REPEAT_N } - 1,81,0 { LINT_REASONS_STABILIZATION } - 1,80,0 { BOX_INTO_ITER} + 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } + 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE } + 1,80,0 { BOX_INTO_ITER } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } + 1,74,0 { REPR_RUST } 1,73,0 { MANUAL_DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index bb40a9430a7af..8cb8cd5901400 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -23,7 +23,6 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs similarity index 99% rename from src/tools/clippy/clippy_utils/src/ty.rs rename to src/tools/clippy/clippy_utils/src/ty/mod.rs index bc3c3ca5c2179..32e7c2bbf7cb6 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -171,7 +171,7 @@ pub fn should_call_clone_as_function(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { ) } -/// Returns true if ty has `iter` or `iter_mut` methods +/// If `ty` is known to have a `iter` or `iter_mut` method, returns a symbol representing the type. pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` // exists and has the desired signature. Unfortunately FnCtxt is not exported diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index afceeec22726a..7a3a861a9caeb 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -7,7 +7,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath, - Stmt, UnOp, UnsafeSource, StructTailExpr, + Stmt, StructTailExpr, UnOp, UnsafeSource, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 8c62dd3ed3859..03e2a24f6f989 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -17,7 +17,8 @@ #![allow( clippy::collapsible_else_if, clippy::needless_borrows_for_generic_args, - clippy::module_name_repetitions + clippy::module_name_repetitions, + clippy::literal_string_with_formatting_args )] mod config; diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index fb159ca2ae038..1000d90f52a52 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2024-11-28" +channel = "nightly-2024-12-26" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 32ee668cda1cb..75ef60a5dc8ac 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -285,7 +285,7 @@ pub fn main() { let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some() && arg_value(&orig_args, "--force-warn", |val| val.contains("clippy::")).is_none(); - // If `--no-deps` is enabled only lint the primary pacakge + // If `--no-deps` is enabled only lint the primary package let relevant_package = !no_deps || env::var("CARGO_PRIMARY_PACKAGE").is_ok(); // Do not run Clippy for Cargo's info queries so that invalid CLIPPY_ARGS are not cached @@ -303,7 +303,7 @@ pub fn main() { .set_using_internal_features(using_internal_features) .run(); } - return Ok(()); + Ok(()) })) } diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed index 98591e15bec47..3bcabb4ab2d36 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed @@ -23,7 +23,7 @@ fn main() { let _ = rustc_span::sym::proc_dash_macro; // interning a keyword - let _ = rustc_span::symbol::kw::SelfLower; + let _ = rustc_span::kw::SelfLower; // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr index 6d86768d344e9..c4d0308979f64 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr @@ -27,7 +27,7 @@ error: interning a defined symbol --> tests/ui-internal/interning_defined_symbol.rs:26:13 | LL | let _ = Symbol::intern("self"); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::kw::SelfLower` error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed index 8e7f020c1f659..3d9deb705ace8 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed @@ -14,8 +14,8 @@ use rustc_span::symbol::{Ident, Symbol}; fn main() { Symbol::intern("foo") == rustc_span::sym::clippy; - Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower; - Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper; + Symbol::intern("foo") == rustc_span::kw::SelfLower; + Symbol::intern("foo") != rustc_span::kw::SelfUpper; Ident::empty().name == rustc_span::sym::clippy; rustc_span::sym::clippy == Ident::empty().name; } diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr index 668c11722f91c..1742603eff6d9 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr @@ -15,13 +15,13 @@ error: unnecessary `Symbol` to string conversion --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 | LL | Symbol::intern("foo").to_string() == "self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::kw::SelfLower` error: unnecessary `Symbol` to string conversion --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::kw::SelfUpper` error: unnecessary `Symbol` to string conversion --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 diff --git a/src/tools/clippy/tests/ui-toml/indexing_slicing/clippy.toml b/src/tools/clippy/tests/ui-toml/indexing_slicing/clippy.toml new file mode 100644 index 0000000000000..7e83868332f1c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/indexing_slicing/clippy.toml @@ -0,0 +1 @@ +allow-indexing-slicing-in-tests = true diff --git a/src/tools/clippy/tests/ui-toml/indexing_slicing/indexing_slicing.rs b/src/tools/clippy/tests/ui-toml/indexing_slicing/indexing_slicing.rs new file mode 100644 index 0000000000000..0a0da88ea1fad --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/indexing_slicing/indexing_slicing.rs @@ -0,0 +1,19 @@ +//@compile-flags: --test +#![warn(clippy::indexing_slicing)] +#![allow(clippy::no_effect)] + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + &x[index..]; +} + +#[cfg(test)] +mod tests { + #[test] + fn test_fn() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + &x[index..]; + } +} diff --git a/src/tools/clippy/tests/ui-toml/indexing_slicing/indexing_slicing.stderr b/src/tools/clippy/tests/ui-toml/indexing_slicing/indexing_slicing.stderr new file mode 100644 index 0000000000000..5a4de8337b468 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/indexing_slicing/indexing_slicing.stderr @@ -0,0 +1,12 @@ +error: slicing may panic + --> tests/ui-toml/indexing_slicing/indexing_slicing.rs:8:6 + | +LL | &x[index..]; + | ^^^^^^^^^^ + | + = help: consider using `.get(n..)` or .get_mut(n..)` instead + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs index 8a6dd36501cff..184c6d17ba41e 100644 --- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs @@ -1,4 +1,5 @@ #![warn(clippy::large_include_file)] +#![allow(clippy::literal_string_with_formatting_args)] // Good const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs"); diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr index 9e1494a47bbaa..82b926cc53ba2 100644 --- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr @@ -1,5 +1,5 @@ error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:13:43 + --> tests/ui-toml/large_include_file/large_include_file.rs:14:43 | LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]` error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:15:35 + --> tests/ui-toml/large_include_file/large_include_file.rs:16:35 | LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); = note: the configuration allows a maximum size of 600 bytes error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:18:1 + --> tests/ui-toml/large_include_file/large_include_file.rs:19:1 | LL | #[doc = include_str!("too_big.txt")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed new file mode 100644 index 0000000000000..36540bf1dcf73 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -0,0 +1,24 @@ +#![deny(clippy::index_refutable_slice)] + +fn below_limit() { + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice { + //~^ ERROR: binding can be a slice pattern + // This would usually not be linted but is included now due to the + // index limit in the config file + println!("{}", slice_7); + } +} + +fn above_limit() { + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some(slice) = slice { + // This will not be linted as 8 is above the limit + println!("{}", slice[8]); + } +} + +fn main() { + below_limit(); + above_limit(); +} diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index e64c8ff329001..da76bb20fd961 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,7 +1,5 @@ #![deny(clippy::index_refutable_slice)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - fn below_limit() { let slice: Option<&[u32]> = Some(&[1, 2, 3]); if let Some(slice) = slice { diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index 3ea600c7d7b0c..022deb330e6e3 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:7:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -9,14 +9,14 @@ note: the lint level is defined here | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice { +LL | +LL | // This would usually not be linted but is included now due to the +LL | // index limit in the config file +LL ~ println!("{}", slice_7); | -LL | println!("{}", slice_7); - | ~~~~~~~ error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6fa583fc0417a..200129da25f50 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -6,6 +6,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-comparison-to-zero allow-dbg-in-tests allow-expect-in-tests + allow-indexing-slicing-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-panic-in-tests @@ -93,6 +94,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-comparison-to-zero allow-dbg-in-tests allow-expect-in-tests + allow-indexing-slicing-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-panic-in-tests @@ -180,6 +182,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-comparison-to-zero allow-dbg-in-tests allow-expect-in-tests + allow-indexing-slicing-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings allow-panic-in-tests diff --git a/src/tools/clippy/tests/ui/as_pointer_underscore.fixed b/src/tools/clippy/tests/ui/as_pointer_underscore.fixed new file mode 100644 index 0000000000000..db06486ecb048 --- /dev/null +++ b/src/tools/clippy/tests/ui/as_pointer_underscore.fixed @@ -0,0 +1,15 @@ +#![warn(clippy::as_pointer_underscore)] +#![crate_type = "lib"] +#![no_std] + +struct S; + +fn f(s: &S) -> usize { + &s as *const &S as usize + //~^ ERROR: using inferred pointer cast +} + +fn g(s: &mut S) -> usize { + s as *mut S as usize + //~^ ERROR: using inferred pointer cast +} diff --git a/src/tools/clippy/tests/ui/as_pointer_underscore.rs b/src/tools/clippy/tests/ui/as_pointer_underscore.rs new file mode 100644 index 0000000000000..955c702ccc998 --- /dev/null +++ b/src/tools/clippy/tests/ui/as_pointer_underscore.rs @@ -0,0 +1,15 @@ +#![warn(clippy::as_pointer_underscore)] +#![crate_type = "lib"] +#![no_std] + +struct S; + +fn f(s: &S) -> usize { + &s as *const _ as usize + //~^ ERROR: using inferred pointer cast +} + +fn g(s: &mut S) -> usize { + s as *mut _ as usize + //~^ ERROR: using inferred pointer cast +} diff --git a/src/tools/clippy/tests/ui/as_pointer_underscore.stderr b/src/tools/clippy/tests/ui/as_pointer_underscore.stderr new file mode 100644 index 0000000000000..270056f36454e --- /dev/null +++ b/src/tools/clippy/tests/ui/as_pointer_underscore.stderr @@ -0,0 +1,17 @@ +error: using inferred pointer cast + --> tests/ui/as_pointer_underscore.rs:8:11 + | +LL | &s as *const _ as usize + | ^^^^^^^^ help: use explicit type: `*const &S` + | + = note: `-D clippy::as-pointer-underscore` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::as_pointer_underscore)]` + +error: using inferred pointer cast + --> tests/ui/as_pointer_underscore.rs:13:10 + | +LL | s as *mut _ as usize + | ^^^^^^ help: use explicit type: `*mut S` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index fbf84337382e1..1815dd58f5107 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -2,6 +2,7 @@ #![allow(incomplete_features)] #![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] +#![allow(clippy::literal_string_with_formatting_args)] extern crate proc_macro; diff --git a/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.fixed new file mode 100644 index 0000000000000..2950b158deb64 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.fixed @@ -0,0 +1,11 @@ +// Make sure that `ref_as_ptr` is not emitted when `borrow_as_ptr` is. + +#![warn(clippy::ref_as_ptr, clippy::borrow_as_ptr)] + +fn f(_: T) {} + +fn main() { + let mut val = 0; + f(&raw const val); + f(&raw mut val); +} diff --git a/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.rs new file mode 100644 index 0000000000000..19eb8f2923374 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.rs @@ -0,0 +1,11 @@ +// Make sure that `ref_as_ptr` is not emitted when `borrow_as_ptr` is. + +#![warn(clippy::ref_as_ptr, clippy::borrow_as_ptr)] + +fn f(_: T) {} + +fn main() { + let mut val = 0; + f(&val as *const _); + f(&mut val as *mut i32); +} diff --git a/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.stderr new file mode 100644 index 0000000000000..82a27af303c21 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_and_ref_as_ptr.stderr @@ -0,0 +1,17 @@ +error: borrow as raw pointer + --> tests/ui/borrow_and_ref_as_ptr.rs:9:7 + | +LL | f(&val as *const _); + | ^^^^^^^^^^^^^^^^ help: try: `&raw const val` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]` + +error: borrow as raw pointer + --> tests/ui/borrow_and_ref_as_ptr.rs:10:7 + | +LL | f(&mut val as *mut i32); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut val` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.fixed new file mode 100644 index 0000000000000..d6842e60a3e9f --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.fixed @@ -0,0 +1,19 @@ +#![warn(clippy::borrow_as_ptr)] +#![allow(clippy::useless_vec)] + +fn a() -> i32 { + 0 +} + +#[clippy::msrv = "1.82"] +fn main() { + let val = 1; + let _p = &raw const val; + let _p = &0 as *const i32; + let _p = &a() as *const i32; + let vec = vec![1]; + let _p = &vec.len() as *const usize; + + let mut val_mut = 1; + let _p_mut = &raw mut val_mut; +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.rs b/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.rs new file mode 100644 index 0000000000000..3c9daed18f15a --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.rs @@ -0,0 +1,19 @@ +#![warn(clippy::borrow_as_ptr)] +#![allow(clippy::useless_vec)] + +fn a() -> i32 { + 0 +} + +#[clippy::msrv = "1.82"] +fn main() { + let val = 1; + let _p = &val as *const i32; + let _p = &0 as *const i32; + let _p = &a() as *const i32; + let vec = vec![1]; + let _p = &vec.len() as *const usize; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.stderr new file mode 100644 index 0000000000000..5611fcae8d4b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_raw_ref.stderr @@ -0,0 +1,17 @@ +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr_raw_ref.rs:11:14 + | +LL | let _p = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `&raw const val` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]` + +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr_raw_ref.rs:18:18 + | +LL | let _p_mut = &mut val_mut as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut val_mut` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/comparison_chain.rs b/src/tools/clippy/tests/ui/comparison_chain.rs index 266cee4c33890..cab460d100dda 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.rs +++ b/src/tools/clippy/tests/ui/comparison_chain.rs @@ -1,3 +1,4 @@ +//@no-rustfix #![allow(dead_code)] #![warn(clippy::comparison_chain)] @@ -238,4 +239,16 @@ const fn sign_i8(n: i8) -> Sign { } } +fn needs_parens() -> &'static str { + let (x, y) = (1, 2); + if x + 1 > y * 2 { + //~^ ERROR: `if` chain can be rewritten with `match` + "aa" + } else if x + 1 < y * 2 { + "bb" + } else { + "cc" + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr index 96d8d819e6a55..814004e3d4b16 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.stderr +++ b/src/tools/clippy/tests/ui/comparison_chain.stderr @@ -1,5 +1,5 @@ error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:14:5 + --> tests/ui/comparison_chain.rs:15:5 | LL | / if x > y { LL | | @@ -7,14 +7,13 @@ LL | | a() LL | | } else if x < y { LL | | b() LL | | } - | |_____^ + | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` | - = help: consider rewriting the `if` chain to use `cmp` and `match` = note: `-D clippy::comparison-chain` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::comparison_chain)]` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:28:5 + --> tests/ui/comparison_chain.rs:29:5 | LL | / if x > y { LL | | @@ -23,12 +22,10 @@ LL | | } else if x < y { ... | LL | | c() LL | | } - | |_____^ - | - = help: consider rewriting the `if` chain to use `cmp` and `match` + | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:37:5 + --> tests/ui/comparison_chain.rs:38:5 | LL | / if x > y { LL | | @@ -37,12 +34,10 @@ LL | | } else if y > x { ... | LL | | c() LL | | } - | |_____^ - | - = help: consider rewriting the `if` chain to use `cmp` and `match` + | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:46:5 + --> tests/ui/comparison_chain.rs:47:5 | LL | / if x > 1 { LL | | @@ -51,12 +46,10 @@ LL | | } else if x < 1 { ... | LL | | c() LL | | } - | |_____^ - | - = help: consider rewriting the `if` chain to use `cmp` and `match` + | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&1) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:121:5 + --> tests/ui/comparison_chain.rs:122:5 | LL | / if x > y { LL | | @@ -64,12 +57,10 @@ LL | | a() LL | | } else if x < y { LL | | b() LL | | } - | |_____^ - | - = help: consider rewriting the `if` chain to use `cmp` and `match` + | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:128:5 + --> tests/ui/comparison_chain.rs:129:5 | LL | / if x > y { LL | | @@ -78,12 +69,10 @@ LL | | } else if x < y { ... | LL | | c() LL | | } - | |_____^ - | - = help: consider rewriting the `if` chain to use `cmp` and `match` + | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:137:5 + --> tests/ui/comparison_chain.rs:138:5 | LL | / if x > y { LL | | @@ -92,9 +81,19 @@ LL | | } else if y > x { ... | LL | | c() LL | | } - | |_____^ + | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` + +error: `if` chain can be rewritten with `match` + --> tests/ui/comparison_chain.rs:244:5 | - = help: consider rewriting the `if` chain to use `cmp` and `match` +LL | / if x + 1 > y * 2 { +LL | | +LL | | "aa" +LL | | } else if x + 1 < y * 2 { +... | +LL | | "cc" +LL | | } + | |_____^ help: consider rewriting the `if` chain with `match`: `match (x + 1).cmp(&(y * 2)) {...}` -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/default_union_representation.rs b/src/tools/clippy/tests/ui/default_union_representation.rs index 41308b077bac6..ba63cde2fa9d4 100644 --- a/src/tools/clippy/tests/ui/default_union_representation.rs +++ b/src/tools/clippy/tests/ui/default_union_representation.rs @@ -1,5 +1,6 @@ #![feature(transparent_unions)] #![warn(clippy::default_union_representation)] +#![allow(clippy::repr_packed_without_abi)] union NoAttribute { //~^ ERROR: this union has the default representation diff --git a/src/tools/clippy/tests/ui/default_union_representation.stderr b/src/tools/clippy/tests/ui/default_union_representation.stderr index c7ef70a0b8e7b..d558a3e8de1b9 100644 --- a/src/tools/clippy/tests/ui/default_union_representation.stderr +++ b/src/tools/clippy/tests/ui/default_union_representation.stderr @@ -1,5 +1,5 @@ error: this union has the default representation - --> tests/ui/default_union_representation.rs:4:1 + --> tests/ui/default_union_representation.rs:5:1 | LL | / union NoAttribute { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::default_union_representation)]` error: this union has the default representation - --> tests/ui/default_union_representation.rs:17:1 + --> tests/ui/default_union_representation.rs:18:1 | LL | / union ReprPacked { LL | | @@ -25,7 +25,7 @@ LL | | } = help: consider annotating `ReprPacked` with `#[repr(C)]` to explicitly specify memory layout error: this union has the default representation - --> tests/ui/default_union_representation.rs:36:1 + --> tests/ui/default_union_representation.rs:37:1 | LL | / union ReprAlign { LL | | @@ -37,7 +37,7 @@ LL | | } = help: consider annotating `ReprAlign` with `#[repr(C)]` to explicitly specify memory layout error: this union has the default representation - --> tests/ui/default_union_representation.rs:57:1 + --> tests/ui/default_union_representation.rs:58:1 | LL | / union ZSTAndTwoFields { LL | | diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs index b06dd78608fdc..d03cc01a08b0b 100644 --- a/src/tools/clippy/tests/ui/derive.rs +++ b/src/tools/clippy/tests/ui/derive.rs @@ -2,6 +2,7 @@ clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, clippy::needless_lifetimes, + clippy::repr_packed_without_abi, dead_code )] #![warn(clippy::expl_impl_clone_on_copy)] diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr index 0eb4b3c1adaa6..d70a59855229d 100644 --- a/src/tools/clippy/tests/ui/derive.stderr +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:13:1 + --> tests/ui/derive.rs:14:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:13:1 + --> tests/ui/derive.rs:14:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:38:1 + --> tests/ui/derive.rs:39:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:38:1 + --> tests/ui/derive.rs:39:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:50:1 + --> tests/ui/derive.rs:51:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:50:1 + --> tests/ui/derive.rs:51:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:62:1 + --> tests/ui/derive.rs:63:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:62:1 + --> tests/ui/derive.rs:63:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:83:1 + --> tests/ui/derive.rs:84:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:83:1 + --> tests/ui/derive.rs:84:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed index da537518a2b53..0822cc7c6350a 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed @@ -75,3 +75,9 @@ fn seven() {} /// ] //~^ ERROR: doc list item without indentation fn eight() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/issues/13705 +/// - \[text in square brackets\] with a long following description +/// that goes over multiple lines +pub fn backslash_escaped_square_braces() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs index 3cc18e35780a2..068de140e00a0 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs @@ -75,3 +75,9 @@ fn seven() {} /// ] //~^ ERROR: doc list item without indentation fn eight() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/issues/13705 +/// - \[text in square brackets\] with a long following description +/// that goes over multiple lines +pub fn backslash_escaped_square_braces() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.fixed b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.fixed new file mode 100644 index 0000000000000..8939a03d2e3ea --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.fixed @@ -0,0 +1,133 @@ +// https://github.com/rust-lang/rust/issues/133150 +#![warn(clippy::doc_nested_refdefs)] +#[rustfmt::skip] +/// > [link][]: def +//~^ ERROR: link reference defined in quote +/// +/// > [link][]: def (title) +//~^ ERROR: link reference defined in quote +/// +/// > [link][]: def "title" +//~^ ERROR: link reference defined in quote +/// +/// > [link]: not def +/// +/// > [link][]: notdef +/// +/// > [link]\: notdef +pub struct Empty; + +#[rustfmt::skip] +/// > [link][]: def +//~^ ERROR: link reference defined in quote +/// > inner text +/// +/// > [link][]: def (title) +//~^ ERROR: link reference defined in quote +/// > inner text +/// +/// > [link][]: def "title" +//~^ ERROR: link reference defined in quote +/// > inner text +/// +/// > [link]: not def +/// > inner text +/// +/// > [link][]: notdef +/// > inner text +/// +/// > [link]\: notdef +/// > inner text +pub struct NotEmpty; + +#[rustfmt::skip] +/// > [link][]: def +//~^ ERROR: link reference defined in quote +/// > +/// > inner text +/// +/// > [link][]: def (title) +//~^ ERROR: link reference defined in quote +/// > +/// > inner text +/// +/// > [link][]: def "title" +//~^ ERROR: link reference defined in quote +/// > +/// > inner text +/// +/// > [link]: not def +/// > +/// > inner text +/// +/// > [link][]: notdef +/// > +/// > inner text +/// +/// > [link]\: notdef +/// > +/// > inner text +pub struct NotEmptyLoose; + +#[rustfmt::skip] +/// > first lines +/// > [link]: def +/// +/// > first lines +/// > [link]: def (title) +/// +/// > firs lines +/// > [link]: def "title" +/// +/// > firs lines +/// > [link]: not def +/// +/// > first lines +/// > [link][]: notdef +/// +/// > first lines +/// > [link]\: notdef +pub struct NotAtStartTight; + +#[rustfmt::skip] +/// > first lines +/// > +/// > [link]: def +/// +/// > first lines +/// > +/// > [link]: def (title) +/// +/// > firs lines +/// > +/// > [link]: def "title" +/// +/// > firs lines +/// > +/// > [link]: not def +/// +/// > first lines +/// > +/// > [link][]: notdef +/// +/// > first lines +/// > +/// > [link]\: notdef +pub struct NotAtStartLoose; + +#[rustfmt::skip] +/// > - [link][]: def +//~^ ERROR: link reference defined in list item +/// > +/// > - [link][]: def (title) +//~^ ERROR: link reference defined in list item +/// > +/// > - [link][]: def "title" +//~^ ERROR: link reference defined in list item +/// > +/// > - [link]: not def +/// > +/// > - [link][]: notdef +/// > +/// > - [link]\: notdef +pub struct ListNested; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.rs b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.rs new file mode 100644 index 0000000000000..f861242384bab --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.rs @@ -0,0 +1,133 @@ +// https://github.com/rust-lang/rust/issues/133150 +#![warn(clippy::doc_nested_refdefs)] +#[rustfmt::skip] +/// > [link]: def +//~^ ERROR: link reference defined in quote +/// +/// > [link]: def (title) +//~^ ERROR: link reference defined in quote +/// +/// > [link]: def "title" +//~^ ERROR: link reference defined in quote +/// +/// > [link]: not def +/// +/// > [link][]: notdef +/// +/// > [link]\: notdef +pub struct Empty; + +#[rustfmt::skip] +/// > [link]: def +//~^ ERROR: link reference defined in quote +/// > inner text +/// +/// > [link]: def (title) +//~^ ERROR: link reference defined in quote +/// > inner text +/// +/// > [link]: def "title" +//~^ ERROR: link reference defined in quote +/// > inner text +/// +/// > [link]: not def +/// > inner text +/// +/// > [link][]: notdef +/// > inner text +/// +/// > [link]\: notdef +/// > inner text +pub struct NotEmpty; + +#[rustfmt::skip] +/// > [link]: def +//~^ ERROR: link reference defined in quote +/// > +/// > inner text +/// +/// > [link]: def (title) +//~^ ERROR: link reference defined in quote +/// > +/// > inner text +/// +/// > [link]: def "title" +//~^ ERROR: link reference defined in quote +/// > +/// > inner text +/// +/// > [link]: not def +/// > +/// > inner text +/// +/// > [link][]: notdef +/// > +/// > inner text +/// +/// > [link]\: notdef +/// > +/// > inner text +pub struct NotEmptyLoose; + +#[rustfmt::skip] +/// > first lines +/// > [link]: def +/// +/// > first lines +/// > [link]: def (title) +/// +/// > firs lines +/// > [link]: def "title" +/// +/// > firs lines +/// > [link]: not def +/// +/// > first lines +/// > [link][]: notdef +/// +/// > first lines +/// > [link]\: notdef +pub struct NotAtStartTight; + +#[rustfmt::skip] +/// > first lines +/// > +/// > [link]: def +/// +/// > first lines +/// > +/// > [link]: def (title) +/// +/// > firs lines +/// > +/// > [link]: def "title" +/// +/// > firs lines +/// > +/// > [link]: not def +/// +/// > first lines +/// > +/// > [link][]: notdef +/// +/// > first lines +/// > +/// > [link]\: notdef +pub struct NotAtStartLoose; + +#[rustfmt::skip] +/// > - [link]: def +//~^ ERROR: link reference defined in list item +/// > +/// > - [link]: def (title) +//~^ ERROR: link reference defined in list item +/// > +/// > - [link]: def "title" +//~^ ERROR: link reference defined in list item +/// > +/// > - [link]: not def +/// > +/// > - [link][]: notdef +/// > +/// > - [link]\: notdef +pub struct ListNested; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.stderr b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.stderr new file mode 100644 index 0000000000000..448659b894103 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_blockquote.stderr @@ -0,0 +1,148 @@ +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:4:7 + | +LL | /// > [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation + = note: `-D clippy::doc-nested-refdefs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_nested_refdefs)]` +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:7:7 + | +LL | /// > [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def (title) + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:10:7 + | +LL | /// > [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def "title" + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:21:7 + | +LL | /// > [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:25:7 + | +LL | /// > [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def (title) + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:29:7 + | +LL | /// > [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def "title" + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:44:7 + | +LL | /// > [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:49:7 + | +LL | /// > [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def (title) + | ++ + +error: link reference defined in quote + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:54:7 + | +LL | /// > [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > [link][]: def "title" + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:119:9 + | +LL | /// > - [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > - [link][]: def + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:122:9 + | +LL | /// > - [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > - [link][]: def (title) + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_blockquote.rs:125:9 + | +LL | /// > - [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// > - [link][]: def "title" + | ++ + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed new file mode 100644 index 0000000000000..fcfcfcc40736a --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed @@ -0,0 +1,71 @@ +// https://github.com/rust-lang/rust/issues/133150 +#![warn(clippy::doc_nested_refdefs)] +#[rustfmt::skip] +/// - [link][]: def +//~^ ERROR: link reference defined in list item +/// +/// - [link][]: def (title) +//~^ ERROR: link reference defined in list item +/// +/// - [link][]: def "title" +//~^ ERROR: link reference defined in list item +/// +/// - [link]: not def +/// +/// - [link][]: notdef +/// +/// - [link]\: notdef +pub struct Empty; + +#[rustfmt::skip] +/// - [link][]: def +//~^ ERROR: link reference defined in list item +/// - [link][]: def (title) +//~^ ERROR: link reference defined in list item +/// - [link][]: def "title" +//~^ ERROR: link reference defined in list item +/// - [link]: not def +/// - [link][]: notdef +/// - [link]\: notdef +pub struct EmptyTight; + +#[rustfmt::skip] +/// - [link][]: def +//~^ ERROR: link reference defined in list item +/// inner text +/// +/// - [link][]: def (title) +//~^ ERROR: link reference defined in list item +/// inner text +/// +/// - [link][]: def "title" +//~^ ERROR: link reference defined in list item +/// inner text +/// +/// - [link]: not def +/// inner text +/// +/// - [link][]: notdef +/// inner text +/// +/// - [link]\: notdef +/// inner text +pub struct NotEmpty; + +#[rustfmt::skip] +/// - [link][]: def +//~^ ERROR: link reference defined in list item +/// inner text +/// - [link][]: def (title) +//~^ ERROR: link reference defined in list item +/// inner text +/// - [link][]: def "title" +//~^ ERROR: link reference defined in list item +/// inner text +/// - [link]: not def +/// inner text +/// - [link][]: notdef +/// inner text +/// - [link]\: notdef +/// inner text +pub struct NotEmptyTight; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs new file mode 100644 index 0000000000000..53368de4616d8 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs @@ -0,0 +1,71 @@ +// https://github.com/rust-lang/rust/issues/133150 +#![warn(clippy::doc_nested_refdefs)] +#[rustfmt::skip] +/// - [link]: def +//~^ ERROR: link reference defined in list item +/// +/// - [link]: def (title) +//~^ ERROR: link reference defined in list item +/// +/// - [link]: def "title" +//~^ ERROR: link reference defined in list item +/// +/// - [link]: not def +/// +/// - [link][]: notdef +/// +/// - [link]\: notdef +pub struct Empty; + +#[rustfmt::skip] +/// - [link]: def +//~^ ERROR: link reference defined in list item +/// - [link]: def (title) +//~^ ERROR: link reference defined in list item +/// - [link]: def "title" +//~^ ERROR: link reference defined in list item +/// - [link]: not def +/// - [link][]: notdef +/// - [link]\: notdef +pub struct EmptyTight; + +#[rustfmt::skip] +/// - [link]: def +//~^ ERROR: link reference defined in list item +/// inner text +/// +/// - [link]: def (title) +//~^ ERROR: link reference defined in list item +/// inner text +/// +/// - [link]: def "title" +//~^ ERROR: link reference defined in list item +/// inner text +/// +/// - [link]: not def +/// inner text +/// +/// - [link][]: notdef +/// inner text +/// +/// - [link]\: notdef +/// inner text +pub struct NotEmpty; + +#[rustfmt::skip] +/// - [link]: def +//~^ ERROR: link reference defined in list item +/// inner text +/// - [link]: def (title) +//~^ ERROR: link reference defined in list item +/// inner text +/// - [link]: def "title" +//~^ ERROR: link reference defined in list item +/// inner text +/// - [link]: not def +/// inner text +/// - [link][]: notdef +/// inner text +/// - [link]\: notdef +/// inner text +pub struct NotEmptyTight; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr new file mode 100644 index 0000000000000..27314c7e968dd --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr @@ -0,0 +1,148 @@ +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:4:7 + | +LL | /// - [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation + = note: `-D clippy::doc-nested-refdefs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_nested_refdefs)]` +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:7:7 + | +LL | /// - [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def (title) + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:10:7 + | +LL | /// - [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def "title" + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:21:7 + | +LL | /// - [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:23:7 + | +LL | /// - [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def (title) + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:25:7 + | +LL | /// - [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def "title" + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:33:7 + | +LL | /// - [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:37:7 + | +LL | /// - [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def (title) + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:41:7 + | +LL | /// - [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def "title" + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:56:7 + | +LL | /// - [link]: def + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:59:7 + | +LL | /// - [link]: def (title) + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def (title) + | ++ + +error: link reference defined in list item + --> tests/ui/doc/doc_nested_refdef_list_item.rs:62:7 + | +LL | /// - [link]: def "title" + | ^^^^^^ + | + = help: link definitions are not shown in rendered documentation +help: for an intra-doc link, add `[]` between the label and the colon + | +LL | /// - [link][]: def "title" + | ++ + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_map_identity.fixed b/src/tools/clippy/tests/ui/filter_map_identity.fixed index f3f6848e5f92f..fdd020fcd7738 100644 --- a/src/tools/clippy/tests/ui/filter_map_identity.fixed +++ b/src/tools/clippy/tests/ui/filter_map_identity.fixed @@ -81,3 +81,8 @@ fn main() { //~^ ERROR: use of } } + +fn issue12653() -> impl Iterator { + [].into_iter().filter_map(|x| x) + // No lint +} diff --git a/src/tools/clippy/tests/ui/filter_map_identity.rs b/src/tools/clippy/tests/ui/filter_map_identity.rs index b9aa9c05be89c..a626de9f5bbd8 100644 --- a/src/tools/clippy/tests/ui/filter_map_identity.rs +++ b/src/tools/clippy/tests/ui/filter_map_identity.rs @@ -81,3 +81,8 @@ fn main() { //~^ ERROR: use of } } + +fn issue12653() -> impl Iterator { + [].into_iter().filter_map(|x| x) + // No lint +} diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed index 2b32fdeae2b59..3dc8eb79ba284 100644 --- a/src/tools/clippy/tests/ui/format.fixed +++ b/src/tools/clippy/tests/ui/format.fixed @@ -6,7 +6,8 @@ clippy::needless_borrow, clippy::uninlined_format_args, clippy::needless_raw_string_hashes, - clippy::useless_vec + clippy::useless_vec, + clippy::literal_string_with_formatting_args )] struct Foo(pub String); diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs index bad192067e933..eaf33c2a6c924 100644 --- a/src/tools/clippy/tests/ui/format.rs +++ b/src/tools/clippy/tests/ui/format.rs @@ -6,7 +6,8 @@ clippy::needless_borrow, clippy::uninlined_format_args, clippy::needless_raw_string_hashes, - clippy::useless_vec + clippy::useless_vec, + clippy::literal_string_with_formatting_args )] struct Foo(pub String); diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr index faa80b48000f9..1368c8cd77e01 100644 --- a/src/tools/clippy/tests/ui/format.stderr +++ b/src/tools/clippy/tests/ui/format.stderr @@ -1,5 +1,5 @@ error: useless use of `format!` - --> tests/ui/format.rs:19:5 + --> tests/ui/format.rs:20:5 | LL | format!("foo"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` @@ -8,19 +8,19 @@ LL | format!("foo"); = help: to override `-D warnings` add `#[allow(clippy::useless_format)]` error: useless use of `format!` - --> tests/ui/format.rs:20:5 + --> tests/ui/format.rs:21:5 | LL | format!("{{}}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` - --> tests/ui/format.rs:21:5 + --> tests/ui/format.rs:22:5 | LL | format!("{{}} abc {{}}"); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` - --> tests/ui/format.rs:22:5 + --> tests/ui/format.rs:23:5 | LL | / format!( LL | | r##"foo {{}} @@ -35,67 +35,67 @@ LL ~ " bar"##.to_string(); | error: useless use of `format!` - --> tests/ui/format.rs:27:13 + --> tests/ui/format.rs:28:13 | LL | let _ = format!(""); | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` error: useless use of `format!` - --> tests/ui/format.rs:29:5 + --> tests/ui/format.rs:30:5 | LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> tests/ui/format.rs:37:5 + --> tests/ui/format.rs:38:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:67:5 + --> tests/ui/format.rs:68:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:69:5 + --> tests/ui/format.rs:70:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> tests/ui/format.rs:73:18 + --> tests/ui/format.rs:74:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> tests/ui/format.rs:77:22 + --> tests/ui/format.rs:78:22 | LL | let _s: String = format!("{}", &*v.join("\n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("\n")).to_string()` error: useless use of `format!` - --> tests/ui/format.rs:83:13 + --> tests/ui/format.rs:84:13 | LL | let _ = format!("{x}"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:85:13 + --> tests/ui/format.rs:86:13 | LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:89:13 + --> tests/ui/format.rs:90:13 | LL | let _ = format!("{abc}"); | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:91:13 + --> tests/ui/format.rs:92:13 | LL | let _ = format!("{xx}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` diff --git a/src/tools/clippy/tests/ui/if_not_else.fixed b/src/tools/clippy/tests/ui/if_not_else.fixed new file mode 100644 index 0000000000000..11d1e13179c48 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_not_else.fixed @@ -0,0 +1,73 @@ +#![warn(clippy::all)] +#![warn(clippy::if_not_else)] + +fn foo() -> bool { + unimplemented!() +} +fn bla() -> bool { + unimplemented!() +} + +fn main() { + if bla() { + println!("Bunny"); + } else { + //~^ ERROR: unnecessary boolean `not` operation + println!("Bugs"); + } + if 4 == 5 { + println!("Bunny"); + } else { + //~^ ERROR: unnecessary `!=` operation + println!("Bugs"); + } + if !foo() { + println!("Foo"); + } else if !bla() { + println!("Bugs"); + } else { + println!("Bunny"); + } + + if (foo() && bla()) { + println!("both true"); + } else { + #[cfg(not(debug_assertions))] + println!("not debug"); + #[cfg(debug_assertions)] + println!("debug"); + if foo() { + println!("foo"); + } else if bla() { + println!("bla"); + } else { + println!("both false"); + } + } +} + +fn with_comments() { + if foo() { + println!("foo"); /* foo */ + } else { + /* foo is false */ + println!("foo is false"); + } + + if bla() { + println!("bla"); // bla + } else { + // bla is false + println!("bla"); + } +} + +fn with_annotations() { + #[cfg(debug_assertions)] + if foo() { + println!("foo"); /* foo */ + } else { + /* foo is false */ + println!("foo is false"); + } +} diff --git a/src/tools/clippy/tests/ui/if_not_else.rs b/src/tools/clippy/tests/ui/if_not_else.rs index fd30e3702a275..fcc67e163e8a4 100644 --- a/src/tools/clippy/tests/ui/if_not_else.rs +++ b/src/tools/clippy/tests/ui/if_not_else.rs @@ -28,4 +28,46 @@ fn main() { } else { println!("Bunny"); } + + if !(foo() && bla()) { + #[cfg(not(debug_assertions))] + println!("not debug"); + #[cfg(debug_assertions)] + println!("debug"); + if foo() { + println!("foo"); + } else if bla() { + println!("bla"); + } else { + println!("both false"); + } + } else { + println!("both true"); + } +} + +fn with_comments() { + if !foo() { + /* foo is false */ + println!("foo is false"); + } else { + println!("foo"); /* foo */ + } + + if !bla() { + // bla is false + println!("bla"); + } else { + println!("bla"); // bla + } +} + +fn with_annotations() { + #[cfg(debug_assertions)] + if !foo() { + /* foo is false */ + println!("foo is false"); + } else { + println!("foo"); /* foo */ + } } diff --git a/src/tools/clippy/tests/ui/if_not_else.stderr b/src/tools/clippy/tests/ui/if_not_else.stderr index 92fed7b1bf700..b01cb5af11f74 100644 --- a/src/tools/clippy/tests/ui/if_not_else.stderr +++ b/src/tools/clippy/tests/ui/if_not_else.stderr @@ -9,9 +9,17 @@ LL | | println!("Bunny"); LL | | } | |_____^ | - = help: remove the `!` and swap the blocks of the `if`/`else` = note: `-D clippy::if-not-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::if_not_else)]` +help: try + | +LL ~ if bla() { +LL + println!("Bunny"); +LL + } else { +LL + +LL + println!("Bugs"); +LL + } + | error: unnecessary `!=` operation --> tests/ui/if_not_else.rs:18:5 @@ -24,7 +32,109 @@ LL | | println!("Bunny"); LL | | } | |_____^ | - = help: change to `==` and swap the blocks of the `if`/`else` +help: try + | +LL ~ if 4 == 5 { +LL + println!("Bunny"); +LL + } else { +LL + +LL + println!("Bugs"); +LL + } + | + +error: unnecessary boolean `not` operation + --> tests/ui/if_not_else.rs:32:5 + | +LL | / if !(foo() && bla()) { +LL | | #[cfg(not(debug_assertions))] +LL | | println!("not debug"); +LL | | #[cfg(debug_assertions)] +... | +LL | | println!("both true"); +LL | | } + | |_____^ + | +help: try + | +LL ~ if (foo() && bla()) { +LL + println!("both true"); +LL + } else { +LL + #[cfg(not(debug_assertions))] +LL + println!("not debug"); +LL + #[cfg(debug_assertions)] +LL + println!("debug"); +LL + if foo() { +LL + println!("foo"); +LL + } else if bla() { +LL + println!("bla"); +LL + } else { +LL + println!("both false"); +LL + } +LL + } + | + +error: unnecessary boolean `not` operation + --> tests/ui/if_not_else.rs:50:5 + | +LL | / if !foo() { +LL | | /* foo is false */ +LL | | println!("foo is false"); +LL | | } else { +LL | | println!("foo"); /* foo */ +LL | | } + | |_____^ + | +help: try + | +LL ~ if foo() { +LL + println!("foo"); /* foo */ +LL + } else { +LL + /* foo is false */ +LL + println!("foo is false"); +LL + } + | + +error: unnecessary boolean `not` operation + --> tests/ui/if_not_else.rs:57:5 + | +LL | / if !bla() { +LL | | // bla is false +LL | | println!("bla"); +LL | | } else { +LL | | println!("bla"); // bla +LL | | } + | |_____^ + | +help: try + | +LL ~ if bla() { +LL + println!("bla"); // bla +LL + } else { +LL + // bla is false +LL + println!("bla"); +LL + } + | + +error: unnecessary boolean `not` operation + --> tests/ui/if_not_else.rs:67:5 + | +LL | / if !foo() { +LL | | /* foo is false */ +LL | | println!("foo is false"); +LL | | } else { +LL | | println!("foo"); /* foo */ +LL | | } + | |_____^ + | +help: try + | +LL ~ if foo() { +LL + println!("foo"); /* foo */ +LL + } else { +LL + /* foo is false */ +LL + println!("foo is false"); +LL + } + | -error: aborting due to 2 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed new file mode 100644 index 0000000000000..ea8e56e18b085 --- /dev/null +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -0,0 +1,177 @@ +#![deny(clippy::index_refutable_slice)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] + +enum SomeEnum { + One(T), + Two(T), + Three(T), + Four(T), +} + +fn lintable_examples() { + // Try with reference + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some([slice_0, ..]) = slice { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + println!("{}", slice_0); + } + + // Try with copy + let slice: Option<[u32; 3]> = Some([1, 2, 3]); + if let Some([slice_0, ..]) = slice { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + println!("{}", slice_0); + } + + // Try with long slice and small indices + let slice: Option<[u32; 9]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9]); + if let Some([slice_0, _, slice_2, ..]) = slice { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + println!("{}", slice_2); + println!("{}", slice_0); + } + + // Multiple bindings + let slice_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([5, 6, 7]); + if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + println!("{}", slice_0); + } + + // Two lintable slices in one if let + let a_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([9, 5, 1]); + let b_wrapped: Option<[u32; 2]> = Some([4, 6]); + if let (SomeEnum::Three([_, _, a_2, ..]), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + //~| ERROR: this binding can be a slice pattern to avoid indexing + println!("{} -> {}", a_2, b_1); + } + + // This requires the slice values to be borrowed as the slice values can only be + // borrowed and `String` doesn't implement copy + let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]); + if let Some([_, ref slice_1, ..]) = slice { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + println!("{:?}", slice_1); + } + println!("{:?}", slice); + + // This should not suggest using the `ref` keyword as the scrutinee is already + // a reference + let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]); + if let Some([slice_0, ..]) = &slice { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + println!("{:?}", slice_0); + } + println!("{:?}", slice); +} + +fn slice_index_above_limit() { + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + + if let Some(slice) = slice { + // Would cause a panic, IDK + println!("{}", slice[7]); + } +} + +fn slice_is_used() { + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some(slice) = slice { + println!("{:?}", slice.len()); + } + + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some(slice) = slice { + println!("{:?}", slice.to_vec()); + } + + let opt: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]); + if let Some(slice) = opt { + if !slice.is_empty() { + println!("first: {}", slice[0]); + } + } +} + +/// The slice is used by an external function and should therefore not be linted +fn check_slice_as_arg() { + fn is_interesting(slice: &[T; 2]) -> bool { + !slice.is_empty() + } + + let slice_wrapped: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]); + if let Some(slice) = &slice_wrapped { + if is_interesting(slice) { + println!("This is interesting {}", slice[0]); + } + } + println!("{:?}", slice_wrapped); +} + +fn check_slice_in_struct() { + #[derive(Debug)] + struct Wrapper<'a> { + inner: Option<&'a [String]>, + is_awesome: bool, + } + + impl<'a> Wrapper<'a> { + fn is_super_awesome(&self) -> bool { + self.is_awesome + } + } + + let inner = &[String::from("New"), String::from("World")]; + let wrap = Wrapper { + inner: Some(inner), + is_awesome: true, + }; + + // Test 1: Field access + if let Some([slice_0, ..]) = wrap.inner { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + if wrap.is_awesome { + println!("This is awesome! {}", slice_0); + } + } + + // Test 2: function access + if let Some([slice_0, ..]) = wrap.inner { + //~^ ERROR: this binding can be a slice pattern to avoid indexing + if wrap.is_super_awesome() { + println!("This is super awesome! {}", slice_0); + } + } + println!("Complete wrap: {:?}", wrap); +} + +/// This would be a nice additional feature to have in the future, but adding it +/// now would make the PR too large. This is therefore only a test that we don't +/// lint cases we can't make a reasonable suggestion for +fn mutable_slice_index() { + // Mut access + let mut slice: Option<[String; 1]> = Some([String::from("Penguin")]); + if let Some(ref mut slice) = slice { + slice[0] = String::from("Mr. Penguin"); + } + println!("Use after modification: {:?}", slice); + + // Mut access on reference + let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); + if let Some(slice) = &mut slice { + slice[0] = String::from("Lord Meow Meow"); + } + println!("Use after modification: {:?}", slice); +} + +/// The lint will ignore bindings with sub patterns as it would be hard +/// to build correct suggestions for these instances :) +fn binding_with_sub_pattern() { + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some(slice @ [_, _, _]) = slice { + println!("{:?}", slice[2]); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs index a4cb50bd68220..1c1d1c4cbe46b 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,8 +1,6 @@ #![deny(clippy::index_refutable_slice)] #![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - enum SomeEnum { One(T), Two(T), diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr index 8819cb0e28bfb..14ee2e54cab10 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:16:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:14:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -9,150 +9,134 @@ note: the lint level is defined here | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([slice_0, ..]) = slice { - | ~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([slice_0, ..]) = slice { +LL | +LL ~ println!("{}", slice_0); | -LL | println!("{}", slice_0); - | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:23:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:21:17 | LL | if let Some(slice) = slice { | ^^^^^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([slice_0, ..]) = slice { - | ~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([slice_0, ..]) = slice { +LL | +LL ~ println!("{}", slice_0); | -LL | println!("{}", slice_0); - | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:30:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:28:17 | LL | if let Some(slice) = slice { | ^^^^^ | -help: try using a slice pattern here - | -LL | if let Some([slice_0, _, slice_2, ..]) = slice { - | ~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and replace the index expressions here +help: replace the binding and indexed access with a slice pattern | +LL ~ if let Some([slice_0, _, slice_2, ..]) = slice { +LL | LL ~ println!("{}", slice_2); LL ~ println!("{}", slice_0); | error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:38:26 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:36:26 | LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped { | ^^^^^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped { - | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped { +LL | +LL ~ println!("{}", slice_0); | -LL | println!("{}", slice_0); - | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:46:29 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:44:29 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let (SomeEnum::Three([_, _, a_2, ..]), Some(b)) = (a_wrapped, b_wrapped) { - | ~~~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let (SomeEnum::Three([_, _, a_2, ..]), Some(b)) = (a_wrapped, b_wrapped) { +LL | +LL | +LL ~ println!("{} -> {}", a_2, b[1]); | -LL | println!("{} -> {}", a_2, b[1]); - | ~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:46:38 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:44:38 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let (SomeEnum::Three(a), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) { - | ~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let (SomeEnum::Three(a), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) { +LL | +LL | +LL ~ println!("{} -> {}", a[2], b_1); | -LL | println!("{} -> {}", a[2], b_1); - | ~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:55:21 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:53:21 | LL | if let Some(ref slice) = slice { | ^^^^^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([_, ref slice_1, ..]) = slice { - | ~~~~~~~~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([_, ref slice_1, ..]) = slice { +LL | +LL ~ println!("{:?}", slice_1); | -LL | println!("{:?}", slice_1); - | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:64:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:62:17 | LL | if let Some(slice) = &slice { | ^^^^^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([slice_0, ..]) = &slice { - | ~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([slice_0, ..]) = &slice { +LL | +LL ~ println!("{:?}", slice_0); | -LL | println!("{:?}", slice_0); - | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:134:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:132:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([slice_0, ..]) = wrap.inner { - | ~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([slice_0, ..]) = wrap.inner { +LL | +LL | if wrap.is_awesome { +LL ~ println!("This is awesome! {}", slice_0); | -LL | println!("This is awesome! {}", slice_0); - | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:142:17 + --> tests/ui/index_refutable_slice/if_let_slice_binding.rs:140:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ | -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([slice_0, ..]) = wrap.inner { - | ~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([slice_0, ..]) = wrap.inner { +LL | +LL | if wrap.is_super_awesome() { +LL ~ println!("This is super awesome! {}", slice_0); | -LL | println!("This is super awesome! {}", slice_0); - | ~~~~~~~ error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed new file mode 100644 index 0000000000000..72edc539f0431 --- /dev/null +++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed @@ -0,0 +1,29 @@ +#![deny(clippy::index_refutable_slice)] + +extern crate if_chain; +use if_chain::if_chain; + +macro_rules! if_let_slice_macro { + () => { + // This would normally be linted + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some(slice) = slice { + println!("{}", slice[0]); + } + }; +} + +fn main() { + // Don't lint this + if_let_slice_macro!(); + + // Do lint this + if_chain! { + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some([slice_0, ..]) = slice; + //~^ ERROR: this binding can be a slice pattern to avoid indexing + then { + println!("{}", slice_0); + } + } +} diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs index 5d9fad4888999..7b474ba423b9a 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs +++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs @@ -1,7 +1,5 @@ #![deny(clippy::index_refutable_slice)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - extern crate if_chain; use if_chain::if_chain; diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr index 69f0aaa977785..64741abb91149 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr +++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:25:21 + --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:23:21 | LL | if let Some(slice) = slice; | ^^^^^ @@ -9,14 +9,13 @@ note: the lint level is defined here | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using a slice pattern here +help: replace the binding and indexed access with a slice pattern | -LL | if let Some([slice_0, ..]) = slice; - | ~~~~~~~~~~~~~ -help: and replace the index expressions here +LL ~ if let Some([slice_0, ..]) = slice; +LL | +LL | then { +LL ~ println!("{}", slice_0); | -LL | println!("{}", slice_0); - | ~~~~~~~ error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed new file mode 100644 index 0000000000000..3456e274f6a4d --- /dev/null +++ b/src/tools/clippy/tests/ui/let_unit.fixed @@ -0,0 +1,196 @@ +#![warn(clippy::let_unit_value)] +#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] + +macro_rules! let_and_return { + ($n:expr) => {{ + let ret = $n; + }}; +} + +fn main() { + println!("x"); + let _y = 1; // this is fine + let _z = ((), 1); // this as well + if true { + // do not lint this, since () is explicit + let _a = (); + let () = dummy(); + let () = (); + () = dummy(); + () = (); + let _a: () = (); + let _a: () = dummy(); + } + + consume_units_with_for_loop(); // should be fine as well + + multiline_sugg(); + + let_and_return!(()) // should be fine +} + +fn dummy() {} + +// Related to issue #1964 +fn consume_units_with_for_loop() { + // `for_let_unit` lint should not be triggered by consuming them using for loop. + let v = vec![(), (), ()]; + let mut count = 0; + for _ in v { + count += 1; + } + assert_eq!(count, 3); + + // Same for consuming from some other Iterator. + let (tx, rx) = ::std::sync::mpsc::channel(); + tx.send(()).unwrap(); + drop(tx); + + count = 0; + for _ in rx.iter() { + count += 1; + } + assert_eq!(count, 1); +} + +fn multiline_sugg() { + let v: Vec = vec![2]; + + v + .into_iter() + .map(|i| i * 2) + .filter(|i| i % 2 == 0) + .map(|_| ()) + .next() + .unwrap(); +} + +#[derive(Copy, Clone)] +pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f() -> T { + unimplemented!() + } + fn f2(_: T) -> U { + unimplemented!() + } + fn f3(x: T) -> T { + x + } + fn f5(x: bool) -> Option { + x.then(|| T::default()) + } + + let _: () = f(); + let x: () = f(); + + let _: () = f2(0i32); + let x: () = f2(0i32); + + let _: () = f3(()); + let x: () = f3(()); + + fn f4(mut x: Vec) -> T { + x.pop().unwrap() + } + let _: () = f4(vec![()]); + let x: () = f4(vec![()]); + + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; + let x: () = if true { f() } else { f2(0) }; + + match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; + + let _: () = f5(true).unwrap(); + + #[allow(clippy::let_unit_value)] + { + let x = f(); + let y; + let z; + match 0 { + 0 => { + y = f(); + z = f(); + }, + 1 => { + println!("test"); + y = f(); + z = f3(()); + }, + _ => panic!(), + } + + let x1; + let x2; + if true { + x1 = f(); + x2 = x1; + } else { + x2 = f(); + x1 = x2; + } + + let opt; + match f5(true) { + Some(x) => opt = x, + None => panic!(), + }; + + #[warn(clippy::let_unit_value)] + { + let _: () = x; + let _: () = y; + let _: () = z; + let _: () = x1; + let _: () = x2; + let _: () = opt; + } + } + + let () = f(); +} + +fn attributes() { + fn f() {} + + #[allow(clippy::let_unit_value)] + let _ = f(); + #[expect(clippy::let_unit_value)] + let _ = f(); +} + +async fn issue10433() { + let _pending: () = std::future::pending().await; +} + +pub async fn issue11502(a: ()) {} + +pub fn issue12594() { + fn returns_unit() {} + + fn returns_result(res: T) -> Result { + Ok(res) + } + + fn actual_test() { + // create first a unit value'd value + returns_unit(); + returns_result(()).unwrap(); + returns_result(()).unwrap(); + // make sure we replace only the first variable + let res = 1; + returns_result(res).unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs index 530103ffaf6ec..e2dafbcb77146 100644 --- a/src/tools/clippy/tests/ui/let_unit.rs +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -1,8 +1,6 @@ #![warn(clippy::let_unit_value)] #![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - macro_rules! let_and_return { ($n:expr) => {{ let ret = $n; diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr index 6f149454af2d0..a2f368f22e5bb 100644 --- a/src/tools/clippy/tests/ui/let_unit.stderr +++ b/src/tools/clippy/tests/ui/let_unit.stderr @@ -1,5 +1,5 @@ error: this let-binding has unit value - --> tests/ui/let_unit.rs:13:5 + --> tests/ui/let_unit.rs:11:5 | LL | let _x = println!("x"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` @@ -8,7 +8,7 @@ LL | let _x = println!("x"); = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` error: this let-binding has unit value - --> tests/ui/let_unit.rs:61:5 + --> tests/ui/let_unit.rs:59:5 | LL | / let _ = v LL | | .into_iter() @@ -31,7 +31,7 @@ LL + .unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:110:5 + --> tests/ui/let_unit.rs:108:5 | LL | / let x = match Some(0) { LL | | None => f2(1), @@ -52,23 +52,17 @@ LL + }; | error: this let-binding has unit value - --> tests/ui/let_unit.rs:191:9 + --> tests/ui/let_unit.rs:189:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: omit the `let` binding - | -LL | returns_unit(); - | -help: variable `res` of type `()` can be replaced with explicit `()` +help: omit the `let` binding and replace variable usages with `()` | -LL | returns_result(()).unwrap(); - | ~~ -help: variable `res` of type `()` can be replaced with explicit `()` +LL ~ returns_unit(); +LL ~ returns_result(()).unwrap(); +LL ~ returns_result(()).unwrap(); | -LL | returns_result(()).unwrap(); - | ~~ error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/literal_string_with_formatting_arg.rs b/src/tools/clippy/tests/ui/literal_string_with_formatting_arg.rs new file mode 100644 index 0000000000000..f257c66f59d36 --- /dev/null +++ b/src/tools/clippy/tests/ui/literal_string_with_formatting_arg.rs @@ -0,0 +1,37 @@ +#![warn(clippy::literal_string_with_formatting_args)] +#![allow(clippy::unnecessary_literal_unwrap)] + +fn main() { + let x: Option = None; + let y = "hello"; + x.expect("{y} {}"); //~ literal_string_with_formatting_args + x.expect(" {y} bla"); //~ literal_string_with_formatting_args + x.expect("{:?}"); //~ literal_string_with_formatting_args + x.expect("{y:?}"); //~ literal_string_with_formatting_args + x.expect(" {y:?} {y:?} "); //~ literal_string_with_formatting_args + x.expect(" {y:..} {y:?} "); //~ literal_string_with_formatting_args + x.expect(r"{y:?} {y:?} "); //~ literal_string_with_formatting_args + x.expect(r"{y:?} y:?}"); //~ literal_string_with_formatting_args + x.expect(r##" {y:?} {y:?} "##); //~ literal_string_with_formatting_args + // Ensure that it doesn't try to go in the middle of a unicode character. + x.expect("———{:?}"); //~ literal_string_with_formatting_args + + // Should not lint! + format!("{y:?}"); + println!("{y:?}"); + x.expect(" {} "); // We ignore `{}` to limit false positives. + x.expect(" { } "); // We ignore `{}` to limit false positives. + x.expect("{{y} {x"); + x.expect("{{y:?}"); + x.expect(" {0}"); // If it only contains an integer, we ignore it. + x.expect(r##" {x:?} "##); // `x` doesn't exist so we shoud not lint + x.expect("{y:...}"); + let _ = "fn main {\n\ + }"; + // Unicode characters escape should not lint either. + "\u{0052}".to_string(); + + // Regression test for . + let x: Option = Some(0); + x.expect("{…}"); +} diff --git a/src/tools/clippy/tests/ui/literal_string_with_formatting_arg.stderr b/src/tools/clippy/tests/ui/literal_string_with_formatting_arg.stderr new file mode 100644 index 0000000000000..32a84f600da72 --- /dev/null +++ b/src/tools/clippy/tests/ui/literal_string_with_formatting_arg.stderr @@ -0,0 +1,71 @@ +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:7:15 + | +LL | x.expect("{y} {}"); + | ^^^ + | + = note: `-D clippy::literal-string-with-formatting-args` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_args)]` + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:8:16 + | +LL | x.expect(" {y} bla"); + | ^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:9:15 + | +LL | x.expect("{:?}"); + | ^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:10:15 + | +LL | x.expect("{y:?}"); + | ^^^^^ + +error: these look like formatting arguments but are not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:11:16 + | +LL | x.expect(" {y:?} {y:?} "); + | ^^^^^ ^^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:12:23 + | +LL | x.expect(" {y:..} {y:?} "); + | ^^^^^ + +error: these look like formatting arguments but are not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:13:16 + | +LL | x.expect(r"{y:?} {y:?} "); + | ^^^^^ ^^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:14:16 + | +LL | x.expect(r"{y:?} y:?}"); + | ^^^^^ + +error: these look like formatting arguments but are not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:15:19 + | +LL | x.expect(r##" {y:?} {y:?} "##); + | ^^^^^ ^^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:17:18 + | +LL | x.expect("———{:?}"); + | ^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:27:19 + | +LL | x.expect(r##" {x:?} "##); // `x` doesn't exist so we shoud not lint + | ^^^^^ + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed new file mode 100644 index 0000000000000..dc1cb8e11fca6 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed @@ -0,0 +1,116 @@ +#![warn(clippy::manual_async_fn)] +#![allow(clippy::needless_pub_self, unused)] + +use std::future::Future; + +async fn fut() -> i32 { 42 } + +#[rustfmt::skip] +async fn fut2() -> i32 { 42 } + +#[rustfmt::skip] +async fn fut3() -> i32 { 42 } + +async fn empty_fut() {} + +#[rustfmt::skip] +async fn empty_fut2() {} + +#[rustfmt::skip] +async fn empty_fut3() {} + +async fn core_fut() -> i32 { 42 } + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S; +impl S { + async fn inh_fut() -> i32 { + // NOTE: this code is here just to check that the indentation is correct in the suggested fix + let a = 42; + let b = 21; + if a < b { + let c = 21; + let d = 42; + if c < d { + let _ = 42; + } + } + 42 + } + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +// Tests related to lifetime capture + +async fn elided(_: &i32) -> i32 { 42 } + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +#[allow(clippy::needless_lifetimes)] +async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + +pub async fn issue_10450() -> i32 { 42 } + +pub(crate) async fn issue_10450_2() -> i32 { 42 } + +pub(self) async fn issue_10450_3() -> i32 { 42 } + +fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs index 6b8ac5033a92d..9ca7654a36882 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.rs +++ b/src/tools/clippy/tests/ui/manual_async_fn.rs @@ -1,8 +1,6 @@ #![warn(clippy::manual_async_fn)] #![allow(clippy::needless_pub_self, unused)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - use std::future::Future; fn fut() -> impl Future { @@ -99,6 +97,7 @@ fn elided_not_bound(_: &i32) -> impl Future { async { 42 } } +#[allow(clippy::needless_lifetimes)] fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { async { 42 } } diff --git a/src/tools/clippy/tests/ui/manual_async_fn.stderr b/src/tools/clippy/tests/ui/manual_async_fn.stderr index f88fc30b3b54e..68a97243436df 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.stderr +++ b/src/tools/clippy/tests/ui/manual_async_fn.stderr @@ -1,5 +1,5 @@ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:8:1 + --> tests/ui/manual_async_fn.rs:6:1 | LL | fn fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,116 +8,84 @@ LL | fn fut() -> impl Future { = help: to override `-D warnings` add `#[allow(clippy::manual_async_fn)]` help: make the function `async` and return the output of the future directly | -LL | async fn fut() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | fn fut() -> impl Future { 42 } - | ~~~~~~ +LL | async fn fut() -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:13:1 + --> tests/ui/manual_async_fn.rs:11:1 | LL | fn fut2() ->impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn fut2() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | fn fut2() ->impl Future { 42 } - | ~~~~~~ +LL | async fn fut2() -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:18:1 + --> tests/ui/manual_async_fn.rs:16:1 | LL | fn fut3()-> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn fut3() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | fn fut3()-> impl Future { 42 } - | ~~~~~~ +LL | async fn fut3() -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:22:1 + --> tests/ui/manual_async_fn.rs:20:1 | LL | fn empty_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: make the function `async` and remove the return type - | -LL | async fn empty_fut() { - | ~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function +help: make the function `async` and return the output of the future directly | -LL | fn empty_fut() -> impl Future {} - | ~~ +LL | async fn empty_fut() {} + | ~~~~~~~~~~~~~~~~~~~~ ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:27:1 + --> tests/ui/manual_async_fn.rs:25:1 | LL | fn empty_fut2() ->impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: make the function `async` and remove the return type - | -LL | async fn empty_fut2() { - | ~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function +help: make the function `async` and return the output of the future directly | -LL | fn empty_fut2() ->impl Future {} - | ~~ +LL | async fn empty_fut2() {} + | ~~~~~~~~~~~~~~~~~~~~~ ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:32:1 + --> tests/ui/manual_async_fn.rs:30:1 | LL | fn empty_fut3()-> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: make the function `async` and remove the return type - | -LL | async fn empty_fut3() { - | ~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function +help: make the function `async` and return the output of the future directly | -LL | fn empty_fut3()-> impl Future {} - | ~~ +LL | async fn empty_fut3() {} + | ~~~~~~~~~~~~~~~~~~~~~ ~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:36:1 + --> tests/ui/manual_async_fn.rs:34:1 | LL | fn core_fut() -> impl core::future::Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn core_fut() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | fn core_fut() -> impl core::future::Future { 42 } - | ~~~~~~ +LL | async fn core_fut() -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:58:5 + --> tests/ui/manual_async_fn.rs:56:5 | LL | fn inh_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn inh_fut() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL ~ fn inh_fut() -> impl Future { +LL ~ async fn inh_fut() -> i32 { LL + // NOTE: this code is here just to check that the indentation is correct in the suggested fix LL + let a = 42; LL + let b = 21; @@ -133,79 +101,59 @@ LL + } | error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:93:1 + --> tests/ui/manual_async_fn.rs:91:1 | LL | fn elided(_: &i32) -> impl Future + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn elided(_: &i32) -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | fn elided(_: &i32) -> impl Future + '_ { 42 } - | ~~~~~~ +LL | async fn elided(_: &i32) -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:102:1 + --> tests/ui/manual_async_fn.rs:101:1 | LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } - | ~~~~~~ +LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:131:1 + --> tests/ui/manual_async_fn.rs:130:1 | LL | pub fn issue_10450() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | pub async fn issue_10450() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | pub fn issue_10450() -> impl Future { 42 } - | ~~~~~~ +LL | pub async fn issue_10450() -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:135:1 + --> tests/ui/manual_async_fn.rs:134:1 | LL | pub(crate) fn issue_10450_2() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | pub(crate) async fn issue_10450_2() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | pub(crate) fn issue_10450_2() -> impl Future { 42 } - | ~~~~~~ +LL | pub(crate) async fn issue_10450_2() -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: this function can be simplified using the `async fn` syntax - --> tests/ui/manual_async_fn.rs:139:1 + --> tests/ui/manual_async_fn.rs:138:1 | LL | pub(self) fn issue_10450_3() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | pub(self) async fn issue_10450_3() -> i32 { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: move the body of the async block to the enclosing function - | -LL | pub(self) fn issue_10450_3() -> impl Future { 42 } - | ~~~~~~ +LL | pub(self) async fn issue_10450_3() -> i32 { 42 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~ error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/manual_split_once.fixed b/src/tools/clippy/tests/ui/manual_split_once.fixed new file mode 100644 index 0000000000000..aaac6a048e1d0 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_split_once.fixed @@ -0,0 +1,144 @@ +#![warn(clippy::manual_split_once)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] + +extern crate itertools; + +#[allow(unused_imports)] +use itertools::Itertools; + +fn main() { + let _ = "key=value".splitn(2, '=').nth(2); + let _ = "key=value".split_once('=').unwrap().1; + let _ = "key=value".split_once('=').unwrap().1; + let (_, _) = "key=value".split_once('=').unwrap(); + + let s = String::from("key=value"); + let _ = s.split_once('=').unwrap().1; + + let s = Box::::from("key=value"); + let _ = s.split_once('=').unwrap().1; + + let s = &"key=value"; + let _ = s.split_once('=').unwrap().1; + + fn _f(s: &str) -> Option<&str> { + let _ = s.split_once('=')?.1; + let _ = s.split_once('=')?.1; + let _ = s.rsplit_once('=')?.0; + let _ = s.rsplit_once('=')?.0; + None + } + + // Don't lint, slices don't have `split_once` + let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); + + // `rsplitn` gives the results in the reverse order of `rsplit_once` + let _ = "key=value".rsplit_once('=').unwrap().0; + let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); + let _ = s.rsplit_once('=').map(|x| x.0); +} + +fn indirect() -> Option<()> { + let (l, r) = "a.b.c".split_once('.').unwrap(); + + + + let (l, r) = "a.b.c".split_once('.')?; + + + + let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + + + + let (l, r) = "a.b.c".rsplit_once('.')?; + + + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None +} + +#[clippy::msrv = "1.51"] +fn _msrv_1_51() { + // `str::split_once` was stabilized in 1.52. Do not lint this + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); +} + +#[clippy::msrv = "1.52"] +fn _msrv_1_52() { + let _ = "key=value".split_once('=').unwrap().1; + + let (a, b) = "a.b.c".split_once('.').unwrap(); + + +} diff --git a/src/tools/clippy/tests/ui/manual_split_once.rs b/src/tools/clippy/tests/ui/manual_split_once.rs index e13c827468b83..113e1737c97da 100644 --- a/src/tools/clippy/tests/ui/manual_split_once.rs +++ b/src/tools/clippy/tests/ui/manual_split_once.rs @@ -1,8 +1,6 @@ #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - extern crate itertools; #[allow(unused_imports)] diff --git a/src/tools/clippy/tests/ui/manual_split_once.stderr b/src/tools/clippy/tests/ui/manual_split_once.stderr index 566204ad8762e..366d860f25efd 100644 --- a/src/tools/clippy/tests/ui/manual_split_once.stderr +++ b/src/tools/clippy/tests/ui/manual_split_once.stderr @@ -1,5 +1,5 @@ error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:13:13 + --> tests/ui/manual_split_once.rs:11:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` @@ -8,79 +8,79 @@ LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); = help: to override `-D warnings` add `#[allow(clippy::manual_split_once)]` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:14:13 + --> tests/ui/manual_split_once.rs:12:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:15:18 + --> tests/ui/manual_split_once.rs:13:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=')` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:18:13 + --> tests/ui/manual_split_once.rs:16:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:21:13 + --> tests/ui/manual_split_once.rs:19:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:24:13 + --> tests/ui/manual_split_once.rs:22:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:27:17 + --> tests/ui/manual_split_once.rs:25:17 | LL | let _ = s.splitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:28:17 + --> tests/ui/manual_split_once.rs:26:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:29:17 + --> tests/ui/manual_split_once.rs:27:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:30:17 + --> tests/ui/manual_split_once.rs:28:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:38:13 + --> tests/ui/manual_split_once.rs:36:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:39:18 + --> tests/ui/manual_split_once.rs:37:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:40:13 + --> tests/ui/manual_split_once.rs:38:13 | LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:44:5 + --> tests/ui/manual_split_once.rs:42:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,21 +89,15 @@ LL | let l = iter.next().unwrap(); LL | let r = iter.next().unwrap(); | ----------------------------- second usage here | -help: try `split_once` - | -LL | let (l, r) = "a.b.c".split_once('.').unwrap(); +help: replace with `split_once` | -help: remove the `iter` usages - | -LL - let l = iter.next().unwrap(); - | -help: remove the `iter` usages - | -LL - let r = iter.next().unwrap(); +LL ~ let (l, r) = "a.b.c".split_once('.').unwrap(); +LL ~ +LL ~ | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:48:5 + --> tests/ui/manual_split_once.rs:46:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,21 +106,15 @@ LL | let l = iter.next()?; LL | let r = iter.next()?; | --------------------- second usage here | -help: try `split_once` - | -LL | let (l, r) = "a.b.c".split_once('.')?; - | -help: remove the `iter` usages - | -LL - let l = iter.next()?; +help: replace with `split_once` | -help: remove the `iter` usages - | -LL - let r = iter.next()?; +LL ~ let (l, r) = "a.b.c".split_once('.')?; +LL ~ +LL ~ | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:52:5 + --> tests/ui/manual_split_once.rs:50:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,21 +123,15 @@ LL | let r = iter.next().unwrap(); LL | let l = iter.next().unwrap(); | ----------------------------- second usage here | -help: try `rsplit_once` - | -LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap(); - | -help: remove the `iter` usages - | -LL - let r = iter.next().unwrap(); +help: replace with `rsplit_once` | -help: remove the `iter` usages - | -LL - let l = iter.next().unwrap(); +LL ~ let (l, r) = "a.b.c".rsplit_once('.').unwrap(); +LL ~ +LL ~ | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:56:5 + --> tests/ui/manual_split_once.rs:54:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -158,27 +140,21 @@ LL | let r = iter.next()?; LL | let l = iter.next()?; | --------------------- second usage here | -help: try `rsplit_once` - | -LL | let (l, r) = "a.b.c".rsplit_once('.')?; - | -help: remove the `iter` usages +help: replace with `rsplit_once` | -LL - let r = iter.next()?; - | -help: remove the `iter` usages - | -LL - let l = iter.next()?; +LL ~ let (l, r) = "a.b.c".rsplit_once('.')?; +LL ~ +LL ~ | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:141:13 + --> tests/ui/manual_split_once.rs:139:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:143:5 + --> tests/ui/manual_split_once.rs:141:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -187,17 +163,11 @@ LL | let a = iter.next().unwrap(); LL | let b = iter.next().unwrap(); | ----------------------------- second usage here | -help: try `split_once` - | -LL | let (a, b) = "a.b.c".split_once('.').unwrap(); - | -help: remove the `iter` usages - | -LL - let a = iter.next().unwrap(); - | -help: remove the `iter` usages +help: replace with `split_once` | -LL - let b = iter.next().unwrap(); +LL ~ let (a, b) = "a.b.c".split_once('.').unwrap(); +LL ~ +LL ~ | error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr index 3c0382767c3fc..4a4772da143a2 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -20,13 +20,10 @@ LL | (1, .., 3) => 42, | ^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns +help: or try merging the arm patterns and removing the obsolete arm | -LL | (1, .., 3) | (.., 3) => 42, - | ~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm - | -LL - (.., 3) => 42, +LL ~ (1, .., 3) | (.., 3) => 42, +LL ~ _ => 0, | error: this match arm has an identical body to another arm @@ -36,13 +33,11 @@ LL | 51 => 1, | ^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | 51 | 42 => 1, - | ~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - 42 => 1, +LL - 51 => 1, +LL + 51 | 42 => 1, | error: this match arm has an identical body to another arm @@ -52,13 +47,10 @@ LL | 41 => 2, | ^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns +help: or try merging the arm patterns and removing the obsolete arm | -LL | 41 | 52 => 2, - | ~~~~~~~ -help: and remove this obsolete arm - | -LL - 52 => 2, +LL ~ 41 | 52 => 2, +LL ~ _ => 0, | error: this match arm has an identical body to another arm @@ -68,13 +60,11 @@ LL | 2 => 2, | ^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | 2 | 1 => 2, - | ~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - 1 => 2, +LL - 2 => 2, +LL + 2 | 1 => 2, | error: this match arm has an identical body to another arm @@ -84,13 +74,11 @@ LL | 3 => 2, | ^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns +help: or try merging the arm patterns and removing the obsolete arm | -LL | 3 | 1 => 2, - | ~~~~~ -help: and remove this obsolete arm - | -LL - 1 => 2, +LL ~ 2 => 2, +LL | +LL ~ 3 | 1 => 2, | error: this match arm has an identical body to another arm @@ -100,14 +88,11 @@ LL | 2 => 2, | ^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns +help: or try merging the arm patterns and removing the obsolete arm | -LL | 2 | 3 => 2, - | ~~~~~ -help: and remove this obsolete arm - | -LL - 3 => 2, -LL + +LL ~ 2 | 3 => 2, +LL | +LL ~ | error: this match arm has an identical body to another arm @@ -117,13 +102,11 @@ LL | CommandInfo::External { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), +LL - CommandInfo::External { name, .. } => name.to_string(), +LL + CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), | error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed new file mode 100644 index 0000000000000..b7d377f1ebffc --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed @@ -0,0 +1,259 @@ +#![warn(clippy::match_same_arms)] +#![allow( + clippy::disallowed_names, + clippy::diverging_sub_expression, + clippy::uninlined_format_args, + clippy::match_single_binding, + clippy::match_like_matches_macro +)] + +fn bar(_: T) {} +fn foo() -> bool { + unimplemented!() +} + +fn match_same_arms() { + let _ = match 42 { + _ => { + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + }; + //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm + + let _ = match 42 { + 51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm + _ => true, + }; + + let _ = match Some(42) { + None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm + }; + + let _ = match Some(42) { + Some(foo) => 24, + None => 24, + }; + + let _ = match Some(42) { + Some(42) => 24, + Some(a) => 24, // bindings are different + None => 0, + }; + + let _ = match Some(42) { + Some(a) if a > 0 => 24, + Some(a) => 24, // one arm has a guard + None => 0, + }; + + match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + // No warning because guards are different + let _ = match Some(42) { + Some(a) if a == 42 => a, + Some(a) if a == 24 => a, + Some(_) => 24, + None => 0, + }; + + let _ = match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + match (Some(42), Some(42)) { + (Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + let _ = match Some(()) { + Some(()) => 0.0, + None => -0.0, + }; + + match (Some(42), Some("")) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), // bindings have different types + _ => (), + } + + let x: Result = Ok(3); + + // No warning because of the guard. + match x { + Ok(x) if x * x == 64 => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // This used to be a false positive; see issue #1996. + match x { + Ok(3) => println!("ok"), + Ok(x) if x * x == 64 => println!("ok 64"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + match (x, Some(1i32)) { + (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm + _ => println!("err"), + } + + // No warning; different types for `x`. + match (x, Some(1.0f64)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // False negative #2251. + match x { + Ok(_tmp) => println!("ok"), + Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm + Err(_) => { + unreachable!(); + }, + } + + // False positive #1390 + macro_rules! empty { + ($e:expr) => {}; + } + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(1); + }, + x => { + empty!(x); + }, + }; + + // still lint if the tokens are the same + match 0 { + 1 | 0 => { + empty!(0); + }, + x => { + empty!(x); + }, + } + //~^^^^^^^ ERROR: this match arm has an identical body to another arm + + match_expr_like_matches_macro_priority(); +} + +fn match_expr_like_matches_macro_priority() { + enum E { + A, + B, + C, + } + let x = E::A; + let _ans = match x { + E::A => false, + E::B => false, + _ => true, + }; +} + +fn main() { + let _ = match Some(0) { + Some(0) => 0, + Some(1) => 1, + #[cfg(feature = "foo")] + Some(2) => 2, + _ => 1, + }; + + enum Foo { + X(u32), + Y(u32), + Z(u32), + } + + // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::Z(_)` up. + let _ = match Foo::X(0) { + Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm + Foo::X(_) | Foo::Y(_) => 2, + _ => 0, + }; + + // Suggest moving `Foo::X(0)` down. + let _ = match Foo::X(0) { + Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + // Don't lint. + let _ = match 0 { + -2 => 1, + -5..=50 => 2, + -150..=88 => 1, + _ => 3, + }; + + struct Bar { + x: u32, + y: u32, + z: u32, + } + + // Lint. + let _ = match None { + Some(Bar { y: 10, z: 0, .. }) => 2, + None => 50, + Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 200, + }; + + let _ = match 0 { + 0 => todo!(), + 1 => todo!(), + 2 => core::convert::identity::(todo!()), + 3 => core::convert::identity::(todo!()), + _ => 5, + }; + + let _ = match 0 { + 1 | 0 => cfg!(not_enable), + _ => false, + }; +} + +// issue #8919, fixed on https://github.com/rust-lang/rust/pull/97312 +mod with_lifetime { + enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), + } + + impl<'a> MaybeStaticStr<'a> { + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + //~^ ERROR: this match arm has an identical body to another arm + } + } + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index dedd02e78733f..dfd15d10c3d34 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -7,8 +7,6 @@ clippy::match_like_matches_macro )] -//@no-rustfix: need to change the suggestion to a multipart suggestion - fn bar(_: T) {} fn foo() -> bool { unimplemented!() diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index 3a28b5afc2b8a..525a25e9287be 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,5 +1,5 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms2.rs:19:9 + --> tests/ui/match_same_arms2.rs:17:9 | LL | / 42 => { LL | | foo(); @@ -12,7 +12,7 @@ LL | | _ => { | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:28:9 + --> tests/ui/match_same_arms2.rs:26:9 | LL | / _ => { LL | | foo(); @@ -26,119 +26,103 @@ LL | | }, = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:42:9 + --> tests/ui/match_same_arms2.rs:40:9 | LL | 51 => foo(), | ^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | 51 | 42 => foo(), - | ~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - 42 => foo(), +LL - 51 => foo(), +LL + 51 | 42 => foo(), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:48:9 + --> tests/ui/match_same_arms2.rs:46:9 | LL | None => 24, | ^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | None | Some(_) => 24, - | ~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - Some(_) => 24, +LL - None => 24, +LL + None | Some(_) => 24, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:70:9 + --> tests/ui/match_same_arms2.rs:68:9 | LL | (None, Some(a)) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | (None, Some(a)) | (Some(a), None) => bar(a), - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - (Some(a), None) => bar(a), +LL - (None, Some(a)) => bar(a), +LL + (None, Some(a)) | (Some(a), None) => bar(a), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:84:9 + --> tests/ui/match_same_arms2.rs:82:9 | LL | (None, Some(a)) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | (None, Some(a)) | (Some(a), None) if a == 42 => a, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - (Some(a), None) if a == 42 => a, +LL - (None, Some(a)) if a == 42 => a, +LL + (None, Some(a)) | (Some(a), None) if a == 42 => a, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:89:9 + --> tests/ui/match_same_arms2.rs:87:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns +help: or try merging the arm patterns and removing the obsolete arm | -LL | (Some(a), ..) | (.., Some(a)) => bar(a), - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm - | -LL - (.., Some(a)) => bar(a), +LL ~ (Some(a), ..) | (.., Some(a)) => bar(a), +LL ~ _ => (), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:123:9 + --> tests/ui/match_same_arms2.rs:121:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | -LL - (Ok(_), Some(x)) => println!("ok {}", x), +LL ~ (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), +LL ~ _ => println!("err"), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:139:9 + --> tests/ui/match_same_arms2.rs:137:9 | LL | Ok(_) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | Ok(_) | Ok(3) => println!("ok"), - | ~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - Ok(3) => println!("ok"), +LL - Ok(_) => println!("ok"), +LL + Ok(_) | Ok(3) => println!("ok"), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:166:9 + --> tests/ui/match_same_arms2.rs:164:9 | LL | / 1 => { LL | | empty!(0); @@ -146,95 +130,82 @@ LL | | }, | |_________^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | 1 | 0 => { - | ~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - 0 => { LL - empty!(0); LL - }, +LL - 1 => { +LL + 1 | 0 => { | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:217:9 + --> tests/ui/match_same_arms2.rs:215:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | Foo::X(0) | Foo::Z(_) => 1, - | ~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | -LL - Foo::Z(_) => 1, +LL ~ Foo::X(0) | Foo::Z(_) => 1, +LL | Foo::X(_) | Foo::Y(_) => 2, +LL ~ _ => 0, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:227:9 + --> tests/ui/match_same_arms2.rs:225:9 | LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | Foo::Z(_) | Foo::X(0) => 1, - | ~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | -LL - Foo::X(0) => 1, +LL ~ Foo::Y(_) | Foo::Z(0) => 2, +LL ~ Foo::Z(_) | Foo::X(0) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:250:9 + --> tests/ui/match_same_arms2.rs:248:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns +help: or try merging the arm patterns and removing the obsolete arm | -LL | Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm - | -LL - Some(Bar { x: 0, y: 5, .. }) => 1, +LL ~ Some(Bar { y: 10, z: 0, .. }) => 2, +LL | None => 50, +LL ~ Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:264:9 + --> tests/ui/match_same_arms2.rs:262:9 | LL | 1 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | 1 | 0 => cfg!(not_enable), - | ~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - 0 => cfg!(not_enable), +LL - 1 => cfg!(not_enable), +LL + 1 | 0 => cfg!(not_enable), | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:280:17 + --> tests/ui/match_same_arms2.rs:278:17 | LL | MaybeStaticStr::Borrowed(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try changing either arm body -help: or try merging the arm patterns - | -LL | MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: and remove this obsolete arm +help: or try merging the arm patterns and removing the obsolete arm | LL - MaybeStaticStr::Static(s) => s, +LL - MaybeStaticStr::Borrowed(s) => s, +LL + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, | error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed index f255cb666528f..b92d9379c904b 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.fixed +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -23,3 +23,9 @@ fn main() { fn foo() {} ); } + +#[cfg_attr(all(), deprecated)] +fn issue_12320() {} + +#[cfg_attr(all(), deprecated, doc = "foo")] +fn issue_12320_2() {} diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs index 1305910ed0e5c..c77e728275048 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.rs +++ b/src/tools/clippy/tests/ui/must_use_unit.rs @@ -26,3 +26,9 @@ fn main() { fn foo() {} ); } + +#[cfg_attr(all(), must_use, deprecated)] +fn issue_12320() {} + +#[cfg_attr(all(), deprecated, doc = "foo", must_use)] +fn issue_12320_2() {} diff --git a/src/tools/clippy/tests/ui/must_use_unit.stderr b/src/tools/clippy/tests/ui/must_use_unit.stderr index c2ee2edda7dc5..b435568deeab8 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.stderr +++ b/src/tools/clippy/tests/ui/must_use_unit.stderr @@ -25,5 +25,21 @@ LL | #[must_use = "With note"] LL | pub fn must_use_with_note() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: this unit-returning function has a `#[must_use]` attribute + --> tests/ui/must_use_unit.rs:31:1 + | +LL | #[cfg_attr(all(), must_use, deprecated)] + | -------------------- help: change these attributes to: `deprecated` +LL | fn issue_12320() {} + | ^^^^^^^^^^^^^^^^ + +error: this unit-returning function has a `#[must_use]` attribute + --> tests/ui/must_use_unit.rs:34:1 + | +LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] + | --------------------------------- help: change these attributes to: `deprecated, doc = "foo"` +LL | fn issue_12320_2() {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index cfa4cf9da3c56..8196d608abd21 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -562,4 +562,18 @@ mod rayon { } } +mod issue13749 { + pub struct Generic(T); + // Non elidable lifetime + #[expect(clippy::extra_unused_lifetimes)] + impl<'a, T> Generic where T: 'a {} +} + +mod issue13749bis { + pub struct Generic(T); + // Non elidable lifetime + #[expect(clippy::extra_unused_lifetimes)] + impl<'a, T: 'a> Generic {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index 5e9d51164268b..b55dd99c46d05 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -562,4 +562,18 @@ mod rayon { } } +mod issue13749 { + pub struct Generic(T); + // Non elidable lifetime + #[expect(clippy::extra_unused_lifetimes)] + impl<'a, T> Generic where T: 'a {} +} + +mod issue13749bis { + pub struct Generic(T); + // Non elidable lifetime + #[expect(clippy::extra_unused_lifetimes)] + impl<'a, T: 'a> Generic {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index a936eb463f964..06c6169d0da0b 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -245,4 +245,57 @@ mod issue9084 { } } +fn a() -> Option<()> { + Some(()) +} +fn b() -> Option<()> { + Some(()) +} +fn c() -> Option<()> { + Some(()) +} + +#[allow(clippy::ifs_same_cond)] +pub fn issue13574() -> Option<()> { + // Do not lint. + // The right hand of all these arms are different functions. + let _ = { + if let Some(a) = a() { + Some(a) + } else if let Some(b) = b() { + Some(b) + } else if let Some(c) = c() { + Some(c) + } else { + None + } + }; + + const A: Option<()> = Some(()); + const B: Option<()> = Some(()); + const C: Option<()> = Some(()); + const D: Option<()> = Some(()); + + let _ = { + if let Some(num) = A { + Some(num) + } else if let Some(num) = B { + Some(num) + } else if let Some(num) = C { + Some(num) + } else if let Some(num) = D { + Some(num) + } else { + None + } + }; + + // Same const, should lint + let _ = { + A + }; + + None +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index b1dd6ff075d88..6b71de68e1be7 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -289,4 +289,65 @@ mod issue9084 { } } +fn a() -> Option<()> { + Some(()) +} +fn b() -> Option<()> { + Some(()) +} +fn c() -> Option<()> { + Some(()) +} + +#[allow(clippy::ifs_same_cond)] +pub fn issue13574() -> Option<()> { + // Do not lint. + // The right hand of all these arms are different functions. + let _ = { + if let Some(a) = a() { + Some(a) + } else if let Some(b) = b() { + Some(b) + } else if let Some(c) = c() { + Some(c) + } else { + None + } + }; + + const A: Option<()> = Some(()); + const B: Option<()> = Some(()); + const C: Option<()> = Some(()); + const D: Option<()> = Some(()); + + let _ = { + if let Some(num) = A { + Some(num) + } else if let Some(num) = B { + Some(num) + } else if let Some(num) = C { + Some(num) + } else if let Some(num) = D { + Some(num) + } else { + None + } + }; + + // Same const, should lint + let _ = { + if let Some(num) = A { + Some(num) + } else if let Some(num) = A { + Some(num) + } else if let Some(num) = A { + Some(num) + } else { + None + } + }; + + None +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr index 5bcab467aeae3..1410585cb2e3e 100644 --- a/src/tools/clippy/tests/ui/needless_match.stderr +++ b/src/tools/clippy/tests/ui/needless_match.stderr @@ -131,5 +131,17 @@ LL | | _ => e, LL | | }; | |_________^ help: replace it with: `e` -error: aborting due to 13 previous errors +error: this if-let expression is unnecessary + --> tests/ui/needless_match.rs:339:9 + | +LL | / if let Some(num) = A { +LL | | Some(num) +LL | | } else if let Some(num) = A { +LL | | Some(num) +... | +LL | | None +LL | | } + | |_________^ help: replace it with: `A` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/needless_option_take.fixed b/src/tools/clippy/tests/ui/needless_option_take.fixed deleted file mode 100644 index d732a2686cb9d..0000000000000 --- a/src/tools/clippy/tests/ui/needless_option_take.fixed +++ /dev/null @@ -1,13 +0,0 @@ -fn main() { - println!("Testing non erroneous option_take_on_temporary"); - let mut option = Some(1); - let _ = Box::new(move || option.take().unwrap()); - - println!("Testing non erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); - - println!("Testing erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); -} diff --git a/src/tools/clippy/tests/ui/needless_option_take.rs b/src/tools/clippy/tests/ui/needless_option_take.rs index f947d874e064b..c6807718a758e 100644 --- a/src/tools/clippy/tests/ui/needless_option_take.rs +++ b/src/tools/clippy/tests/ui/needless_option_take.rs @@ -1,3 +1,15 @@ +struct MyStruct; + +impl MyStruct { + pub fn get_option() -> Option { + todo!() + } +} + +fn return_option() -> Option { + todo!() +} + fn main() { println!("Testing non erroneous option_take_on_temporary"); let mut option = Some(1); @@ -7,7 +19,40 @@ fn main() { let x = Some(3); x.as_ref(); - println!("Testing erroneous option_take_on_temporary"); let x = Some(3); x.as_ref().take(); + //~^ ERROR: called `Option::take()` on a temporary value + + println!("Testing non erroneous option_take_on_temporary"); + let mut x = Some(3); + let y = x.as_mut(); + + let mut x = Some(3); + let y = x.as_mut().take(); + //~^ ERROR: called `Option::take()` on a temporary value + let y = x.replace(289).take(); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = Some(3).as_mut().take(); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = Option::as_mut(&mut x).take(); + //~^ ERROR: called `Option::take()` on a temporary value + + let x = return_option(); + let x = return_option().take(); + //~^ ERROR: called `Option::take()` on a temporary value + + let x = MyStruct::get_option(); + let x = MyStruct::get_option().take(); + //~^ ERROR: called `Option::take()` on a temporary value + + let mut my_vec = vec![1, 2, 3]; + my_vec.push(4); + let y = my_vec.first(); + let y = my_vec.first().take(); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = my_vec.first().take(); + //~^ ERROR: called `Option::take()` on a temporary value } diff --git a/src/tools/clippy/tests/ui/needless_option_take.stderr b/src/tools/clippy/tests/ui/needless_option_take.stderr index 4a73ccb86d081..e036bd53170ab 100644 --- a/src/tools/clippy/tests/ui/needless_option_take.stderr +++ b/src/tools/clippy/tests/ui/needless_option_take.stderr @@ -1,11 +1,76 @@ error: called `Option::take()` on a temporary value - --> tests/ui/needless_option_take.rs:12:5 + --> tests/ui/needless_option_take.rs:23:5 | LL | x.as_ref().take(); - | ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()` + | ^^^^^^^^^^^^^^^^^ | + = note: `as_ref` creates a temporary value, so calling take() has no effect = note: `-D clippy::needless-option-take` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_option_take)]` -error: aborting due to 1 previous error +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:31:13 + | +LL | let y = x.as_mut().take(); + | ^^^^^^^^^^^^^^^^^ + | + = note: `as_mut` creates a temporary value, so calling take() has no effect + +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:33:13 + | +LL | let y = x.replace(289).take(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `replace` creates a temporary value, so calling take() has no effect + +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:36:13 + | +LL | let y = Some(3).as_mut().take(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `as_mut` creates a temporary value, so calling take() has no effect + +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:39:13 + | +LL | let y = Option::as_mut(&mut x).take(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `as_mut` creates a temporary value, so calling take() has no effect + +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:43:13 + | +LL | let x = return_option().take(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `return_option` creates a temporary value, so calling take() has no effect + +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:47:13 + | +LL | let x = MyStruct::get_option().take(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `get_option` creates a temporary value, so calling take() has no effect + +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:53:13 + | +LL | let y = my_vec.first().take(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `first` creates a temporary value, so calling take() has no effect + +error: called `Option::take()` on a temporary value + --> tests/ui/needless_option_take.rs:56:13 + | +LL | let y = my_vec.first().take(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `first` creates a temporary value, so calling take() has no effect + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed index c25c2062aceba..9864dd2550b62 100644 --- a/src/tools/clippy/tests/ui/precedence.fixed +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -20,6 +20,10 @@ fn main() { 1 ^ (1 - 1); 3 | (2 - 1); 3 & (5 - 2); + 0x0F00 & (0x00F0 << 4); + 0x0F00 & (0xF000 >> 4); + (0x0F00 << 1) ^ 3; + (0x0F00 << 1) | 2; let b = 3; trip!(b * 8); diff --git a/src/tools/clippy/tests/ui/precedence.rs b/src/tools/clippy/tests/ui/precedence.rs index dc242ecf4c72e..9ef5c43833f88 100644 --- a/src/tools/clippy/tests/ui/precedence.rs +++ b/src/tools/clippy/tests/ui/precedence.rs @@ -20,6 +20,10 @@ fn main() { 1 ^ 1 - 1; 3 | 2 - 1; 3 & 5 - 2; + 0x0F00 & 0x00F0 << 4; + 0x0F00 & 0xF000 >> 4; + 0x0F00 << 1 ^ 3; + 0x0F00 << 1 | 2; let b = 3; trip!(b * 8); diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index 8057c25a5e499..0d63e827d66ea 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -43,5 +43,29 @@ error: operator precedence can trip the unwary LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: aborting due to 7 previous errors +error: operator precedence can trip the unwary + --> tests/ui/precedence.rs:23:5 + | +LL | 0x0F00 & 0x00F0 << 4; + | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)` + +error: operator precedence can trip the unwary + --> tests/ui/precedence.rs:24:5 + | +LL | 0x0F00 & 0xF000 >> 4; + | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)` + +error: operator precedence can trip the unwary + --> tests/ui/precedence.rs:25:5 + | +LL | 0x0F00 << 1 ^ 3; + | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3` + +error: operator precedence can trip the unwary + --> tests/ui/precedence.rs:26:5 + | +LL | 0x0F00 << 1 | 2; + | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed index a7157c07f8a98..1705a7ff01bb4 100644 --- a/src/tools/clippy/tests/ui/print_literal.fixed +++ b/src/tools/clippy/tests/ui/print_literal.fixed @@ -1,5 +1,5 @@ #![warn(clippy::print_literal)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_args)] fn main() { // these should be fine diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs index 4b04b42744ccd..d10b26b5887cc 100644 --- a/src/tools/clippy/tests/ui/print_literal.rs +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -1,5 +1,5 @@ #![warn(clippy::print_literal)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_args)] fn main() { // these should be fine diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 47149622ef7c6..47d6e119543d1 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -15,6 +15,7 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(unpredictable_function_pointer_comparisons)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 7a78a5d280ddb..12c7db69be2e4 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -15,6 +15,7 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(unpredictable_function_pointer_comparisons)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index dc24bc16d0ea4..1ec45c4f1f7ba 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:64:9 + --> tests/ui/rename.rs:65:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,397 +8,397 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:66:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` diff --git a/src/tools/clippy/tests/ui/repr_packed_without_abi.rs b/src/tools/clippy/tests/ui/repr_packed_without_abi.rs new file mode 100644 index 0000000000000..16b5ededee990 --- /dev/null +++ b/src/tools/clippy/tests/ui/repr_packed_without_abi.rs @@ -0,0 +1,37 @@ +#![deny(clippy::repr_packed_without_abi)] + +#[repr(packed)] +struct NetworkPacketHeader { + header_length: u8, + header_version: u16, +} + +#[repr(packed)] +union Foo { + a: u8, + b: u16, +} + +#[repr(C, packed)] +struct NoLintCNetworkPacketHeader { + header_length: u8, + header_version: u16, +} + +#[repr(Rust, packed)] +struct NoLintRustNetworkPacketHeader { + header_length: u8, + header_version: u16, +} + +#[repr(packed, C)] +union NotLintCFoo { + a: u8, + b: u16, +} + +#[repr(packed, Rust)] +union NotLintRustFoo { + a: u8, + b: u16, +} diff --git a/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr b/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr new file mode 100644 index 0000000000000..4f7acd00db3d8 --- /dev/null +++ b/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr @@ -0,0 +1,35 @@ +error: item uses `packed` representation without ABI-qualification + --> tests/ui/repr_packed_without_abi.rs:4:1 + | +LL | #[repr(packed)] + | ------ `packed` representation set here +LL | / struct NetworkPacketHeader { +LL | | header_length: u8, +LL | | header_version: u16, +LL | | } + | |_^ + | + = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI + = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` +note: the lint level is defined here + --> tests/ui/repr_packed_without_abi.rs:1:9 + | +LL | #![deny(clippy::repr_packed_without_abi)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: item uses `packed` representation without ABI-qualification + --> tests/ui/repr_packed_without_abi.rs:10:1 + | +LL | #[repr(packed)] + | ------ `packed` representation set here +LL | / union Foo { +LL | | a: u8, +LL | | b: u16, +LL | | } + | |_^ + | + = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI + = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/result_unit_error_no_std.rs b/src/tools/clippy/tests/ui/result_unit_error_no_std.rs new file mode 100644 index 0000000000000..1e7a028a7fc02 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_unit_error_no_std.rs @@ -0,0 +1,26 @@ +#![feature(lang_items, start, libc)] +#![no_std] +#![warn(clippy::result_unit_err)] + +#[clippy::msrv = "1.80"] +pub fn returns_unit_error_no_lint() -> Result { + Err(()) +} + +#[clippy::msrv = "1.81"] +pub fn returns_unit_error_lint() -> Result { + Err(()) +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/result_unit_error_no_std.stderr b/src/tools/clippy/tests/ui/result_unit_error_no_std.stderr new file mode 100644 index 0000000000000..33692e6055438 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_unit_error_no_std.stderr @@ -0,0 +1,12 @@ +error: this returns a `Result<_, ()>` + --> tests/ui/result_unit_error_no_std.rs:11:1 + | +LL | pub fn returns_unit_error_lint() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom `Error` type instead + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::result_unit_err)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs index 258dba9dd831e..31944f5ef1b1d 100644 --- a/src/tools/clippy/tests/ui/shadow.rs +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -119,4 +119,26 @@ fn ice_8748() { }]; } +// https://github.com/rust-lang/rust-clippy/issues/10780 +fn shadow_closure() { + // These are not shadow_unrelated; but they are correctly shadow_reuse + let x = Some(1); + #[allow(clippy::shadow_reuse)] + let y = x.map(|x| x + 1); + let z = x.map(|x| x + 1); + let a: Vec> = [100u8, 120, 140] + .iter() + .map(|i| i.checked_mul(2)) + .map(|i| i.map(|i| i - 10)) + .collect(); +} + +struct Issue13795 { + value: i32, +} + +fn issue13795(value: Issue13795) { + let Issue13795 { value, .. } = value; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr index fdd149a2216f2..c8c524b3a2f58 100644 --- a/src/tools/clippy/tests/ui/shadow.stderr +++ b/src/tools/clippy/tests/ui/shadow.stderr @@ -280,5 +280,41 @@ note: previous binding is here LL | let x = 1; | ^ -error: aborting due to 23 previous errors +error: `x` is shadowed + --> tests/ui/shadow.rs:128:20 + | +LL | let z = x.map(|x| x + 1); + | ^ + | +note: previous binding is here + --> tests/ui/shadow.rs:125:9 + | +LL | let x = Some(1); + | ^ + +error: `i` is shadowed + --> tests/ui/shadow.rs:132:25 + | +LL | .map(|i| i.map(|i| i - 10)) + | ^ + | +note: previous binding is here + --> tests/ui/shadow.rs:132:15 + | +LL | .map(|i| i.map(|i| i - 10)) + | ^ + +error: `value` is shadowed by itself in `value` + --> tests/ui/shadow.rs:141:22 + | +LL | let Issue13795 { value, .. } = value; + | ^^^^^ + | +note: previous binding is here + --> tests/ui/shadow.rs:140:15 + | +LL | fn issue13795(value: Issue13795) { + | ^^^^^ + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.fixed b/src/tools/clippy/tests/ui/significant_drop_tightening.fixed new file mode 100644 index 0000000000000..ed05f6e0c8d35 --- /dev/null +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.fixed @@ -0,0 +1,144 @@ +#![warn(clippy::significant_drop_tightening)] + +use std::sync::Mutex; + +pub fn complex_return_triggers_the_lint() -> i32 { + fn foo() -> i32 { + 1 + } + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let _ = *lock; + let _ = *lock; + drop(lock); + foo() +} + +pub fn issue_10413() { + let mutex = Mutex::new(Some(1)); + let opt = Some(1); + if opt.is_some() { + let lock = mutex.lock().unwrap(); + let _ = *lock; + if opt.is_some() { + let _ = *lock; + } + } +} + +pub fn issue_11128() { + use std::mem::drop as unlock; + + struct Foo { + droppable: Option>, + mutex: Mutex>, + } + + impl Drop for Foo { + fn drop(&mut self) { + if let Some(droppable) = self.droppable.take() { + let lock = self.mutex.lock().unwrap(); + let idx_opt = lock.iter().copied().find(|el| Some(el) == droppable.first()); + if let Some(idx) = idx_opt { + let local_droppable = vec![lock.first().copied().unwrap_or_default()]; + unlock(lock); + drop(local_droppable); + } + } + } + } +} + +pub fn issue_11160() -> bool { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + true +} + +pub fn issue_11189() { + struct Number { + pub value: u32, + } + + fn do_something() -> Result<(), ()> { + let number = Mutex::new(Number { value: 1 }); + let number2 = Mutex::new(Number { value: 2 }); + let number3 = Mutex::new(Number { value: 3 }); + let mut lock = number.lock().unwrap(); + let mut lock2 = number2.lock().unwrap(); + let mut lock3 = number3.lock().unwrap(); + lock.value += 1; + lock2.value += 1; + lock3.value += 1; + drop((lock, lock2, lock3)); + Ok(()) + } +} + +pub fn path_return_can_be_ignored() -> i32 { + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let rslt = *lock; + let _ = *lock; + rslt +} + +pub fn post_bindings_can_be_ignored() { + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let rslt = *lock; + let another = rslt; + let _ = another; +} + +pub fn unnecessary_contention_with_multiple_owned_results() { + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + let _ = lock.is_positive(); + } + + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let rslt0 = lock.abs(); + let rslt1 = lock.is_positive(); + drop(lock); + do_heavy_computation_that_takes_time((rslt0, rslt1)); + } +} + +pub fn unnecessary_contention_with_single_owned_results() { + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + } + { + let mutex = Mutex::new(vec![1i32]); + let mut lock = mutex.lock().unwrap(); + lock.clear(); + } + + { + let mutex = Mutex::new(1i32); + + let rslt0 = mutex.lock().unwrap().abs(); + + do_heavy_computation_that_takes_time(rslt0); + } + { + let mutex = Mutex::new(vec![1i32]); + + mutex.lock().unwrap().clear(); + + do_heavy_computation_that_takes_time(()); + } +} + +// Marker used for illustration purposes. +pub fn do_heavy_computation_that_takes_time(_: T) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.rs b/src/tools/clippy/tests/ui/significant_drop_tightening.rs index 77538167548ec..e5f17278f0f6a 100644 --- a/src/tools/clippy/tests/ui/significant_drop_tightening.rs +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.rs @@ -1,7 +1,5 @@ #![warn(clippy::significant_drop_tightening)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - use std::sync::Mutex; pub fn complex_return_triggers_the_lint() -> i32 { diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.stderr b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr index 7d7e3ac7d0ae2..aef774a3d3600 100644 --- a/src/tools/clippy/tests/ui/significant_drop_tightening.stderr +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr @@ -1,5 +1,5 @@ error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:12:9 + --> tests/ui/significant_drop_tightening.rs:10:9 | LL | pub fn complex_return_triggers_the_lint() -> i32 { | __________________________________________________- @@ -23,7 +23,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:106:13 + --> tests/ui/significant_drop_tightening.rs:104:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -43,7 +43,7 @@ LL + drop(lock); | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:127:13 + --> tests/ui/significant_drop_tightening.rs:125:13 | LL | / { LL | | let mutex = Mutex::new(1i32); @@ -59,14 +59,11 @@ help: merge the temporary construction with its single usage | LL ~ LL + let rslt0 = mutex.lock().unwrap().abs(); - | -help: remove separated single usage - | -LL - let rslt0 = lock.abs(); +LL ~ | error: temporary with significant `Drop` can be early dropped - --> tests/ui/significant_drop_tightening.rs:133:17 + --> tests/ui/significant_drop_tightening.rs:131:17 | LL | / { LL | | let mutex = Mutex::new(vec![1i32]); @@ -82,10 +79,7 @@ help: merge the temporary construction with its single usage | LL ~ LL + mutex.lock().unwrap().clear(); - | -help: remove separated single usage - | -LL - lock.clear(); +LL ~ | error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed index 4016b2699d6b4..d3d5fd8b35c87 100644 --- a/src/tools/clippy/tests/ui/single_match.fixed +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -17,7 +17,13 @@ fn single_match() { }; let x = Some(1u8); - if let Some(y) = x { println!("{:?}", y) } + match x { + // Note the missing block braces. + // We suggest `if let Some(y) = x { .. }` because the macro + // is expanded before we can do anything. + Some(y) => println!("{:?}", y), + _ => (), + } let z = (1u8, 1u8); if let (2..=3, 7..=9) = z { dummy() }; @@ -297,6 +303,10 @@ fn issue11365() { if let Some(A | B) = &Some(A) { println!() } } +fn issue12758(s: &[u8]) { + if &s[0..3] == b"foo" { println!() } +} + #[derive(Eq, PartialEq)] pub struct Data([u8; 4]); @@ -318,5 +328,25 @@ fn irrefutable_match() { - println!() + println!(); + + let mut x = vec![1i8]; + + // Should not lint. + match x.pop() { + // bla + Some(u) => println!("{u}"), + // more comments! + None => {}, + } + // Should not lint. + match x.pop() { + // bla + Some(u) => { + // bla + println!("{u}"); + }, + // bla + None => {}, + } } diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index 75edaa606053d..2f3547c50639d 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -361,6 +361,13 @@ fn issue11365() { } } +fn issue12758(s: &[u8]) { + match &s[0..3] { + b"foo" => println!(), + _ => {}, + } +} + #[derive(Eq, PartialEq)] pub struct Data([u8; 4]); @@ -401,4 +408,24 @@ fn irrefutable_match() { CONST_I32 => println!(), _ => {}, } + + let mut x = vec![1i8]; + + // Should not lint. + match x.pop() { + // bla + Some(u) => println!("{u}"), + // more comments! + None => {}, + } + // Should not lint. + match x.pop() { + // bla + Some(u) => { + // bla + println!("{u}"); + }, + // bla + None => {}, + } } diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr index dd03737279ad1..54bbfbac093c2 100644 --- a/src/tools/clippy/tests/ui/single_match.stderr +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -18,15 +18,6 @@ LL + println!("{:?}", y); LL ~ }; | -error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:23:5 - | -LL | / match x { -... | -LL | | _ => (), -LL | | } - | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` - error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> tests/ui/single_match.rs:32:5 | @@ -213,8 +204,17 @@ LL | | None | Some(_) => {}, LL | | } | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match.rs:365:5 + | +LL | / match &s[0..3] { +LL | | b"foo" => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` + error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:371:5 + --> tests/ui/single_match.rs:378:5 | LL | / match DATA { LL | | DATA => println!(), @@ -223,7 +223,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:376:5 + --> tests/ui/single_match.rs:383:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -232,7 +232,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:382:5 + --> tests/ui/single_match.rs:389:5 | LL | / match i { LL | | i => { @@ -252,7 +252,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:390:5 + --> tests/ui/single_match.rs:397:5 | LL | / match i { LL | | i => {}, @@ -261,7 +261,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:395:5 + --> tests/ui/single_match.rs:402:5 | LL | / match i { LL | | i => (), @@ -270,13 +270,13 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:400:5 + --> tests/ui/single_match.rs:407:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), LL | | _ => {}, LL | | } - | |_____^ help: try: `println!()` + | |_____^ help: try: `println!();` error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs index 3d06c2621681d..309a5920dfdeb 100644 --- a/src/tools/clippy/tests/ui/trailing_empty_array.rs +++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs @@ -1,4 +1,5 @@ #![warn(clippy::trailing_empty_array)] +#![allow(clippy::repr_packed_without_abi)] // Do lint: diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.stderr b/src/tools/clippy/tests/ui/trailing_empty_array.stderr index 756381478f2b7..7ebff372cf759 100644 --- a/src/tools/clippy/tests/ui/trailing_empty_array.stderr +++ b/src/tools/clippy/tests/ui/trailing_empty_array.stderr @@ -1,5 +1,5 @@ error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:5:1 + --> tests/ui/trailing_empty_array.rs:6:1 | LL | / struct RarelyUseful { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::trailing_empty_array)]` error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:11:1 + --> tests/ui/trailing_empty_array.rs:12:1 | LL | / struct OnlyField { LL | | @@ -24,7 +24,7 @@ LL | | } = help: consider annotating `OnlyField` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:16:1 + --> tests/ui/trailing_empty_array.rs:17:1 | LL | / struct GenericArrayType { LL | | @@ -36,7 +36,7 @@ LL | | } = help: consider annotating `GenericArrayType` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:23:1 + --> tests/ui/trailing_empty_array.rs:24:1 | LL | / struct OnlyAnotherAttribute { LL | | @@ -48,7 +48,7 @@ LL | | } = help: consider annotating `OnlyAnotherAttribute` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:30:1 + --> tests/ui/trailing_empty_array.rs:31:1 | LL | / struct OnlyADeriveAttribute { LL | | @@ -60,7 +60,7 @@ LL | | } = help: consider annotating `OnlyADeriveAttribute` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:37:1 + --> tests/ui/trailing_empty_array.rs:38:1 | LL | / struct ZeroSizedWithConst { LL | | @@ -72,7 +72,7 @@ LL | | } = help: consider annotating `ZeroSizedWithConst` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:47:1 + --> tests/ui/trailing_empty_array.rs:48:1 | LL | / struct ZeroSizedWithConstFunction { LL | | @@ -84,7 +84,7 @@ LL | | } = help: consider annotating `ZeroSizedWithConstFunction` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:56:1 + --> tests/ui/trailing_empty_array.rs:57:1 | LL | / struct ZeroSizedWithConstFunction2 { LL | | @@ -96,7 +96,7 @@ LL | | } = help: consider annotating `ZeroSizedWithConstFunction2` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:62:1 + --> tests/ui/trailing_empty_array.rs:63:1 | LL | struct ZeroSizedArrayWrapper([usize; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,7 @@ LL | struct ZeroSizedArrayWrapper([usize; 0]); = help: consider annotating `ZeroSizedArrayWrapper` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:65:1 + --> tests/ui/trailing_empty_array.rs:66:1 | LL | struct TupleStruct(i32, [usize; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +112,7 @@ LL | struct TupleStruct(i32, [usize; 0]); = help: consider annotating `TupleStruct` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> tests/ui/trailing_empty_array.rs:68:1 + --> tests/ui/trailing_empty_array.rs:69:1 | LL | / struct LotsOfFields { LL | | diff --git a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.fixed b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.fixed index f0d570efdcee7..9911d1317070a 100644 --- a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.fixed +++ b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -3,6 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] +#![allow(clippy::literal_string_with_formatting_args)] fn main() { let var = 1; diff --git a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.stderr b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.stderr index 0541dd9a7d7bc..4b154abac5bc0 100644 --- a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.stderr +++ b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:10:5 + --> tests/ui/uninlined_format_args_panic.rs:11:5 | LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.fixed b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.fixed index 7c0f28c457642..87b74670565f4 100644 --- a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.fixed +++ b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -3,6 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] +#![allow(clippy::literal_string_with_formatting_args)] fn main() { let var = 1; diff --git a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.stderr b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.stderr index 3615eaa9dee37..7638d3f8bbadc 100644 --- a/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.stderr +++ b/src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:10:5 + --> tests/ui/uninlined_format_args_panic.rs:11:5 | LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + println!("val='{var}'"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:13:9 + --> tests/ui/uninlined_format_args_panic.rs:14:9 | LL | panic!("p1 {}", var); | ^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + panic!("p1 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:16:9 + --> tests/ui/uninlined_format_args_panic.rs:17:9 | LL | panic!("p2 {0}", var); | ^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + panic!("p2 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:19:9 + --> tests/ui/uninlined_format_args_panic.rs:20:9 | LL | panic!("p3 {var}", var = var); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + panic!("p3 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:29:5 + --> tests/ui/uninlined_format_args_panic.rs:30:5 | LL | assert!(var == 1, "p5 {}", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + assert!(var == 1, "p5 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:30:5 + --> tests/ui/uninlined_format_args_panic.rs:31:5 | LL | debug_assert!(var == 1, "p6 {}", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/uninlined_format_args_panic.rs b/src/tools/clippy/tests/ui/uninlined_format_args_panic.rs index fa594d9a96f0f..647c69bc5c410 100644 --- a/src/tools/clippy/tests/ui/uninlined_format_args_panic.rs +++ b/src/tools/clippy/tests/ui/uninlined_format_args_panic.rs @@ -3,6 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] +#![allow(clippy::literal_string_with_formatting_args)] fn main() { let var = 1; diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed new file mode 100644 index 0000000000000..dc5e163ff04e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed @@ -0,0 +1,201 @@ +#![allow(unused_assignments)] +#![warn(clippy::unnecessary_to_owned)] + +#[allow(dead_code)] +#[derive(Clone, Copy)] +enum FileType { + Account, + PrivateKey, + Certificate, +} + +fn main() { + let path = std::path::Path::new("x"); + + let _ = check_files(&[(FileType::Account, path)]); + let _ = check_files_vec(vec![(FileType::Account, path)]); + + // negative tests + let _ = check_files_ref(&[(FileType::Account, path)]); + let _ = check_files_mut(&[(FileType::Account, path)]); + let _ = check_files_ref_mut(&[(FileType::Account, path)]); + let _ = check_files_self_and_arg(&[(FileType::Account, path)]); + let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); + + check_mut_iteratee_and_modify_inner_variable(); +} + +// `check_files` and its variants are based on: +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 +fn check_files(files: &[(FileType, &std::path::Path)]) -> bool { + for (t, path) in files { + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool { + for (t, path) in files.iter() { + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool { + for (ref t, path) in files.iter().copied() { + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +#[allow(unused_assignments)] +fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool { + for (mut t, path) in files.iter().copied() { + t = FileType::PrivateKey; + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool { + for (ref mut t, path) in files.iter().copied() { + *t = FileType::PrivateKey; + let other = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool { + for (t, path) in files.iter().copied() { + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.join(path).is_file() || !other.is_file() { + return false; + } + } + true +} + +#[allow(unused_assignments)] +fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { + for (mut t, path) in files.iter().cloned() { + t = FileType::PrivateKey; + let other = match get_file_path(&t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() || !other.is_file() { + return false; + } + } + true +} + +fn get_file_path(_file_type: &FileType) -> Result { + Ok(std::path::PathBuf::new()) +} + +// Issue 12098 +// https://github.com/rust-lang/rust-clippy/issues/12098 +// no message emits +fn check_mut_iteratee_and_modify_inner_variable() { + struct Test { + list: Vec, + mut_this: bool, + } + + impl Test { + fn list(&self) -> &[String] { + &self.list + } + } + + let mut test = Test { + list: vec![String::from("foo"), String::from("bar")], + mut_this: false, + }; + + for _item in test.list().to_vec() { + println!("{}", _item); + + test.mut_this = true; + { + test.mut_this = true; + } + } +} + +mod issue_12821 { + fn foo() { + let v: Vec<_> = "hello".chars().collect(); + for c in v.iter() { + //~^ ERROR: unnecessary use of `cloned` + println!("{c}"); // should not suggest to remove `&` + } + } + + fn bar() { + let v: Vec<_> = "hello".chars().collect(); + for c in v.iter() { + //~^ ERROR: unnecessary use of `cloned` + let ref_c = c; //~ HELP: remove any references to the binding + println!("{ref_c}"); + } + } + + fn baz() { + let v: Vec<_> = "hello".chars().enumerate().collect(); + for (i, c) in v.iter() { + //~^ ERROR: unnecessary use of `cloned` + let ref_c = c; //~ HELP: remove any references to the binding + let ref_i = i; + println!("{i} {ref_c}"); // should not suggest to remove `&` from `i` + } + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs index 331b7b25271ba..8f797ac717fb6 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs @@ -1,8 +1,6 @@ #![allow(unused_assignments)] #![warn(clippy::unnecessary_to_owned)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - #[allow(dead_code)] #[derive(Clone, Copy)] enum FileType { diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr index e3592e3cbbd82..6f2ae0ab1f35a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr @@ -1,71 +1,58 @@ error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:33:22 + --> tests/ui/unnecessary_iter_cloned.rs:31:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` -help: use - | -LL | for (t, path) in files { - | ~~~~~ help: remove any references to the binding | -LL - let other = match get_file_path(&t) { -LL + let other = match get_file_path(t) { +LL ~ for (t, path) in files { +LL ~ let other = match get_file_path(t) { | error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:48:22 + --> tests/ui/unnecessary_iter_cloned.rs:46:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ | -help: use - | -LL | for (t, path) in files.iter() { - | ~~~~~~~~~~~~ help: remove any references to the binding | -LL - let other = match get_file_path(&t) { -LL + let other = match get_file_path(t) { +LL ~ for (t, path) in files.iter() { +LL ~ let other = match get_file_path(t) { | error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:179:18 + --> tests/ui/unnecessary_iter_cloned.rs:177:18 | LL | for c in v.iter().cloned() { - | ^^^^^^^^^^^^^^^^^ help: use: `v.iter()` + | ^^^^^^^^^^^^^^^^^ help: remove any references to the binding: `v.iter()` error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:187:18 + --> tests/ui/unnecessary_iter_cloned.rs:185:18 | LL | for c in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ | -help: use - | -LL | for c in v.iter() { - | ~~~~~~~~ help: remove any references to the binding | -LL - let ref_c = &c; -LL + let ref_c = c; +LL ~ for c in v.iter() { +LL | +LL ~ let ref_c = c; | error: unnecessary use of `cloned` - --> tests/ui/unnecessary_iter_cloned.rs:196:23 + --> tests/ui/unnecessary_iter_cloned.rs:194:23 | LL | for (i, c) in v.iter().cloned() { | ^^^^^^^^^^^^^^^^^ | -help: use - | -LL | for (i, c) in v.iter() { - | ~~~~~~~~ help: remove any references to the binding | +LL ~ for (i, c) in v.iter() { +LL | LL ~ let ref_c = c; LL ~ let ref_i = i; | diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed new file mode 100644 index 0000000000000..c7be000b820cf --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed @@ -0,0 +1,20 @@ +#![no_std] +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_11524() -> Vec { + let mut vec = vec![1, 2, 3]; + + // Should lint and suggest `vec.sort_by_key(|a| a + 1);` + vec.sort_by_key(|a| a + 1); + vec +} + +fn issue_11524_2() -> Vec { + let mut vec = vec![1, 2, 3]; + + // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` + vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); + vec +} diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs new file mode 100644 index 0000000000000..5f44be97c61f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs @@ -0,0 +1,20 @@ +#![no_std] +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_11524() -> Vec { + let mut vec = vec![1, 2, 3]; + + // Should lint and suggest `vec.sort_by_key(|a| a + 1);` + vec.sort_by(|a, b| (a + 1).cmp(&(b + 1))); + vec +} + +fn issue_11524_2() -> Vec { + let mut vec = vec![1, 2, 3]; + + // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` + vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); + vec +} diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr new file mode 100644 index 0000000000000..a57fbc7a6328c --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr @@ -0,0 +1,17 @@ +error: consider using `sort_by_key` + --> tests/ui/unnecessary_sort_by_no_std.rs:10:5 + | +LL | vec.sort_by(|a, b| (a + 1).cmp(&(b + 1))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| a + 1)` + | + = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_sort_by)]` + +error: consider using `sort_by_key` + --> tests/ui/unnecessary_sort_by_no_std.rs:18:5 + | +LL | vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| core::cmp::Reverse(b + 1))` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed new file mode 100644 index 0000000000000..fdcac8fb08dcf --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -0,0 +1,587 @@ +#![allow( + clippy::needless_borrow, + clippy::needless_borrows_for_generic_args, + clippy::ptr_arg, + clippy::manual_async_fn, + clippy::needless_lifetimes +)] +#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] + +use std::borrow::Cow; +use std::ffi::{CStr, CString, OsStr, OsString}; +use std::ops::Deref; + +#[derive(Clone)] +struct X(String); + +impl Deref for X { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl AsRef for X { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} + +#[allow(clippy::to_string_trait_impl)] +impl ToString for X { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl X { + fn join(&self, other: impl AsRef) -> Self { + let mut s = self.0.clone(); + s.push_str(other.as_ref()); + Self(s) + } +} + +#[allow(dead_code)] +#[derive(Clone)] +enum FileType { + Account, + PrivateKey, + Certificate, +} + +fn main() { + let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); + let os_str = OsStr::new("x"); + let path = std::path::Path::new("x"); + let s = "x"; + let array = ["x"]; + let array_ref = &["x"]; + let slice = &["x"][..]; + let x = X(String::from("x")); + let x_ref = &x; + + require_c_str(&Cow::from(c_str)); + require_c_str(c_str); + + require_os_str(os_str); + require_os_str(&Cow::from(os_str)); + require_os_str(os_str); + + require_path(path); + require_path(&Cow::from(path)); + require_path(path); + + require_str(s); + require_str(&Cow::from(s)); + require_str(s); + require_str(x_ref.as_ref()); + + require_slice(slice); + require_slice(&Cow::from(slice)); + require_slice(array.as_ref()); + require_slice(array_ref.as_ref()); + require_slice(slice); + require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. + + require_x(&Cow::::Owned(x.clone())); + require_x(&x_ref.to_owned()); // No longer flagged because of #8759. + + require_deref_c_str(c_str); + require_deref_os_str(os_str); + require_deref_path(path); + require_deref_str(s); + require_deref_slice(slice); + + require_impl_deref_c_str(c_str); + require_impl_deref_os_str(os_str); + require_impl_deref_path(path); + require_impl_deref_str(s); + require_impl_deref_slice(slice); + + require_deref_str_slice(s, slice); + require_deref_slice_str(slice, s); + + require_as_ref_c_str(c_str); + require_as_ref_os_str(os_str); + require_as_ref_path(path); + require_as_ref_str(s); + require_as_ref_str(&x); + require_as_ref_slice(array); + require_as_ref_slice(array_ref); + require_as_ref_slice(slice); + + require_impl_as_ref_c_str(c_str); + require_impl_as_ref_os_str(os_str); + require_impl_as_ref_path(path); + require_impl_as_ref_str(s); + require_impl_as_ref_str(&x); + require_impl_as_ref_slice(array); + require_impl_as_ref_slice(array_ref); + require_impl_as_ref_slice(slice); + + require_as_ref_str_slice(s, array); + require_as_ref_str_slice(s, array_ref); + require_as_ref_str_slice(s, slice); + require_as_ref_slice_str(array, s); + require_as_ref_slice_str(array_ref, s); + require_as_ref_slice_str(slice, s); + + let _ = x.join(x_ref); + + let _ = slice.iter().copied(); + let _ = slice.iter().copied(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + + let _ = slice.iter().copied(); + let _ = slice.iter().copied(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + let _ = [std::path::PathBuf::new()][..].iter().cloned(); + + let _ = check_files(&[FileType::Account]); + + // negative tests + require_string(&s.to_string()); + require_string(&Cow::from(s).into_owned()); + require_string(&s.to_owned()); + require_string(&x_ref.to_string()); + + // `X` isn't copy. + require_slice(&x.to_owned()); + require_deref_slice(x.to_owned()); + + // The following should be flagged by `redundant_clone`, but not by this lint. + require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap()); + require_os_str(&OsString::from("x")); + require_path(&std::path::PathBuf::from("x")); + require_str(&String::from("x")); + require_slice(&[String::from("x")]); + + let slice = [0u8; 1024]; + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); + // Expression is of type `&String`, can't suggest `str::from_utf8` here + let _ref_string = &String::from_utf8(b"foo".to_vec()).unwrap(); + macro_rules! arg_from_macro { + () => { + b"foo".to_vec() + }; + } + macro_rules! string_from_utf8_from_macro { + () => { + &String::from_utf8(b"foo".to_vec()).unwrap() + }; + } + let _ref_str: &str = &String::from_utf8(arg_from_macro!()).unwrap(); + let _ref_str: &str = string_from_utf8_from_macro!(); +} + +fn require_c_str(_: &CStr) {} +fn require_os_str(_: &OsStr) {} +fn require_path(_: &std::path::Path) {} +fn require_str(_: &str) {} +fn require_slice(_: &[T]) {} +fn require_x(_: &X) {} + +fn require_deref_c_str>(_: T) {} +fn require_deref_os_str>(_: T) {} +fn require_deref_path>(_: T) {} +fn require_deref_str>(_: T) {} +fn require_deref_slice>(_: U) {} + +fn require_impl_deref_c_str(_: impl Deref) {} +fn require_impl_deref_os_str(_: impl Deref) {} +fn require_impl_deref_path(_: impl Deref) {} +fn require_impl_deref_str(_: impl Deref) {} +fn require_impl_deref_slice(_: impl Deref) {} + +fn require_deref_str_slice, U, V: Deref>(_: T, _: V) {} +fn require_deref_slice_str, V: Deref>(_: U, _: V) {} + +fn require_as_ref_c_str>(_: T) {} +fn require_as_ref_os_str>(_: T) {} +fn require_as_ref_path>(_: T) {} +fn require_as_ref_str>(_: T) {} +fn require_as_ref_slice>(_: U) {} + +fn require_impl_as_ref_c_str(_: impl AsRef) {} +fn require_impl_as_ref_os_str(_: impl AsRef) {} +fn require_impl_as_ref_path(_: impl AsRef) {} +fn require_impl_as_ref_str(_: impl AsRef) {} +fn require_impl_as_ref_slice(_: impl AsRef<[T]>) {} + +fn require_as_ref_str_slice, U, V: AsRef<[U]>>(_: T, _: V) {} +fn require_as_ref_slice_str, V: AsRef>(_: U, _: V) {} + +// `check_files` is based on: +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 +fn check_files(file_types: &[FileType]) -> bool { + for t in file_types { + let path = match get_file_path(t) { + Ok(p) => p, + Err(_) => { + return false; + }, + }; + if !path.is_file() { + return false; + } + } + true +} + +fn get_file_path(_file_type: &FileType) -> Result { + Ok(std::path::PathBuf::new()) +} + +fn require_string(_: &String) {} + +#[clippy::msrv = "1.35"] +fn _msrv_1_35() { + // `copied` was stabilized in 1.36, so clippy should use `cloned`. + let _ = &["x"][..].iter().cloned(); +} + +#[clippy::msrv = "1.36"] +fn _msrv_1_36() { + let _ = &["x"][..].iter().copied(); +} + +// https://github.com/rust-lang/rust-clippy/issues/8507 +mod issue_8507 { + #![allow(dead_code)] + + struct Opaque

(P); + + pub trait Abstracted {} + + impl

Abstracted for Opaque

{} + + fn build

(p: P) -> Opaque

+ where + P: AsRef, + { + Opaque(p) + } + + // Should not lint. + fn test_str(s: &str) -> Box { + Box::new(build(s.to_string())) + } + + // Should not lint. + fn test_x(x: super::X) -> Box { + Box::new(build(x)) + } + + #[derive(Clone, Copy)] + struct Y(&'static str); + + impl AsRef for Y { + fn as_ref(&self) -> &str { + self.0 + } + } + + #[allow(clippy::to_string_trait_impl)] + impl ToString for Y { + fn to_string(&self) -> String { + self.0.to_string() + } + } + + // Should lint because Y is copy. + fn test_y(y: Y) -> Box { + Box::new(build(y)) + } +} + +// https://github.com/rust-lang/rust-clippy/issues/8759 +mod issue_8759 { + #![allow(dead_code)] + + #[derive(Default)] + struct View {} + + impl std::borrow::ToOwned for View { + type Owned = View; + fn to_owned(&self) -> Self::Owned { + View {} + } + } + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} + +mod issue_8759_variant { + #![allow(dead_code)] + + #[derive(Clone, Default)] + struct View {} + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} + +mod issue_9317 { + #![allow(dead_code)] + + struct Bytes {} + + #[allow(clippy::to_string_trait_impl)] + impl ToString for Bytes { + fn to_string(&self) -> String { + "123".to_string() + } + } + + impl AsRef<[u8]> for Bytes { + fn as_ref(&self) -> &[u8] { + &[1, 2, 3] + } + } + + fn consume>(c: C) { + let _ = c; + } + + pub fn main() { + let b = Bytes {}; + // Should not lint. + consume(b.to_string()); + } +} + +mod issue_9351 { + #![allow(dead_code)] + + use std::ops::Deref; + use std::path::{Path, PathBuf}; + + fn require_deref_path>(x: T) -> T { + x + } + + fn generic_arg_used_elsewhere>(_x: T, _y: T) {} + + fn id>(x: T) -> T { + x + } + + fn predicates_are_satisfied(_x: impl std::fmt::Write) {} + + // Should lint + fn single_return() -> impl AsRef { + id("abc") + } + + // Should not lint + fn multiple_returns(b: bool) -> impl AsRef { + if b { + return String::new(); + } + + id("abc".to_string()) + } + + struct S1(String); + + // Should not lint + fn fields1() -> S1 { + S1(id("abc".to_string())) + } + + struct S2 { + s: String, + } + + // Should not lint + fn fields2() { + let mut s = S2 { s: "abc".into() }; + s.s = id("abc".to_string()); + } + + pub fn main() { + let path = std::path::Path::new("x"); + let path_buf = path.to_owned(); + + // Should not lint. + let _x: PathBuf = require_deref_path(path.to_owned()); + generic_arg_used_elsewhere(path.to_owned(), path_buf); + predicates_are_satisfied(id("abc".to_string())); + } +} + +mod issue_9504 { + #![allow(dead_code)] + + async fn foo>(_: S) {} + async fn bar() { + foo(std::path::PathBuf::new().to_string_lossy().to_string()).await; + } +} + +mod issue_9771a { + #![allow(dead_code)] + + use std::marker::PhantomData; + + pub struct Key, V: ?Sized>(K, PhantomData); + + impl, V: ?Sized> Key { + pub fn new(key: K) -> Key { + Key(key, PhantomData) + } + } + + pub fn pkh(pkh: &[u8]) -> Key, String> { + Key::new([b"pkh-", pkh].concat().to_vec()) + } +} + +mod issue_9771b { + #![allow(dead_code)] + + pub struct Key>(K); + + pub fn from(c: &[u8]) -> Key> { + let v = [c].concat(); + Key(v.to_vec()) + } +} + +// This is a watered down version of the code in: https://github.com/oxigraph/rio +// The ICE is triggered by the call to `to_owned` on this line: +// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 +mod issue_10021 { + #![allow(unused)] + + pub struct Iri(T); + + impl> Iri { + pub fn parse(iri: T) -> Result { + unimplemented!() + } + } + + pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { + let base_iri = Iri::parse(url.to_owned())?; + Ok(()) + } +} + +mod issue_10033 { + #![allow(dead_code)] + use std::fmt::Display; + use std::ops::Deref; + + fn _main() { + let f = Foo; + + // Not actually unnecessary - this calls `Foo`'s `Display` impl, not `str`'s (even though `Foo` does + // deref to `str`) + foo(&f.to_string()); + } + + fn foo(s: &str) { + println!("{}", s); + } + + struct Foo; + + impl Deref for Foo { + type Target = str; + + fn deref(&self) -> &Self::Target { + "str" + } + } + + impl Display for Foo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Foo") + } + } +} + +mod issue_11952 { + use core::future::{Future, IntoFuture}; + + fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future> { + async move { + let _y = y; + Ok(()) + } + } + + fn bar() { + IntoFuture::into_future(foo([], &0)); + } +} + +fn borrow_checks() { + use std::borrow::Borrow; + use std::collections::HashSet; + + fn inner(a: &[&str]) { + let mut s = HashSet::from([vec!["a"]]); + s.remove(a); //~ ERROR: unnecessary use of `to_vec` + } + + let mut s = HashSet::from(["a".to_string()]); + s.remove("b"); //~ ERROR: unnecessary use of `to_owned` + s.remove("b"); //~ ERROR: unnecessary use of `to_string` + // Should not warn. + s.remove("b"); + + let mut s = HashSet::from([vec!["a"]]); + s.remove(["b"].as_slice()); //~ ERROR: unnecessary use of `to_vec` + s.remove((&["b"]).as_slice()); //~ ERROR: unnecessary use of `to_vec` + + // Should not warn. + s.remove(&["b"].to_vec().clone()); + s.remove(["a"].as_slice()); + + trait SetExt { + fn foo>(&self, _: &String); + } + + impl SetExt for HashSet { + fn foo>(&self, _: &String) {} + } + + // Should not lint! + HashSet::::new().foo::<&str>(&"".to_owned()); + HashSet::::new().get(&1.to_string()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index da0c761f795b3..10a9727a9a798 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -7,8 +7,6 @@ )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index 7ab1f667d9b58..498ac68cdaa0f 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:157:64 + --> tests/ui/unnecessary_to_owned.rs:155:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:157:20 + --> tests/ui/unnecessary_to_owned.rs:155:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:158:40 + --> tests/ui/unnecessary_to_owned.rs:156:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:158:21 + --> tests/ui/unnecessary_to_owned.rs:156:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:159:48 + --> tests/ui/unnecessary_to_owned.rs:157:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:159:19 + --> tests/ui/unnecessary_to_owned.rs:157:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:160:35 + --> tests/ui/unnecessary_to_owned.rs:158:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:160:18 + --> tests/ui/unnecessary_to_owned.rs:158:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:161:39 + --> tests/ui/unnecessary_to_owned.rs:159:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:161:20 + --> tests/ui/unnecessary_to_owned.rs:159:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:66:36 + --> tests/ui/unnecessary_to_owned.rs:64:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,415 +70,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:67:19 + --> tests/ui/unnecessary_to_owned.rs:65:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> tests/ui/unnecessary_to_owned.rs:69:20 + --> tests/ui/unnecessary_to_owned.rs:67:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:70:38 + --> tests/ui/unnecessary_to_owned.rs:68:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:71:20 + --> tests/ui/unnecessary_to_owned.rs:69:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> tests/ui/unnecessary_to_owned.rs:73:18 + --> tests/ui/unnecessary_to_owned.rs:71:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:74:34 + --> tests/ui/unnecessary_to_owned.rs:72:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:75:18 + --> tests/ui/unnecessary_to_owned.rs:73:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:77:17 + --> tests/ui/unnecessary_to_owned.rs:75:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:78:30 + --> tests/ui/unnecessary_to_owned.rs:76:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:79:17 + --> tests/ui/unnecessary_to_owned.rs:77:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:80:17 + --> tests/ui/unnecessary_to_owned.rs:78:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:82:19 + --> tests/ui/unnecessary_to_owned.rs:80:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:83:36 + --> tests/ui/unnecessary_to_owned.rs:81:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:84:19 + --> tests/ui/unnecessary_to_owned.rs:82:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:85:19 + --> tests/ui/unnecessary_to_owned.rs:83:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:86:19 + --> tests/ui/unnecessary_to_owned.rs:84:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:89:42 + --> tests/ui/unnecessary_to_owned.rs:87:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:92:25 + --> tests/ui/unnecessary_to_owned.rs:90:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:93:26 + --> tests/ui/unnecessary_to_owned.rs:91:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:94:24 + --> tests/ui/unnecessary_to_owned.rs:92:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:95:23 + --> tests/ui/unnecessary_to_owned.rs:93:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:96:25 + --> tests/ui/unnecessary_to_owned.rs:94:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:98:30 + --> tests/ui/unnecessary_to_owned.rs:96:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:99:31 + --> tests/ui/unnecessary_to_owned.rs:97:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:100:29 + --> tests/ui/unnecessary_to_owned.rs:98:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:101:28 + --> tests/ui/unnecessary_to_owned.rs:99:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:102:30 + --> tests/ui/unnecessary_to_owned.rs:100:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:104:29 + --> tests/ui/unnecessary_to_owned.rs:102:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:104:43 + --> tests/ui/unnecessary_to_owned.rs:102:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:105:29 + --> tests/ui/unnecessary_to_owned.rs:103:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:105:47 + --> tests/ui/unnecessary_to_owned.rs:103:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:107:26 + --> tests/ui/unnecessary_to_owned.rs:105:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:108:27 + --> tests/ui/unnecessary_to_owned.rs:106:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:109:25 + --> tests/ui/unnecessary_to_owned.rs:107:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:110:24 + --> tests/ui/unnecessary_to_owned.rs:108:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:111:24 + --> tests/ui/unnecessary_to_owned.rs:109:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:112:26 + --> tests/ui/unnecessary_to_owned.rs:110:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:113:26 + --> tests/ui/unnecessary_to_owned.rs:111:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:114:26 + --> tests/ui/unnecessary_to_owned.rs:112:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:116:31 + --> tests/ui/unnecessary_to_owned.rs:114:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:117:32 + --> tests/ui/unnecessary_to_owned.rs:115:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:118:30 + --> tests/ui/unnecessary_to_owned.rs:116:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:119:29 + --> tests/ui/unnecessary_to_owned.rs:117:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:120:29 + --> tests/ui/unnecessary_to_owned.rs:118:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:121:31 + --> tests/ui/unnecessary_to_owned.rs:119:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:122:31 + --> tests/ui/unnecessary_to_owned.rs:120:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:123:31 + --> tests/ui/unnecessary_to_owned.rs:121:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:125:30 + --> tests/ui/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:125:44 + --> tests/ui/unnecessary_to_owned.rs:123:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:30 + --> tests/ui/unnecessary_to_owned.rs:124:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:44 + --> tests/ui/unnecessary_to_owned.rs:124:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:127:30 + --> tests/ui/unnecessary_to_owned.rs:125:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:127:44 + --> tests/ui/unnecessary_to_owned.rs:125:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:30 + --> tests/ui/unnecessary_to_owned.rs:126:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:48 + --> tests/ui/unnecessary_to_owned.rs:126:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:129:30 + --> tests/ui/unnecessary_to_owned.rs:127:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:129:52 + --> tests/ui/unnecessary_to_owned.rs:127:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:130:30 + --> tests/ui/unnecessary_to_owned.rs:128:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:130:48 + --> tests/ui/unnecessary_to_owned.rs:128:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:132:20 + --> tests/ui/unnecessary_to_owned.rs:130:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:134:13 + --> tests/ui/unnecessary_to_owned.rs:132:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:135:13 + --> tests/ui/unnecessary_to_owned.rs:133:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:136:13 + --> tests/ui/unnecessary_to_owned.rs:134:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:137:13 + --> tests/ui/unnecessary_to_owned.rs:135:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:139:13 + --> tests/ui/unnecessary_to_owned.rs:137:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:140:13 + --> tests/ui/unnecessary_to_owned.rs:138:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:141:13 + --> tests/ui/unnecessary_to_owned.rs:139:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:142:13 + --> tests/ui/unnecessary_to_owned.rs:140:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:164:26 + --> tests/ui/unnecessary_to_owned.rs:162:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:165:26 + --> tests/ui/unnecessary_to_owned.rs:163:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +502,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:166:26 + --> tests/ui/unnecessary_to_owned.rs:164:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -514,77 +514,73 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:223:14 + --> tests/ui/unnecessary_to_owned.rs:221:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ | -help: use - | -LL | for t in file_types { - | ~~~~~~~~~~ help: remove any references to the binding | -LL - let path = match get_file_path(&t) { -LL + let path = match get_file_path(t) { +LL ~ for t in file_types { +LL ~ let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:246:14 + --> tests/ui/unnecessary_to_owned.rs:244:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:251:14 + --> tests/ui/unnecessary_to_owned.rs:249:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:299:24 + --> tests/ui/unnecessary_to_owned.rs:297:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:408:12 + --> tests/ui/unnecessary_to_owned.rs:406:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:551:37 + --> tests/ui/unnecessary_to_owned.rs:549:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:561:18 + --> tests/ui/unnecessary_to_owned.rs:559:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:565:14 + --> tests/ui/unnecessary_to_owned.rs:563:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:566:14 + --> tests/ui/unnecessary_to_owned.rs:564:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:571:14 + --> tests/ui/unnecessary_to_owned.rs:569:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:572:14 + --> tests/ui/unnecessary_to_owned.rs:570:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index 231fc0a892ad1..de1062f123b7b 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -134,3 +134,12 @@ pub mod ambiguous_glob_exports { pub use my_prelude::*; pub use my_type::*; } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/13764 +pub mod unknown_namespace { + pub mod some_module { + pub struct SomeType; + } + #[allow(rustc::non_glob_import_of_type_ir_inherent)] + use some_module::SomeType; +} diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index 8dfcd2110a4bb..94657dd1ca378 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -134,3 +134,12 @@ pub mod ambiguous_glob_exports { pub use my_prelude::*; pub use my_type::*; } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/13764 +pub mod unknown_namespace { + pub mod some_module { + pub struct SomeType; + } + #[allow(rustc::non_glob_import_of_type_ir_inherent)] + use some_module::SomeType; +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index eff617a801689..2f7edd92bb7c7 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -3,6 +3,8 @@ // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] +use std::ops::ControlFlow; + fn test_generic(val: T) -> T { let _ = val; val @@ -297,3 +299,46 @@ impl From> for Foo<'b'> { Foo } } + +fn direct_application() { + let _: Result<(), std::io::Error> = test_issue_3913(); + //~^ useless_conversion + let _: Result<(), std::io::Error> = test_issue_3913(); + //~^ useless_conversion + let _: Result<(), std::io::Error> = test_issue_3913(); + //~^ useless_conversion + let _: Result<(), std::io::Error> = test_issue_3913(); + //~^ useless_conversion + + let c: ControlFlow<()> = ControlFlow::Continue(()); + let _: ControlFlow<()> = c; + //~^ useless_conversion + let c: ControlFlow<()> = ControlFlow::Continue(()); + let _: ControlFlow<()> = c; + //~^ useless_conversion + + struct Absorb; + impl From<()> for Absorb { + fn from(_: ()) -> Self { + Self + } + } + impl From for Absorb { + fn from(_: std::io::Error) -> Self { + Self + } + } + let _: Vec = [1u32].into_iter().collect(); + //~^ useless_conversion + + // No lint for those + let _: Result = test_issue_3913().map(Into::into); + let _: Result<(), Absorb> = test_issue_3913().map_err(Into::into); + let _: Result = test_issue_3913().map(From::from); + let _: Result<(), Absorb> = test_issue_3913().map_err(From::from); +} + +fn gen_identity(x: [T; 3]) -> Vec { + x.into_iter().collect() + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 64b0662078919..eacdf77f90520 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -3,6 +3,8 @@ // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] +use std::ops::ControlFlow; + fn test_generic(val: T) -> T { let _ = T::from(val); val.into() @@ -297,3 +299,46 @@ impl From> for Foo<'b'> { Foo } } + +fn direct_application() { + let _: Result<(), std::io::Error> = test_issue_3913().map(Into::into); + //~^ useless_conversion + let _: Result<(), std::io::Error> = test_issue_3913().map_err(Into::into); + //~^ useless_conversion + let _: Result<(), std::io::Error> = test_issue_3913().map(From::from); + //~^ useless_conversion + let _: Result<(), std::io::Error> = test_issue_3913().map_err(From::from); + //~^ useless_conversion + + let c: ControlFlow<()> = ControlFlow::Continue(()); + let _: ControlFlow<()> = c.map_break(Into::into); + //~^ useless_conversion + let c: ControlFlow<()> = ControlFlow::Continue(()); + let _: ControlFlow<()> = c.map_continue(Into::into); + //~^ useless_conversion + + struct Absorb; + impl From<()> for Absorb { + fn from(_: ()) -> Self { + Self + } + } + impl From for Absorb { + fn from(_: std::io::Error) -> Self { + Self + } + } + let _: Vec = [1u32].into_iter().map(Into::into).collect(); + //~^ useless_conversion + + // No lint for those + let _: Result = test_issue_3913().map(Into::into); + let _: Result<(), Absorb> = test_issue_3913().map_err(Into::into); + let _: Result = test_issue_3913().map(From::from); + let _: Result<(), Absorb> = test_issue_3913().map_err(From::from); +} + +fn gen_identity(x: [T; 3]) -> Vec { + x.into_iter().map(Into::into).collect() + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index b149357bcf4f4..6aeb382902ba1 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:7:13 + --> tests/ui/useless_conversion.rs:9:13 | LL | let _ = T::from(val); | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` @@ -11,220 +11,268 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:8:5 + --> tests/ui/useless_conversion.rs:10:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:20:22 + --> tests/ui/useless_conversion.rs:22:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:50:22 + --> tests/ui/useless_conversion.rs:52:22 | LL | if Some("ok") == lines.into_iter().next() {} | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:55:21 + --> tests/ui/useless_conversion.rs:57:21 | LL | let mut lines = text.lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:61:22 + --> tests/ui/useless_conversion.rs:63:22 | LL | if Some("ok") == text.lines().into_iter().next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:67:13 + --> tests/ui/useless_conversion.rs:69:13 | LL | let _ = NUMBERS.into_iter().next(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:72:17 + --> tests/ui/useless_conversion.rs:74:17 | LL | let mut n = NUMBERS.into_iter(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:134:21 + --> tests/ui/useless_conversion.rs:136:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:135:21 + --> tests/ui/useless_conversion.rs:137:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:136:13 + --> tests/ui/useless_conversion.rs:138:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:137:13 + --> tests/ui/useless_conversion.rs:139:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:138:13 + --> tests/ui/useless_conversion.rs:140:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> tests/ui/useless_conversion.rs:139:13 + --> tests/ui/useless_conversion.rs:141:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:140:21 + --> tests/ui/useless_conversion.rs:142:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:145:13 + --> tests/ui/useless_conversion.rs:147:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:151:23 + --> tests/ui/useless_conversion.rs:153:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:153:13 + --> tests/ui/useless_conversion.rs:155:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> tests/ui/useless_conversion.rs:155:13 + --> tests/ui/useless_conversion.rs:157:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:187:7 + --> tests/ui/useless_conversion.rs:189:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:177:13 + --> tests/ui/useless_conversion.rs:179:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:188:7 + --> tests/ui/useless_conversion.rs:190:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:178:18 + --> tests/ui/useless_conversion.rs:180:18 | LL | fn c(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:189:7 + --> tests/ui/useless_conversion.rs:191:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:181:12 + --> tests/ui/useless_conversion.rs:183:12 | LL | T: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:192:7 + --> tests/ui/useless_conversion.rs:194:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:177:13 + --> tests/ui/useless_conversion.rs:179:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:193:7 + --> tests/ui/useless_conversion.rs:195:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:177:13 + --> tests/ui/useless_conversion.rs:179:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:239:24 + --> tests/ui/useless_conversion.rs:241:24 | LL | foo2::([1, 2, 3].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:218:12 + --> tests/ui/useless_conversion.rs:220:12 | LL | I: IntoIterator + Helper, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:247:14 + --> tests/ui/useless_conversion.rs:249:14 | LL | foo3([1, 2, 3].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:227:12 + --> tests/ui/useless_conversion.rs:229:12 | LL | I: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:256:16 + --> tests/ui/useless_conversion.rs:258:16 | LL | S1.foo([1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:253:27 + --> tests/ui/useless_conversion.rs:255:27 | LL | pub fn foo(&self, _: I) {} | ^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:275:44 + --> tests/ui/useless_conversion.rs:277:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:262:20 + --> tests/ui/useless_conversion.rs:264:20 | LL | J: IntoIterator, | ^^^^^^^^^^^^ -error: aborting due to 28 previous errors +error: useless conversion to the same type: `()` + --> tests/ui/useless_conversion.rs:304:58 + | +LL | let _: Result<(), std::io::Error> = test_issue_3913().map(Into::into); + | ^^^^^^^^^^^^^^^^ help: consider removing + +error: useless conversion to the same type: `std::io::Error` + --> tests/ui/useless_conversion.rs:306:58 + | +LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(Into::into); + | ^^^^^^^^^^^^^^^^^^^^ help: consider removing + +error: useless conversion to the same type: `()` + --> tests/ui/useless_conversion.rs:308:58 + | +LL | let _: Result<(), std::io::Error> = test_issue_3913().map(From::from); + | ^^^^^^^^^^^^^^^^ help: consider removing + +error: useless conversion to the same type: `std::io::Error` + --> tests/ui/useless_conversion.rs:310:58 + | +LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(From::from); + | ^^^^^^^^^^^^^^^^^^^^ help: consider removing + +error: useless conversion to the same type: `()` + --> tests/ui/useless_conversion.rs:314:31 + | +LL | let _: ControlFlow<()> = c.map_break(Into::into); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing + +error: useless conversion to the same type: `()` + --> tests/ui/useless_conversion.rs:317:31 + | +LL | let _: ControlFlow<()> = c.map_continue(Into::into); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing + +error: useless conversion to the same type: `u32` + --> tests/ui/useless_conversion.rs:331:41 + | +LL | let _: Vec = [1u32].into_iter().map(Into::into).collect(); + | ^^^^^^^^^^^^^^^^ help: consider removing + +error: useless conversion to the same type: `T` + --> tests/ui/useless_conversion.rs:342:18 + | +LL | x.into_iter().map(Into::into).collect() + | ^^^^^^^^^^^^^^^^ help: consider removing + +error: aborting due to 36 previous errors diff --git a/src/tools/clippy/tests/ui/zombie_processes.rs b/src/tools/clippy/tests/ui/zombie_processes.rs index b41bcce3f7f7c..6f0d2760a860d 100644 --- a/src/tools/clippy/tests/ui/zombie_processes.rs +++ b/src/tools/clippy/tests/ui/zombie_processes.rs @@ -1,7 +1,7 @@ #![warn(clippy::zombie_processes)] -#![allow(clippy::if_same_then_else, clippy::ifs_same_cond)] +#![allow(clippy::if_same_then_else, clippy::ifs_same_cond, clippy::needless_return)] -use std::process::{Child, Command}; +use std::process::{Child, Command, ExitStatus}; fn main() { { @@ -12,7 +12,7 @@ fn main() { { let mut x = Command::new("").spawn().unwrap(); - //~^ ERROR: spawned process is never `wait()`ed on + //~^ zombie_processes x.kill(); x.id(); } @@ -39,7 +39,7 @@ fn main() { } { let mut x = Command::new("").spawn().unwrap(); - //~^ ERROR: spawned process is never `wait()`ed on + //~^ zombie_processes let v = &x; // (allow shared refs is fine because one cannot call `.wait()` through that) } @@ -64,14 +64,14 @@ fn main() { // It should assume that it might not exit and still lint { let mut x = Command::new("").spawn().unwrap(); - //~^ ERROR: spawned process is never `wait()`ed on + //~^ zombie_processes if true { std::process::exit(0); } } { let mut x = Command::new("").spawn().unwrap(); - //~^ ERROR: spawned process is never `wait()`ed on + //~^ zombie_processes if true { while false {} // Calling `exit()` after leaving a while loop should still be linted. @@ -97,7 +97,7 @@ fn main() { { let mut x = Command::new("").spawn().unwrap(); - //~^ ERROR: spawned process is never `wait()`ed on + //~^ zombie_processes if true { return; } @@ -106,12 +106,32 @@ fn main() { { let mut x = Command::new("").spawn().unwrap(); - //~^ ERROR: spawned process is never `wait()`ed on + //~^ zombie_processes if true { x.wait().unwrap(); } } + { + let mut x = Command::new("").spawn().unwrap(); + //~^ zombie_processes + if true { + x.wait().unwrap(); + } else { + // this else block exists to test the other help message + } + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ zombie_processes + if true { + // this else block exists to test the other help message + } else { + x.wait().unwrap(); + } + } + { let mut x = Command::new("").spawn().unwrap(); if true { @@ -143,3 +163,8 @@ fn main() { fn process_child(c: Child) { todo!() } + +fn return_wait() -> ExitStatus { + let mut x = Command::new("").spawn().unwrap(); + return x.wait().unwrap(); +} diff --git a/src/tools/clippy/tests/ui/zombie_processes.stderr b/src/tools/clippy/tests/ui/zombie_processes.stderr index eec821a4c8f18..afc518c60db2a 100644 --- a/src/tools/clippy/tests/ui/zombie_processes.stderr +++ b/src/tools/clippy/tests/ui/zombie_processes.stderr @@ -4,7 +4,7 @@ error: spawned process is never `wait()`ed on LL | let mut x = Command::new("").spawn().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider calling `.wait()` + = help: consider calling `.wait()` = note: not doing so might leave behind zombie processes = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning = note: `-D clippy::zombie-processes` implied by `-D warnings` @@ -16,7 +16,7 @@ error: spawned process is never `wait()`ed on LL | let mut x = Command::new("").spawn().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider calling `.wait()` + = help: consider calling `.wait()` = note: not doing so might leave behind zombie processes = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning @@ -26,7 +26,7 @@ error: spawned process is never `wait()`ed on LL | let mut x = Command::new("").spawn().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider calling `.wait()` + = help: consider calling `.wait()` = note: not doing so might leave behind zombie processes = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning @@ -36,29 +36,96 @@ error: spawned process is never `wait()`ed on LL | let mut x = Command::new("").spawn().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider calling `.wait()` + = help: consider calling `.wait()` = note: not doing so might leave behind zombie processes = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning -error: spawned process is never `wait()`ed on +error: spawned process is not `wait()`ed on in all code paths --> tests/ui/zombie_processes.rs:99:21 | LL | let mut x = Command::new("").spawn().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider calling `.wait()` +note: no `wait()` call exists on the code path to this early return + --> tests/ui/zombie_processes.rs:102:13 + | +LL | return; + | ^^^^^^ +note: `wait()` call exists, but it is unreachable due to the early return + --> tests/ui/zombie_processes.rs:104:9 + | +LL | x.wait().unwrap(); + | ^ + = help: consider calling `.wait()` in all code paths = note: not doing so might leave behind zombie processes = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning -error: spawned process is never `wait()`ed on +error: spawned process is not `wait()`ed on in all code paths --> tests/ui/zombie_processes.rs:108:21 | LL | let mut x = Command::new("").spawn().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider calling `.wait()` +note: this if expression has a `wait()` call, but it is missing an else block + --> tests/ui/zombie_processes.rs:110:9 + | +LL | / if true { +LL | | x.wait().unwrap(); +LL | | } + | |_________^ +note: `wait()` called here + --> tests/ui/zombie_processes.rs:111:13 + | +LL | x.wait().unwrap(); + | ^ + = help: consider calling `.wait()` in all code paths + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is not `wait()`ed on in all code paths + --> tests/ui/zombie_processes.rs:116:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `wait()` is not called in this if branch + --> tests/ui/zombie_processes.rs:120:10 + | +LL | } else { + | __________^ +LL | | // this else block exists to test the other help message +LL | | } + | |_________^ +note: `wait()` is called in the other branch + --> tests/ui/zombie_processes.rs:119:13 + | +LL | x.wait().unwrap(); + | ^ + = help: consider calling `.wait()` in all code paths + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is not `wait()`ed on in all code paths + --> tests/ui/zombie_processes.rs:126:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `wait()` is not called in this if branch + --> tests/ui/zombie_processes.rs:128:9 + | +LL | / if true { +LL | | // this else block exists to test the other help message +LL | | } else { + | |_________^ +note: `wait()` is called in the other branch + --> tests/ui/zombie_processes.rs:131:13 + | +LL | x.wait().unwrap(); + | ^ + = help: consider calling `.wait()` in all code paths = note: not doing so might leave behind zombie processes = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 9a5365b2158b6..c2197b89c566e 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -232,13 +232,13 @@ const APPLICABILITIES_FILTER_DEFAULT = { MaybeIncorrect: true, HasPlaceholders: true, }; -const URL_PARAMS_CORRESPONDANCE = { +const URL_PARAMS_CORRESPONDENCE = { "groups_filter": "groups", "levels_filter": "levels", "applicabilities_filter": "applicabilities", "version_filter": "versions", }; -const VERSIONS_CORRESPONDANCE = { +const VERSIONS_CORRESPONDENCE = { "lte": "≤", "gte": "≥", "eq": "=", @@ -285,7 +285,7 @@ window.filters = { } function updateIfNeeded(filterName, obj2) { const obj1 = filters[filterName]; - const name = URL_PARAMS_CORRESPONDANCE[filterName]; + const name = URL_PARAMS_CORRESPONDENCE[filterName]; if (!compareObjects(obj1, obj2)) { urlParams.set( name, @@ -316,9 +316,9 @@ window.filters = { versions.push(`lte:${filters.version_filter["≤"]}`); } if (versions.length !== 0) { - urlParams.set(URL_PARAMS_CORRESPONDANCE["version_filter"], versions.join(",")); + urlParams.set(URL_PARAMS_CORRESPONDENCE["version_filter"], versions.join(",")); } else { - urlParams.delete(URL_PARAMS_CORRESPONDANCE["version_filter"]); + urlParams.delete(URL_PARAMS_CORRESPONDENCE["version_filter"]); } let params = urlParams.toString(); @@ -532,7 +532,7 @@ function parseURLFilters() { const urlParams = new URLSearchParams(window.location.search); for (const [key, value] of urlParams.entries()) { - for (const [corres_key, corres_value] of Object.entries(URL_PARAMS_CORRESPONDANCE)) { + for (const [corres_key, corres_value] of Object.entries(URL_PARAMS_CORRESPONDENCE)) { if (corres_value === key) { if (key !== "versions") { const settings = new Set(value.split(",")); @@ -545,7 +545,7 @@ function parseURLFilters() { for (const [kind, value] of settings) { const elem = document.querySelector( - `#version-filter input[data-value="${VERSIONS_CORRESPONDANCE[kind]}"]`); + `#version-filter input[data-value="${VERSIONS_CORRESPONDENCE[kind]}"]`); elem.value = value; updateVersionFilters(elem, true); }