diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh index ea118a3b6fce5..2f062799a3607 100644 --- a/src/tools/clippy/.github/deploy.sh +++ b/src/tools/clippy/.github/deploy.sh @@ -45,6 +45,8 @@ if [[ -n $TAG_NAME ]]; then git add "$TAG_NAME" # Update the symlink git add stable + # Update the index.html file + git add index.html git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" elif [[ $BETA = "true" ]]; then if git diff --exit-code --quiet -- beta/; then diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh index 701be6bd76d34..5a81b41129188 100755 --- a/src/tools/clippy/.github/driver.sh +++ b/src/tools/clippy/.github/driver.sh @@ -47,9 +47,9 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? -./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/box_default.rs 2>box_default.stderr && exit 1 -sed -e "/= help: for/d" box_default.stderr > normalized.stderr -diff -u normalized.stderr tests/ui/box_default.stderr +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/string_to_string.rs 2>string_to_string.stderr && exit 1 +sed -e "/= help: for/d" string_to_string.stderr > normalized.stderr +diff -u normalized.stderr tests/ui/string_to_string.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same SYSROOT=$(rustc --print sysroot) diff --git a/src/tools/clippy/.github/workflows/clippy_changelog.yml b/src/tools/clippy/.github/workflows/clippy_changelog.yml new file mode 100644 index 0000000000000..a2657bfea490d --- /dev/null +++ b/src/tools/clippy/.github/workflows/clippy_changelog.yml @@ -0,0 +1,59 @@ +name: Clippy changelog check + +on: + merge_group: + pull_request: + types: [opened, reopened, synchronize, edited] + +concurrency: + # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. + # If the push is not attached to a PR, we will cancel all builds on the same branch. + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + +jobs: + changelog: + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + + steps: + # Run + - name: Check Changelog + if: ${{ github.event_name == 'pull_request' }} + run: | + body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \ + python -c "import sys, json; print(json.load(sys.stdin)['body'])") + output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") + if [ -z "$output" ]; then + echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it." + exit 1 + else + echo "changelog: $output" + fi + env: + PYTHONIOENCODING: 'utf-8' + PR_NUMBER: '${{ github.event.number }}' + + # We need to have the "conclusion" job also on PR CI, to make it possible + # to add PRs to a merge queue. + conclusion_changelog: + needs: [ changelog ] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index dee7d028655e3..c337a96bdac52 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -15,37 +15,7 @@ defaults: shell: bash jobs: - changelog: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.ref }} - # Unsetting this would make so that any malicious package could get our Github Token - persist-credentials: false - - # Run - - name: Check Changelog - run: | - MESSAGE=$(git log --format=%B -n 1) - PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//') - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || { - echo "ERROR: PR body must contain 'changelog: ...'" - exit 1 - } - if [[ "$output" = "none" ]]; then - echo "WARNING: changelog is 'none'" - else - echo "changelog: $output" - fi - env: - PYTHONIOENCODING: 'utf-8' base: - needs: changelog strategy: matrix: include: @@ -119,7 +89,6 @@ jobs: OS: ${{ runner.os }} metadata_collection: - needs: changelog runs-on: ubuntu-latest steps: @@ -138,7 +107,6 @@ jobs: run: cargo collect-metadata integration_build: - needs: changelog runs-on: ubuntu-latest steps: @@ -228,7 +196,7 @@ jobs: INTEGRATION: ${{ matrix.integration }} conclusion: - needs: [ changelog, base, metadata_collection, integration_build, integration ] + needs: [ base, metadata_collection, integration_build, integration ] # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 64966f1d1898b..d487c7d949857 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -1,6 +1,12 @@ name: Lintcheck -on: pull_request +on: + pull_request: + paths-ignore: + - 'book/**' + - 'util/**' + - 'tests/**' + - '*.md' env: RUST_BACKTRACE: 1 diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 69d00dc027e85..13902f78b541d 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -27,7 +27,7 @@ jobs: - name: Install mdbook run: | mkdir mdbook - curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.43/mdbook-v0.4.43-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH # Run diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 1770e8095a01c..bc42c07224e1a 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5533,6 +5533,7 @@ Released 2018-09-13 [`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 +[`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use @@ -5762,11 +5763,13 @@ Released 2018-09-13 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid +[`manual_repeat_n`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain [`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic @@ -5904,6 +5907,7 @@ Released 2018-09-13 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty +[`non_std_lazy_statics`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics [`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options @@ -6063,6 +6067,7 @@ Released 2018-09-13 [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next +[`sliced_string_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#sliced_string_as_bytes [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc @@ -6172,12 +6177,14 @@ Released 2018-09-13 [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports +[`unnecessary_semicolon`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern +[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns [`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable @@ -6216,6 +6223,7 @@ Released 2018-09-13 [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq +[`useless_nonzero_new_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_nonzero_new_unchecked [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 1f6c918fc6cc6..3b33f71906383 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -199,7 +199,7 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and responding to issues there may not always be enough time to stay on top of it all. -Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example +Our highest priority is fixing [ICEs][I-ICE] and [bugs][C-bug], for example an ICE in a popular crate that many other crates depend on. We don't want Clippy to crash on your code and we want it to be as reliable as the suggestions from Rust compiler errors. @@ -213,8 +213,8 @@ Or rather: before the sync this should be addressed, e.g. by removing a lint again, so it doesn't hit beta/stable. [triage]: https://forge.rust-lang.org/release/triage-procedure.html -[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash -[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug +[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE +[C-bug]: https://github.com/rust-lang/rust-clippy/labels/C-bug [p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low [p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium [p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index efa59fecc0e88..c9e4f15afbf8e 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] categories = ["development-tools", "development-tools::cargo-plugins"] build = "build.rs" -edition = "2021" +edition = "2024" publish = false [[bin]] diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index cb3a22d4288fa..32c1d33e2ed36 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -159,11 +159,11 @@ line. (You can swap `clippy::all` with the specific lint category you are target You can add options to your code to `allow`/`warn`/`deny` Clippy lints: * the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`). - Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html). + Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html). * all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, - `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive - lints prone to false positives. + `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive + lints prone to false positives. * only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) diff --git a/src/tools/clippy/book/book.toml b/src/tools/clippy/book/book.toml index 93b6641f7e1e7..c918aadf83c4b 100644 --- a/src/tools/clippy/book/book.toml +++ b/src/tools/clippy/book/book.toml @@ -6,7 +6,7 @@ src = "src" title = "Clippy Documentation" [rust] -edition = "2018" +edition = "2024" [output.html] edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}" diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index c07568697d02d..c26ad319f4f5d 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -299,10 +299,11 @@ This is good, because it makes writing this particular lint less complicated. We have to make this decision with every new Clippy lint. It boils down to using either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. -In short, the `EarlyLintPass` runs before type checking and -[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass` -has access to type information. Consider using the `LateLintPass` unless you need -something specific from the `EarlyLintPass`. +`EarlyLintPass` runs before type checking and +[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering, while `LateLintPass` +runs after these stages, providing access to type information. The `cargo dev new_lint` command +defaults to the recommended `LateLintPass`, but you can specify `--pass=early` if your lint +only needs AST level analysis. Since we don't need type information for checking the function name, we used `--pass=early` when running the new lint automation and all the imports were @@ -537,7 +538,7 @@ via `Tools -> Clippy` and you should see the generated code in the output below. If the command was executed successfully, you can copy the code over to where you are implementing your lint. -[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 ## Print HIR lint @@ -552,7 +553,7 @@ attribute to expressions you often need to enable _Clippy_. [_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html -[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=daf14db3a7f39ca467cd1b86c34b9afb ## Documentation 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 c354e8914f5db..b44ad80a25cc8 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 @@ -265,10 +265,10 @@ functions to deal with macros: ``` [Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html -[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.pat_ty [paths]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/paths/index.html diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index ceabb255e2d0b..169cecd7d11dc 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -139,10 +139,10 @@ Untracked files: ``` -## The `define_clippy_lints` macro +## The `declare_clippy_lint` macro After `cargo dev new_lint`, you should see a macro with the name -`define_clippy_lints`. It will be in the same file if you defined a standalone +`declare_clippy_lint`. It will be in the same file if you defined a standalone lint, and it will be in `mod.rs` if you defined a type-specific lint. The macro looks something like this: diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md index 20b870eb69a8e..8b080c099b81c 100644 --- a/src/tools/clippy/book/src/development/infrastructure/release.md +++ b/src/tools/clippy/book/src/development/infrastructure/release.md @@ -96,9 +96,9 @@ git tag rust-1.XX.0 # XX should be exchanged with the correspondin 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]. +After this, the release should be available on the Clippy [tags page]. -[release page]: https://github.com/rust-lang/rust-clippy/releases +[tags page]: https://github.com/rust-lang/rust-clippy/tags ## Publish `clippy_utils` diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 181e794e6e468..b8f9fff9c6138 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -750,6 +750,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_pattern_char_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison) * [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) * [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) @@ -762,11 +763,13 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) * [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) * [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) * [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) +* [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index c761e207c6b61..e473a58394022 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -3,7 +3,7 @@ name = "clippy_config" # begin autogenerated version version = "0.1.86" # end autogenerated version -edition = "2021" +edition = "2024" publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index c616589c56e05..552141476f3ac 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -619,6 +619,7 @@ define_Conf! { manual_pattern_char_comparison, manual_range_contains, manual_rem_euclid, + manual_repeat_n, manual_retain, manual_split_once, manual_str_repeat, @@ -631,11 +632,13 @@ define_Conf! { mem_replace_with_default, missing_const_for_fn, needless_borrow, + non_std_lazy_statics, option_as_ref_deref, option_map_unwrap_or, ptr_as_ptr, redundant_field_names, redundant_static_lifetimes, + same_item_push, seek_from_current, seek_rewind, transmute_ptr_to_ref, diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index d3a103eaf4c62..47b7b3758613c 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -2,7 +2,7 @@ name = "clippy_dev" description = "Clippy developer tooling" version = "0.0.1" -edition = "2021" +edition = "2024" [dependencies] aho-corasick = "1.0" diff --git a/src/tools/clippy/clippy_dev/src/setup/intellij.rs b/src/tools/clippy/clippy_dev/src/setup/intellij.rs index a7138f36a4ef5..c56811ee0a01e 100644 --- a/src/tools/clippy/clippy_dev/src/setup/intellij.rs +++ b/src/tools/clippy/clippy_dev/src/setup/intellij.rs @@ -62,7 +62,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result { eprintln!("error: unable to get the absolute path of rustc ({err})"); return Err(()); }, - }; + } } let path = path.join("compiler"); diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 612d1c0ae1398..fc0780f89a7f7 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -842,7 +842,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, Err(e) => panic_file(e, new_name, "create"), - }; + } match fs::rename(old_name, new_name) { Ok(()) => true, Err(e) => { diff --git a/src/tools/clippy/clippy_dummy/Cargo.toml b/src/tools/clippy/clippy_dummy/Cargo.toml index c206a1eb07b50..61bdd421c764e 100644 --- a/src/tools/clippy/clippy_dummy/Cargo.toml +++ b/src/tools/clippy/clippy_dummy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_dummy" # rename to clippy before publishing version = "0.0.303" -edition = "2018" +edition = "2024" readme = "crates-readme.md" description = "A bunch of helpful lints to avoid common pitfalls in Rust." build = 'build.rs' diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index b575ac1bf4cc5..c62a7ec783b6f 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -8,7 +8,7 @@ repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] -edition = "2021" +edition = "2024" [dependencies] arrayvec = { version = "0.7", default-features = false } 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 380b094d017e0..0389223c3e0fa 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 @@ -445,8 +445,8 @@ fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssoc #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. use SourceItemOrderingTraitAssocItemKind::*; match value { - AssocItemKind::Const { .. } => Const, - AssocItemKind::Type { .. } => Type, + AssocItemKind::Const => Const, + AssocItemKind::Type => Type, AssocItemKind::Fn { .. } => Fn, } } diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index c8dd77d9578d7..c01155ca86e01 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -257,7 +257,7 @@ fn build_sugg<'tcx>( // The receiver may have been a value type, so we need to add an `&` to // be sure the argument to clone_from will be a reference. arg_sugg = arg_sugg.addr(); - }; + } format!("{receiver_sugg}.clone_from({arg_sugg})") }, diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 8c91c65eaf76b..3e4bcfbfc1905 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -60,7 +60,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) } outer_attr_kind.insert(kind); }, - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs index 96a2b1614646e..60371dcd7715d 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs @@ -161,6 +161,15 @@ declare_clippy_lint! { /// [dependencies] /// regex = "*" /// ``` + /// Use instead: + /// ```toml + /// [dependencies] + /// # allow patch updates, but not minor or major version changes + /// some_crate_1 = "~1.2.3" + /// + /// # pin the version to a specific version + /// some_crate_2 = "=1.2.3" + /// ``` #[clippy::version = "1.32.0"] pub WILDCARD_DEPENDENCIES, cargo, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs index 3cf4a43b0d4c7..504d0a267e47d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs @@ -84,6 +84,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca diag .note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); - }; + } }); } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 4be53ace6871e..45045e58ac75e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -205,7 +205,7 @@ fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { // - uncertain if there are any uncertain values (because they could be negative or positive), Sign::Uncertain => return Sign::Uncertain, Sign::ZeroOrPositive => (), - }; + } } // A mul/div is: @@ -236,7 +236,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { // - uncertain if there are any uncertain values (because they could be negative or positive), Sign::Uncertain => return Sign::Uncertain, Sign::ZeroOrPositive => positive_count += 1, - }; + } } // A sum is: diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 364f5c7dc7a08..1edfde974227e 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -273,7 +273,7 @@ fn get_types_from_cast<'a>( }, _ => {}, } - }; + } None } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 87e546fbf0144..86bcf8edd578b 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -142,6 +142,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::DOC_NESTED_REFDEFS_INFO, + crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO, crate::doc::EMPTY_DOCS_INFO, crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, @@ -335,6 +336,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, crate::matches::MANUAL_FILTER_INFO, crate::matches::MANUAL_MAP_INFO, + crate::matches::MANUAL_OK_ERR_INFO, crate::matches::MANUAL_UNWRAP_OR_INFO, crate::matches::MATCH_AS_REF_INFO, crate::matches::MATCH_BOOL_INFO, @@ -419,6 +421,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::MANUAL_IS_VARIANT_AND_INFO, crate::methods::MANUAL_NEXT_BACK_INFO, crate::methods::MANUAL_OK_OR_INFO, + crate::methods::MANUAL_REPEAT_N_INFO, crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, crate::methods::MANUAL_SPLIT_ONCE_INFO, crate::methods::MANUAL_STR_REPEAT_INFO, @@ -466,6 +469,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, crate::methods::SINGLE_CHAR_ADD_STR_INFO, crate::methods::SKIP_WHILE_NEXT_INFO, + crate::methods::SLICED_STRING_AS_BYTES_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, @@ -495,6 +499,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, + crate::methods::USELESS_NONZERO_NEW_UNCHECKED_INFO, crate::methods::VEC_RESIZE_TO_ZERO_INFO, crate::methods::VERBOSE_FILE_READS_INFO, crate::methods::WAKER_CLONE_WAKE_INFO, @@ -572,6 +577,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::non_expressive_names::SIMILAR_NAMES_INFO, crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::non_std_lazy_statics::NON_STD_LAZY_STATICS_INFO, crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO, crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, @@ -753,8 +759,10 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, + crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, + crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO, crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, crate::unused_async::UNUSED_ASYNC_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index 33a97222b8f8f..bbd5dc15542d1 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -80,6 +80,6 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { String::new(), Applicability::MachineApplicable, ); - }; + } } } 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 c04a73c890f0e..ca3eaae7b85f3 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -223,19 +223,17 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { } fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { - match pat.kind { - PatKind::Expr(&PatExpr { - hir_id, - kind: PatExprKind::Lit { lit, .. }, - .. - }) => { - let ty = self.cx.typeck_results().node_type(hir_id); - self.check_lit(lit, ty, hir_id); - return; - }, - _ => {}, + if let PatKind::Expr(&PatExpr { + hir_id, + kind: PatExprKind::Lit { lit, .. }, + .. + }) = pat.kind + { + let ty = self.cx.typeck_results().node_type(hir_id); + self.check_lit(lit, ty, hir_id); + return; } - walk_pat(self, pat) + walk_pat(self, pat); } fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index f5589d8f8e250..123e358d7c3df 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { deref_count += 1; }, None => break None, - }; + } }; let use_node = use_cx.use_node(cx); diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs index f9e4a43c0e7a8..2577324f23df9 100644 --- a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs +++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs @@ -1,11 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use itertools::Itertools; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_span::{BytePos, Span}; +use std::cmp::Ordering; use std::ops::Range; -use super::DOC_LAZY_CONTINUATION; +use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS}; fn map_container_to_text(c: &super::Container) -> &'static str { match c { @@ -28,12 +29,57 @@ pub(super) fn check( return; } + // Blockquote let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); let blockquote_level = containers .iter() .filter(|c| matches!(c, super::Container::Blockquote)) .count(); - let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count(); + if ccount < blockquote_level { + span_lint_and_then( + cx, + DOC_LAZY_CONTINUATION, + span, + "doc quote line without `>` marker", + |diag| { + let mut doc_start_range = &doc[range]; + let mut suggested = String::new(); + for c in containers { + let text = map_container_to_text(c); + if doc_start_range.starts_with(text) { + doc_start_range = &doc_start_range[text.len()..]; + span = span.with_lo( + span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")), + ); + } else if matches!(c, super::Container::Blockquote) + && let Some(i) = doc_start_range.find('>') + { + doc_start_range = &doc_start_range[i + 1..]; + span = span + .with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); + } else { + suggested.push_str(text); + } + } + diag.span_suggestion_verbose( + span, + "add markers to start of line", + suggested, + Applicability::MachineApplicable, + ); + diag.help("if this not intended to be a quote at all, escape it with `\\>`"); + }, + ); + return; + } + + if ccount != 0 && blockquote_level != 0 { + // If this doc is a blockquote, we don't go further. + return; + } + + // List + let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count(); let list_indentation = containers .iter() .map(|c| { @@ -44,50 +90,36 @@ pub(super) fn check( } }) .sum(); - if ccount < blockquote_level || lcount < list_indentation { - let msg = if ccount < blockquote_level { - "doc quote line without `>` marker" - } else { - "doc list item without indentation" - }; - span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { - if ccount == 0 && blockquote_level == 0 { + match leading_spaces.cmp(&list_indentation) { + Ordering::Less => span_lint_and_then( + cx, + DOC_LAZY_CONTINUATION, + span, + "doc list item without indentation", + |diag| { // simpler suggestion style for indentation - let indent = list_indentation - lcount; + let indent = list_indentation - leading_spaces; diag.span_suggestion_verbose( span.shrink_to_hi(), "indent this line", - std::iter::repeat(" ").take(indent).join(""), + std::iter::repeat_n(" ", indent).join(""), Applicability::MaybeIncorrect, ); diag.help("if this is supposed to be its own paragraph, add a blank line"); - return; - } - let mut doc_start_range = &doc[range]; - let mut suggested = String::new(); - for c in containers { - let text = map_container_to_text(c); - if doc_start_range.starts_with(text) { - doc_start_range = &doc_start_range[text.len()..]; - span = span - .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger"))); - } else if matches!(c, super::Container::Blockquote) - && let Some(i) = doc_start_range.find('>') - { - doc_start_range = &doc_start_range[i + 1..]; - span = - span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); - } else { - suggested.push_str(text); - } - } - diag.span_suggestion_verbose( + }, + ), + Ordering::Greater => { + let sugg = std::iter::repeat_n(" ", list_indentation).join(""); + span_lint_and_sugg( + cx, + DOC_OVERINDENTED_LIST_ITEMS, span, - "add markers to start of line", - suggested, - Applicability::MachineApplicable, + "doc list item overindented", + format!("try using `{sugg}` ({list_indentation} spaces)"), + sugg, + Applicability::MaybeIncorrect, ); - diag.help("if this not intended to be a quote at all, escape it with `\\>`"); - }); + }, + Ordering::Equal => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 7561a6cf2a780..15530c3dbc509 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -428,6 +428,39 @@ declare_clippy_lint! { "require every line of a paragraph to be indented and marked" } +declare_clippy_lint! { + /// ### What it does + /// + /// Detects overindented list items in doc comments where the continuation + /// lines are indented more than necessary. + /// + /// ### Why is this bad? + /// + /// Overindented list items in doc comments can lead to inconsistent and + /// poorly formatted documentation when rendered. Excessive indentation may + /// cause the text to be misinterpreted as a nested list item or code block, + /// affecting readability and the overall structure of the documentation. + /// + /// ### Example + /// + /// ```no_run + /// /// - This is the first item in a list + /// /// and this line is overindented. + /// # fn foo() {} + /// ``` + /// + /// Fixes this into: + /// ```no_run + /// /// - This is the first item in a list + /// /// and this line is overindented. + /// # fn foo() {} + /// ``` + #[clippy::version = "1.80.0"] + pub DOC_OVERINDENTED_LIST_ITEMS, + style, + "ensure list items are not overindented" +} + declare_clippy_lint! { /// ### What it does /// Checks if the first paragraph in the documentation of items listed in the module page is too long. @@ -617,6 +650,7 @@ impl_lint_pass!(Documentation => [ SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, DOC_LAZY_CONTINUATION, + DOC_OVERINDENTED_LIST_ITEMS, EMPTY_LINE_AFTER_OUTER_ATTR, EMPTY_LINE_AFTER_DOC_COMMENTS, TOO_LONG_FIRST_DOC_PARAGRAPH, diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index e9e9d00907ea0..a090a987d4fc3 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { var.span, "C-like enum variant discriminant is not portable to 32-bit targets", ); - }; + } } } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 57a30de7ad14a..52b699274bbbd 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,9 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id}; +use clippy_utils::{ + get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, +}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; @@ -101,19 +103,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }; if body.value.span.from_expansion() { - if body.params.is_empty() { - if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); - } + if body.params.is_empty() + && let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) + { + let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" }; + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + format!("{vec_crate}::vec::Vec::new"), + Applicability::MachineApplicable, + ); } // skip `foo(|| macro!())` return; diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 688979311c89a..cdbfe7af8f9de 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -183,7 +183,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { .collect() }; self.emit_sugg(spans, msg, help); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs index 05e341e06fde4..752dbc0db4dbc 100644 --- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -45,7 +45,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: for param in generics.params { if param.is_impl_trait() { report(cx, param, generics); - }; + } } } } 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 1a01f5f885c30..90d3db2700fa0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -160,7 +160,7 @@ fn check_needless_must_use( && !is_must_use_ty(cx, future_ty) { return; - }; + } } span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index dd5908553e59b..41d2b18803d95 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -120,7 +120,7 @@ fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, B let ecx = ConstEvalCtxt::new(cx); if let Some(Constant::Int(c)) = ecx.eval(r) { return Some((c, op.node, l)); - }; + } if let Some(Constant::Int(c)) = ecx.eval(l) { return Some((c, invert_op(op.node)?, r)); } diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 37481dc7feb7b..152d506a7c00d 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -350,7 +350,7 @@ fn check_with_condition<'tcx>( if cx.typeck_results().expr_ty(cond_left).is_signed() { } else { print_lint_and_sugg(cx, var_name, expr); - }; + } } }, ExprKind::Path(QPath::TypeRelative(_, name)) => { diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs index 5131f5b7269b6..b1cb6da9475ba 100644 --- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs +++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs @@ -65,6 +65,6 @@ impl LateLintPass<'_> for IterOverHashType { expr.span, "iteration over unordered hash-based type", ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 2d2438514cc9d..34ded6c65009d 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -41,6 +41,6 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { Some(ty.span.with_lo(local.pat.span.hi())), "remove the explicit type `_` declaration", ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index fad6f9d088031..4b700673d0f80 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -279,6 +279,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; +mod non_std_lazy_statics; mod non_zero_suggestions; mod nonstandard_macro_braces; mod octal_escapes; @@ -372,8 +373,10 @@ mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; +mod unnecessary_semicolon; mod unnecessary_struct_initialization; mod unnecessary_wraps; +mod unneeded_struct_pattern; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; @@ -680,7 +683,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); + store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf))); store.register_late_pass(|_| Box::new(entry::HashMapPass)); store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv)); @@ -970,5 +973,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index e6761ea5c67c3..c9ab0beb5dfa0 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -1,4 +1,6 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::trait_ref_of_method; use itertools::Itertools; use rustc_ast::visit::{try_visit, walk_list}; @@ -20,7 +22,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{Ident, kw}; @@ -91,7 +93,19 @@ declare_clippy_lint! { "unused lifetimes in function definitions" } -declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); +pub struct Lifetimes { + msrv: Msrv, +} + +impl Lifetimes { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { .. } = item.kind { - check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { item.generics, item.span, report_extra_lifetimes, + &self.msrv, ); } } @@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv); } } + + extract_msrv_attr!(LateContext); } +#[allow(clippy::too_many_arguments)] fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, @@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>( generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, + msrv: &Msrv, ) { if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; @@ -195,7 +214,7 @@ fn check_fn_inner<'tcx>( } } - if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) { if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) { return; } @@ -216,6 +235,7 @@ fn could_use_elision<'tcx>( body: Option, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], + msrv: &Msrv, ) -> Option<(Vec, Vec)> { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -249,17 +269,17 @@ fn could_use_elision<'tcx>( let input_lts = input_visitor.lts; let output_lts = output_visitor.lts; - if let Some(trait_sig) = trait_sig { - if explicit_self_type(cx, func, trait_sig.first().copied()) { - return None; - } + if let Some(trait_sig) = trait_sig + && non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv) + { + return None; } if let Some(body_id) = body { let body = cx.tcx.hir().body(body_id); let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); - if explicit_self_type(cx, func, first_ident) { + if non_elidable_self_type(cx, func, first_ident, msrv) { return None; } @@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { - if let Some(ident) = ident +// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064 +fn non_elidable_self_type<'tcx>( + cx: &LateContext<'tcx>, + func: &FnDecl<'tcx>, + ident: Option, + msrv: &Msrv, +) -> bool { + if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION) + && let Some(ident) = ident && ident.name == kw::SelfLower && !func.implicit_self.has_implicit_self() && let Some(self_ty) = func.inputs.first() @@ -488,11 +514,13 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } +#[allow(clippy::struct_excessive_bools)] struct Usage { lifetime: Lifetime, in_where_predicate: bool, in_bounded_ty: bool, in_generics_arg: bool, + lifetime_elision_impossible: bool, } struct LifetimeChecker<'cx, 'tcx, F> { @@ -501,6 +529,7 @@ struct LifetimeChecker<'cx, 'tcx, F> { where_predicate_depth: usize, bounded_ty_depth: usize, generic_args_depth: usize, + lifetime_elision_impossible: bool, phantom: std::marker::PhantomData, } @@ -525,6 +554,7 @@ where where_predicate_depth: 0, bounded_ty_depth: 0, generic_args_depth: 0, + lifetime_elision_impossible: false, phantom: std::marker::PhantomData, } } @@ -566,6 +596,7 @@ where in_where_predicate: self.where_predicate_depth != 0, in_bounded_ty: self.bounded_ty_depth != 0, in_generics_arg: self.generic_args_depth != 0, + lifetime_elision_impossible: self.lifetime_elision_impossible, }); } } @@ -592,11 +623,44 @@ where self.generic_args_depth -= 1; } + fn visit_fn_decl(&mut self, fd: &'tcx FnDecl<'tcx>) -> Self::Result { + self.lifetime_elision_impossible = !is_candidate_for_elision(fd); + walk_fn_decl(self, fd); + self.lifetime_elision_impossible = false; + } + fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } } +/// Check if `fd` supports function elision with an anonymous (or elided) lifetime, +/// and has a lifetime somewhere in its output type. +fn is_candidate_for_elision(fd: &FnDecl<'_>) -> bool { + struct V; + + impl Visitor<'_> for V { + type Result = ControlFlow; + + fn visit_lifetime(&mut self, lifetime: &Lifetime) -> Self::Result { + ControlFlow::Break(lifetime.is_elided() || lifetime.is_anonymous()) + } + } + + if fd.lifetime_elision_allowed + && let Return(ret_ty) = fd.output + && walk_unambig_ty(&mut V, ret_ty).is_break() + { + // The first encountered input lifetime will either be one on `self`, or will be the only lifetime. + fd.inputs + .iter() + .find_map(|ty| walk_unambig_ty(&mut V, ty).break_value()) + .unwrap() + } else { + false + } +} + fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { let mut checker = LifetimeChecker::::new(cx, generics); @@ -662,6 +726,7 @@ fn report_elidable_impl_lifetimes<'tcx>( Usage { lifetime, in_where_predicate: false, + lifetime_elision_impossible: false, .. }, ] = usages.as_slice() @@ -719,7 +784,7 @@ fn report_elidable_lifetimes( |diag| { if !include_suggestions { return; - }; + } if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index e2dcb20f906d3..a4cedf3bed354 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -251,7 +251,7 @@ impl LiteralDigitGrouping { ); if !consistent { return Err(WarningType::InconsistentDigitGrouping); - }; + } } Ok(()) 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 index 49353a1b76bec..a957c0e22a292 100644 --- 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 @@ -29,9 +29,9 @@ declare_clippy_lint! { /// let y = "hello"; /// x.expect(&format!("{y:?}")); /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.85.0"] pub LITERAL_STRING_WITH_FORMATTING_ARGS, - suspicious, + nursery, "Checks if string literals have formatting arguments" } diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index f3ca4a4a57152..c5e75af2303c6 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -830,7 +830,7 @@ impl Loops { for_kv_map::check(cx, pat, arg, body); mut_range_bound::check(cx, arg, body); single_element_loop::check(cx, pat, arg, body, expr); - same_item_push::check(cx, pat, arg, body, expr); + same_item_push::check(cx, pat, arg, body, expr, &self.msrv); manual_flatten::check(cx, pat, arg, body, span); manual_find::check(cx, pat, arg, body, span, expr); unused_enumerate_index::check(cx, pat, arg, body); diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 951ebc9caef04..c27e930c99a5e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -1,8 +1,9 @@ use super::SAME_ITEM_PUSH; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_to_local; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{msrvs, path_to_local, std_or_core}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -19,19 +20,30 @@ pub(super) fn check<'tcx>( _: &'tcx Expr<'_>, body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, + msrv: &Msrv, ) { - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext, msrv: &Msrv) { let mut app = Applicability::Unspecified; let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0; let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0; - span_lint_and_help( + let secondary_help = if msrv.meets(msrvs::REPEAT_N) + && let Some(std_or_core) = std_or_core(cx) + { + format!("or `{vec_str}.extend({std_or_core}::iter::repeat_n({item_str}, SIZE))`") + } else { + format!("or `{vec_str}.resize(NEW_SIZE, {item_str})`") + }; + + span_lint_and_then( cx, SAME_ITEM_PUSH, vec.span, - "it looks like the same item is being pushed into this Vec", - None, - format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), + "it looks like the same item is being pushed into this `Vec`", + |diag| { + diag.help(format!("consider using `vec![{item_str};SIZE]`")) + .help(secondary_help); + }, ); } @@ -67,11 +79,11 @@ pub(super) fn check<'tcx>( { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item, ctxt); + emit_lint(cx, vec, pushed_item, ctxt, msrv); } }, _ => {}, @@ -79,11 +91,11 @@ pub(super) fn check<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), _ => {}, } }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index d2a2321dae805..17e25635ce105 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_context; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; @@ -99,7 +99,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< && let QPath::Resolved(_, count_func_path) = count_func_qpath && let Some(segment_zero) = count_func_path.segments.first() && let Some(args) = segment_zero.args - && let Some(real_ty_span) = args.args.first().map(|arg| arg.span()) + && let Some(real_ty_span) = args.args.first().map(GenericArg::span) && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id) { diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index aa59b047b169a..816ca17b3d2d5 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -102,12 +102,48 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { { build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); } + + // (x + (Y - 1)) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, div_rhs) { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + } + + // ((Y - 1) + x) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, div_rhs) { + build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); + } + + // (x - (-Y - 1)) / Y + if inner_op.node == BinOpKind::Sub + && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = div_rhs.kind + && differ_by_one(abs_div_rhs, inner_rhs) + { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + } } } extract_msrv_attr!(LateContext); } +/// Checks if two expressions represent non-zero integer literals such that `small_expr + 1 == +/// large_expr`. +fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(small) = small_expr.kind + && let ExprKind::Lit(large) = large_expr.kind + && let LitKind::Int(s, _) = small.node + && let LitKind::Int(l, _) = large.node + { + Some(l.get()) == s.get().checked_add(1) + } else if let ExprKind::Unary(UnOp::Neg, small_inner_expr) = small_expr.kind + && let ExprKind::Unary(UnOp::Neg, large_inner_expr) = large_expr.kind + { + differ_by_one(large_inner_expr, small_inner_expr) + } else { + false + } +} + fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.peel_refs().kind() { diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 9860deba84324..38106277a88fe 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -7,7 +7,7 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit}; +use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; @@ -202,8 +202,14 @@ fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { } fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange { - if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind - && let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind + if let PatExprKind::Lit { + lit: start_lit, + negated: false, + } = &start.kind + && let PatExprKind::Lit { + lit: end_lit, + negated: false, + } = &end.kind { check_lit_range(start_lit, end_lit) } else { diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index a70955a7c78da..8503dde3fb6b4 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -106,7 +106,7 @@ impl<'tcx> QuestionMark { emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body); }, } - }; + } } } @@ -295,7 +295,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) ) { return; - }; + } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index ebfd946b07ee0..8aeec89f0bfa7 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { } }, _ => return, - }; + } let mut app = Applicability::MachineApplicable; let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0; diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 79de41db3438b..d69384a2cb702 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { return; - }; + } if let Res::Local(hir_id) = target_res && let Some(used_mutably) = mutated_variables(then, cx) diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs index cfa054706d6bf..4cc43e427ec61 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -34,7 +34,7 @@ fn get_cond_expr<'tcx>( needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the * cond */ }); - }; + } None } @@ -45,7 +45,7 @@ fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> if block.stmts.is_empty() { return block.expr; } - }; + } None } @@ -68,14 +68,14 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) && path_to_local_id(arg, target); } - }; + } false } fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); - }; + } false } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs new file mode 100644 index 0000000000000..b1a555b91d1bc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -0,0 +1,135 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::option_arg_ty; +use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; +use rustc_ast::BindingMode; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, Path, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::Ty; +use rustc_span::symbol::Ident; + +use super::MANUAL_OK_ERR; + +pub(crate) fn check_if_let( + cx: &LateContext<'_>, + expr: &Expr<'_>, + let_pat: &Pat<'_>, + let_expr: &Expr<'_>, + if_then: &Expr<'_>, + else_expr: &Expr<'_>, +) { + if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr)) + && let Some((is_ok, ident)) = is_ok_or_err(cx, let_pat) + && is_some_ident(cx, if_then, ident, inner_expr_ty) + && is_none(cx, else_expr) + { + apply_lint(cx, expr, let_expr, is_ok); + } +} + +pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) { + if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr)) + && arms.len() == 2 + && arms.iter().all(|arm| arm.guard.is_none()) + && let Some((idx, is_ok)) = arms.iter().enumerate().find_map(|(arm_idx, arm)| { + // Check if the arm is a `Ok(x) => x` or `Err(x) => x` alternative. + // In this case, return its index and whether it uses `Ok` or `Err`. + if let Some((is_ok, ident)) = is_ok_or_err(cx, arm.pat) + && is_some_ident(cx, arm.body, ident, inner_expr_ty) + { + Some((arm_idx, is_ok)) + } else { + None + } + }) + // Accept wildcard only as the second arm + && is_variant_or_wildcard(cx, arms[1-idx].pat, idx == 0, is_ok) + // Check that the body of the non `Ok`/`Err` arm is `None` + && is_none(cx, arms[1 - idx].body) + { + apply_lint(cx, expr, scrutinee, is_ok); + } +} + +/// Check that `pat` applied to a `Result` only matches `Ok(_)`, `Err(_)`, not a subset or a +/// superset of it. If `can_be_wild` is `true`, wildcards are also accepted. In the case of +/// a non-wildcard, `must_match_err` indicates whether the `Err` or the `Ok` variant should be +/// accepted. +fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool, must_match_err: bool) -> bool { + match pat.kind { + PatKind::Wild | PatKind::Path(..) | PatKind::Binding(_, _, _, None) if can_be_wild => true, + PatKind::TupleStruct(qpath, ..) => { + is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err + }, + PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { + is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) + }, + _ => false, + } +} + +/// Return `Some((true, IDENT))` if `pat` contains `Ok(IDENT)`, `Some((false, IDENT))` if it +/// contains `Err(IDENT)`, `None` otherwise. +fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'hir Ident)> { + if let PatKind::TupleStruct(qpath, [arg], _) = &pat.kind + && let PatKind::Binding(BindingMode::NONE, _, ident, _) = &arg.kind + && let res = cx.qpath_res(qpath, pat.hir_id) + && let Res::Def(DefKind::Ctor(..), id) = res + && let id @ Some(_) = cx.tcx.opt_parent(id) + { + let lang_items = cx.tcx.lang_items(); + if id == lang_items.result_ok_variant() { + return Some((true, ident)); + } else if id == lang_items.result_err_variant() { + return Some((false, ident)); + } + } + None +} + +/// Check if `expr` contains `Some(ident)`, possibly as a block +fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { + if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind + && is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome) + && cx.typeck_results().expr_ty(body_arg) == ty + && let ExprKind::Path(QPath::Resolved( + _, + Path { + segments: [segment], .. + }, + )) = body_arg.kind + { + segment.ident.name == ident.name + } else { + false + } +} + +/// Check if `expr` is `None`, possibly as a block +fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) +} + +/// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or +/// `err`, depending on `is_ok`. +fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok: bool) { + let method = if is_ok { "ok" } else { "err" }; + let mut app = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par(); + span_lint_and_sugg( + cx, + MANUAL_OK_ERR, + expr.span, + format!("manual implementation of `{method}`"), + "replace with", + format!("{scrut}.{method}()"), + app, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index bac5cf88cfbf8..0b57740064c19 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -109,7 +109,7 @@ where } }, None => return None, - }; + } let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs index 7e43d222a6623..b90cf6357c5c0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_unit_expr; -use clippy_utils::source::{expr_block, snippet}; +use clippy_utils::source::expr_block; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -17,17 +17,28 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] cx, MATCH_BOOL, expr.span, - "you seem to be trying to match on a boolean expression", + "`match` on a boolean expression", move |diag| { if arms.len() == 2 { - // no guards - let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + let mut app = Applicability::MachineApplicable; + let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + let test = Sugg::hir_with_applicability(cx, scrutinee, "_", &mut app); if let PatExprKind::Lit { lit, .. } = arm_bool.kind { - match lit.node { - LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), - LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), + match &lit.node { + LitKind::Bool(true) => Some(test), + LitKind::Bool(false) => Some(!test), _ => None, } + .map(|test| { + if let Some(guard) = &arms[0] + .guard + .map(|g| Sugg::hir_with_applicability(cx, g, "_", &mut app)) + { + test.and(guard) + } else { + test + } + }) } else { None } @@ -35,39 +46,31 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] None }; - if let Some((true_expr, false_expr)) = exprs { - let mut app = Applicability::HasPlaceholders; + if let Some(test_sugg) = test_sugg { let ctxt = expr.span.ctxt(); + let (true_expr, false_expr) = (arms[0].body, arms[1].body); let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { (false, false) => Some(format!( "if {} {} else {}", - snippet(cx, scrutinee.span, "b"), + test_sugg, expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app), expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) )), (false, true) => Some(format!( "if {} {}", - snippet(cx, scrutinee.span, "b"), + test_sugg, expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app) )), - (true, false) => { - let test = Sugg::hir(cx, scrutinee, ".."); - Some(format!( - "if {} {}", - !test, - expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) - )) - }, + (true, false) => Some(format!( + "if {} {}", + !test_sugg, + expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) + )), (true, true) => None, }; if let Some(sugg) = sugg { - diag.span_suggestion( - expr.span, - "consider using an `if`/`else` expression", - sugg, - Applicability::HasPlaceholders, - ); + diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 223d0dc76569b..d697f427c7052 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -117,7 +117,7 @@ where if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; } - }; + } span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 9f5b7c855a139..df1b83cbb516a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 91e40e4275c07..595655600890b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -170,7 +170,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ); }); }, - }; + } } enum CommonPrefixSearcher<'a> { diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index ac1eae07eff63..a7fdd483c16cd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -2,6 +2,7 @@ mod collapsible_match; mod infallible_destructuring_match; mod manual_filter; mod manual_map; +mod manual_ok_err; mod manual_unwrap_or; mod manual_utils; mod match_as_ref; @@ -972,6 +973,40 @@ declare_clippy_lint! { "checks for unnecessary guards in match expressions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for manual implementation of `.ok()` or `.err()` + /// on `Result` values. + /// + /// ### Why is this bad? + /// Using `.ok()` or `.err()` rather than a `match` or + /// `if let` is less complex and more readable. + /// + /// ### Example + /// ```no_run + /// # fn func() -> Result { Ok(0) } + /// let a = match func() { + /// Ok(v) => Some(v), + /// Err(_) => None, + /// }; + /// let b = if let Err(v) = func() { + /// Some(v) + /// } else { + /// None + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// # fn func() -> Result { Ok(0) } + /// let a = func().ok(); + /// let b = func().err(); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_OK_ERR, + complexity, + "find manual implementations of `.ok()` or `.err()` on `Result`" +} + pub struct Matches { msrv: Msrv, infallible_destructuring_match_linted: bool, @@ -1013,6 +1048,7 @@ impl_lint_pass!(Matches => [ MANUAL_MAP, MANUAL_FILTER, REDUNDANT_GUARDS, + MANUAL_OK_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -1091,6 +1127,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { manual_unwrap_or::check_match(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); manual_filter::check_match(cx, ex, arms, expr); + manual_ok_err::check_match(cx, expr, ex, arms); } if self.infallible_destructuring_match_linted { @@ -1134,6 +1171,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_let.if_then, else_expr, ); + manual_ok_err::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 2ce6a8a85a5eb..35f2e780d2e29 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use crate::FxHashSet; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::source::{first_line_of_span, indent_of, snippet}; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{get_attr, is_lint_allowed}; use itertools::Itertools; @@ -152,7 +152,7 @@ fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: & diag.multipart_suggestion( suggestion_message, vec![ - (expr.span.shrink_to_lo(), replacement), + (first_line_of_span(cx, expr.span).shrink_to_lo(), replacement), (found.found_span, scrutinee_replacement), ], Applicability::MaybeIncorrect, @@ -441,8 +441,9 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> { let parent_expr_before = self.parent_expr.replace(ex); match ex.kind { - // Skip blocks because values in blocks will be dropped as usual. - ExprKind::Block(..) => (), + // Skip blocks because values in blocks will be dropped as usual, and await + // desugaring because temporary insides the future will have been dropped. + ExprKind::Block(..) | ExprKind::Match(_, _, MatchSource::AwaitDesugar) => (), _ => walk_expr(self, ex), } diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs index 6c02207af49df..ff7769af1df41 100644 --- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine err_ty = ty; } else { return; - }; + } span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs index 390ba889fd2e6..b75d1ab9a7aa3 100644 --- a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs @@ -13,7 +13,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { && has_non_exhaustive_attr(cx.tcx, *adt_def) { return; - }; + } for arm in arms { if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs index 4a2124c74a882..687272e550bb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs @@ -62,5 +62,5 @@ pub(super) fn check<'tcx>( ), applicability, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs index 34159f2d150ea..a9f6a41c2357d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs @@ -32,5 +32,5 @@ pub(super) fn check<'tcx>( ), applicability, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index a82abc79f2a24..de22514c37c66 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -46,5 +46,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E format!("{receiver}.as_bytes().get({n}).copied()"), applicability, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs index 2a0a9d3710dca..223a960b800e7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, // &T where T: Copy ty::Ref(_, ty, _) if is_copy(cx, *ty) => {}, _ => return, - }; + } span_lint_and_sugg( cx, CLONED_INSTEAD_OF_COPIED, diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 10360b4817bc9..cbf713a3b17c4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -1,8 +1,8 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_range_full; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{is_range_full, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; use rustc_lint::LateContext; @@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re .then_some("Vec") .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String")) .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs)) + && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); let sugg = if let ty::Ref(..) = recv_ty.kind() { - format!("std::mem::take({recv})") + format!("{exec_context}::mem::take({recv})") } else { - format!("std::mem::take(&mut {recv})") + format!("{exec_context}::mem::take(&mut {recv})") }; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs index 44b55570eead2..f2786efa44cb6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -37,7 +37,7 @@ pub(super) fn check( "expect_err".to_string(), Applicability::MachineApplicable, ); - }; + } } /// Given a `Result` type, return its data (`T`). diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 6dc48c26ba93b..daa6e0e7f940c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>( if ty.is_str() && can_be_static_str(cx, arg) { return false; } - }; + } true } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 163058713377b..aa45969c8982b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span "into_iter()".to_string(), Applicability::MaybeIncorrect, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs b/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs new file mode 100644 index 0000000000000..6e09bf132aa14 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs @@ -0,0 +1,43 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MANUAL_REPEAT_N; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + repeat_expr: &Expr<'_>, + take_arg: &Expr<'_>, + msrv: &Msrv, +) { + if msrv.meets(msrvs::REPEAT_N) + && !expr.span.from_expansion() + && is_trait_method(cx, expr, sym::Iterator) + && let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind + && let Some(def_id) = fn_def_id(cx, repeat_expr) + && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id) + && !expr_use_ctxt(cx, expr).is_ty_unified + && let Some(std_or_core) = std_or_core(cx) + { + let mut app = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MANUAL_REPEAT_N, + expr.span, + "this `repeat().take()` can be written more concisely", + "consider using `repeat_n()` instead", + format!( + "{std_or_core}::iter::repeat_n({}, {})", + snippet_with_context(cx, repeat_arg.span, expr.span.ctxt(), "..", &mut app).0, + snippet(cx, take_arg.span, "..") + ), + app, + ); + } +} 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 1ebb71e251aba..78656ace831d5 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 @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{eager_or_lazy, higher, usage}; +use clippy_utils::{eager_or_lazy, higher, std_or_core, usage}; use rustc_ast::LitKind; use rustc_ast::ast::RangeLimits; use rustc_data_structures::packed::Pu128; @@ -75,6 +75,7 @@ pub(super) fn check( } = body_hir && !usage::BindingUsageFinder::are_params_used(cx, body_hir) && let Some(count) = extract_count_with_applicability(cx, range, &mut applicability) + && let Some(exec_context) = std_or_core(cx) { let method_to_use_name; let new_span; @@ -105,7 +106,7 @@ pub(super) fn check( let mut parts = vec![ ( receiver.span.to(method_call_span), - format!("std::iter::{method_to_use_name}"), + format!("{exec_context}::iter::{method_to_use_name}"), ), new_span, ]; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 3965c4d40878d..42418318fda8d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -58,6 +58,7 @@ mod manual_inspect; mod manual_is_variant_and; mod manual_next_back; mod manual_ok_or; +mod manual_repeat_n; mod manual_saturating_arithmetic; mod manual_str_repeat; mod manual_try_fold; @@ -101,6 +102,7 @@ mod single_char_add_str; mod single_char_insert_string; mod single_char_push_string; mod skip_while_next; +mod sliced_string_as_bytes; mod stable_sort_primitive; mod str_split; mod str_splitn; @@ -130,6 +132,7 @@ mod unnecessary_to_owned; mod unused_enumerate_index; mod unwrap_expect_used; mod useless_asref; +mod useless_nonzero_new_unchecked; mod utils; mod vec_resize_to_zero; mod verbose_file_reads; @@ -2416,14 +2419,14 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `.then_some(..).unwrap_or(..)` + /// Checks for unnecessary method chains that can be simplified into `if .. else ..`. /// /// ### Why is this bad? /// This can be written more clearly with `if .. else ..` /// /// ### Limitations /// This lint currently only looks for usages of - /// `.then_some(..).unwrap_or(..)`, but will be expanded + /// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded /// to account for similar patterns. /// /// ### Example @@ -4311,6 +4314,84 @@ declare_clippy_lint! { "using `Iterator::last` on a `DoubleEndedIterator`" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `NonZero*::new_unchecked()` being used in a `const` context. + /// + /// ### Why is this bad? + /// + /// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a + /// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical + /// runtime performances while not requiring `unsafe`. + /// + /// ### Example + /// ```no_run + /// use std::num::NonZeroUsize; + /// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + /// ``` + /// Use instead: + /// ```no_run + /// use std::num::NonZeroUsize; + /// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap(); + /// ``` + #[clippy::version = "1.86.0"] + pub USELESS_NONZERO_NEW_UNCHECKED, + complexity, + "using `NonZero::new_unchecked()` in a `const` context" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `repeat().take()` that can be replaced with `repeat_n()`. + /// + /// ### Why is this bad? + /// + /// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning. + /// + /// ### Example + /// ```no_run + /// let _ = std::iter::repeat(10).take(3); + /// ``` + /// Use instead: + /// ```no_run + /// let _ = std::iter::repeat_n(10, 3); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_REPEAT_N, + style, + "detect `repeat().take()` that can be replaced with `repeat_n()`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for string slices immediantly followed by `as_bytes`. + /// + /// ### Why is this bad? + /// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic. + /// + /// ### Known problems + /// In some cases, the UTF-8 validation and potential panic from string slicing may be required for + /// the code's correctness. If you need to ensure the slice boundaries fall on valid UTF-8 character + /// boundaries, the original form (`s[1..5].as_bytes()`) should be preferred. + /// + /// ### Example + /// ```rust + /// let s = "Lorem ipsum"; + /// s[1..5].as_bytes(); + /// ``` + /// Use instead: + /// ```rust + /// let s = "Lorem ipsum"; + /// &s.as_bytes()[1..5]; + /// ``` + #[clippy::version = "1.86.0"] + pub SLICED_STRING_AS_BYTES, + perf, + "slicing a string and immediately calling as_bytes is less efficient and can lead to panics" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4477,6 +4558,9 @@ impl_lint_pass!(Methods => [ MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, UNNECESSARY_MAP_OR, DOUBLE_ENDED_ITERATOR_LAST, + USELESS_NONZERO_NEW_UNCHECKED, + MANUAL_REPEAT_N, + SLICED_STRING_AS_BYTES, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4505,6 +4589,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { from_iter_instead_of_collect::check(cx, expr, args, func); unnecessary_fallible_conversions::check_function(cx, expr, func); manual_c_str_literals::check(cx, expr, func, args, &self.msrv); + useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv); }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; @@ -4743,6 +4828,7 @@ impl Methods { if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } + sliced_string_as_bytes::check(cx, expr, recv); }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv), @@ -4924,8 +5010,8 @@ impl Methods { }, ("is_empty", []) => { match method_call(recv) { - Some(("as_bytes", prev_recv, [], _, _)) => { - needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span); + Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span); }, Some(("as_str", recv, [], as_str_span, _)) => { redundant_as_str::check(cx, expr, recv, as_str_span, span); @@ -4962,8 +5048,8 @@ impl Methods { double_ended_iterator_last::check(cx, expr, recv, call_span); }, ("len", []) => { - if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { - needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span); + if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span); } }, ("lock", []) => { @@ -5015,7 +5101,7 @@ impl Methods { option_map_or_none::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map); option_map_or_err_ok::check(cx, expr, recv, def, map); - unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv); + unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv); }, ("map_or_else", [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); @@ -5146,6 +5232,7 @@ impl Methods { ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [arg]) => { iter_out_of_bounds::check_take(cx, expr, recv, arg); + manual_repeat_n::check(cx, expr, recv, arg, &self.msrv); if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, @@ -5220,8 +5307,8 @@ impl Methods { Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv); }, - Some(("then_some", t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); + Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method); }, _ => {}, } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs index 75e9f31723039..7c9f7bae99063 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs @@ -8,18 +8,16 @@ use rustc_span::Span; use super::NEEDLESS_AS_BYTES; -pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) { - if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() - && let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs() - && (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str()) - { +pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) { + let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); + if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); span_lint_and_sugg( cx, NEEDLESS_AS_BYTES, span, - "needless call to `as_bytes()`", + format!("needless call to `{prev_method}`"), format!("`{method}()` can be called directly on strings"), format!("{sugg}.{method}()"), app, diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs index 697eab32a33b0..b71f79f848242 100644 --- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,8 +1,11 @@ use super::OBFUSCATED_IF_ELSE; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::ExprKind; use rustc_lint::LateContext; pub(super) fn check<'tcx>( @@ -11,19 +14,30 @@ pub(super) fn check<'tcx>( then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, + then_method_name: &str, ) { - // something.then_some(blah).unwrap_or(blah) - // ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr - let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { - let mut applicability = Applicability::MachineApplicable; + let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + + let if_then = match then_method_name { + "then" if let ExprKind::Closure(closure) = then_arg.kind => { + let body = cx.tcx.hir().body(closure.body); + snippet_with_applicability(cx, body.value.span, "..", &mut applicability) + }, + "then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + _ => String::new().into(), + }; + let sugg = format!( "if {} {{ {} }} else {{ {} }}", - snippet_with_applicability(cx, then_recv.span, "..", &mut applicability), - snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability), + if_then, snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability) ); @@ -31,8 +45,7 @@ pub(super) fn check<'tcx>( cx, OBFUSCATED_IF_ELSE, expr.span, - "use of `.then_some(..).unwrap_or(..)` can be written \ - more clearly with `if .. else ..`", + "this method chain can be written more clearly with `if .. else ..`", "try", sugg, applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs index febd7fd5cf2f4..b3811a335e1a0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs @@ -37,7 +37,7 @@ pub(super) fn check( let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#); } else { let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#); - }; + } span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs new file mode 100644 index 0000000000000..6d4cfdb34f319 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -0,0 +1,29 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_lang_item; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; +use rustc_lint::LateContext; + +use super::SLICED_STRING_AS_BYTES; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { + if let ExprKind::Index(indexed, index, _) = recv.kind + && is_range_literal(index) + && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() + && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) + { + let mut applicability = Applicability::MaybeIncorrect; + let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability); + let range = snippet_with_applicability(cx, index.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + SLICED_STRING_AS_BYTES, + expr.span, + "calling `as_bytes` after slicing a string", + "try", + format!("&{stringish}.as_bytes()[{range}]"), + applicability, + ); + } +} 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 671c189a98e66..c0e0156858811 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 @@ -87,7 +87,7 @@ pub fn check_for_loop_iter( // skip lint return true; } - }; + } // the lint should not be executed if no violation happens let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index b7dbebe60a42e..6dea1506d0e32 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -1,9 +1,8 @@ use std::borrow::Cow; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_opt; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use clippy_utils::visitors::is_local_used; @@ -12,7 +11,7 @@ use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Span, sym}; use super::UNNECESSARY_MAP_OR; @@ -42,13 +41,14 @@ pub(super) fn check<'a>( recv: &Expr<'_>, def: &Expr<'_>, map: &Expr<'_>, + method_span: Span, msrv: &Msrv, ) { let ExprKind::Lit(def_kind) = def.kind else { return; }; - let recv_ty = cx.typeck_results().expr_ty(recv); + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let Bool(def_bool) = def_kind.node else { return; @@ -60,6 +60,8 @@ pub(super) fn check<'a>( Some(_) | None => return, }; + let ext_def_span = def.span.until(map.span); + let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind && let closure_body = cx.tcx.hir().body(map_closure.body) && let closure_body_value = closure_body.value.peel_blocks() @@ -114,26 +116,17 @@ pub(super) fn check<'a>( } .into_string(); - (sugg, "a standard comparison", app) - } else if !def_bool - && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) - && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) - && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite()) - { + (vec![(expr.span, sugg)], "a standard comparison", app) + } else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) { let suggested_name = variant.method_name(); ( - format!("{recv_callsite}.{suggested_name}({span_callsite})",), + vec![(method_span, suggested_name.into()), (ext_def_span, String::default())], suggested_name, Applicability::MachineApplicable, ) - } else if def_bool - && matches!(variant, Variant::Some) - && msrv.meets(msrvs::IS_NONE_OR) - && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) - && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite()) - { + } else if def_bool && matches!(variant, Variant::Some) && msrv.meets(msrvs::IS_NONE_OR) { ( - format!("{recv_callsite}.is_none_or({span_callsite})"), + vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())], "is_none_or", Applicability::MachineApplicable, ) @@ -145,13 +138,13 @@ pub(super) fn check<'a>( return; } - span_lint_and_sugg( + span_lint_and_then( cx, UNNECESSARY_MAP_OR, expr.span, "this `map_or` can be simplified", - format!("use {method} instead"), - sugg, - applicability, + |diag| { + diag.multipart_suggestion_verbose(format!("use {method} instead"), sugg, applicability); + }, ); } 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 9a45b04d1a624..f0b29213e1e58 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 @@ -43,8 +43,7 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident)) }, // The two exprs are method calls. - // Check to see that the function is the same and the arguments are mirrored - // This is enough because the receiver of the method is listed in the arguments + // Check to see that the function is the same and the arguments and receivers are mirrored ( ExprKind::MethodCall(left_segment, left_receiver, left_args, _), ExprKind::MethodCall(right_segment, right_receiver, right_args, _), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 964f1603f0e51..7d72310c1c442 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -217,10 +217,13 @@ fn check_into_iter_call_arg( && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. + && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) { if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { "copied" } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs new file mode 100644 index 0000000000000..0bd50429c09db --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -0,0 +1,59 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::is_inside_always_const_context; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::USELESS_NONZERO_NEW_UNCHECKED; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'tcx>, args: &[Expr<'_>], msrv: &Msrv) { + if msrv.meets(msrvs::CONST_UNWRAP) + && let ExprKind::Path(QPath::TypeRelative(ty, segment)) = func.kind + && segment.ident.name == sym::new_unchecked + && let [init_arg] = args + && is_inside_always_const_context(cx.tcx, expr.hir_id) + && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) + { + let mut app = Applicability::MachineApplicable; + let ty_str = snippet_with_applicability(cx, ty.span, "_", &mut app); + let msg = format!("`{ty_str}::new()` and `Option::unwrap()` can be safely used in a `const` context"); + let sugg = format!( + "{ty_str}::new({}).unwrap()", + snippet_with_applicability(cx, init_arg.span, "_", &mut app) + ); + + if let Node::Block(Block { + stmts: [], + span: block_span, + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }) = cx.tcx.parent_hir_node(expr.hir_id) + { + if !block_span.from_expansion() { + // The expression is the only component of an `unsafe` block. Propose + // to replace the block altogether. + span_lint_and_sugg( + cx, + USELESS_NONZERO_NEW_UNCHECKED, + *block_span, + msg, + "use instead", + sugg, + app, + ); + } + } else { + // The expression is enclosed in a larger `unsafe` context. Indicate that + // this may no longer be needed for the fixed expression. + span_lint_and_then(cx, USELESS_NONZERO_NEW_UNCHECKED, expr.span, msg, |diagnostic| { + diagnostic + .span_suggestion(expr.span, "use instead", sugg, app) + .note("the fixed expression does not require an `unsafe` context"); + }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index b856c929cf67b..b511b1e46b386 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -214,11 +214,12 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { ); }, ); - }; + } if let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::Binary(ref binop, a, b) = expr.kind - && (binop.node == BinOpKind::And || binop.node == BinOpKind::Or) - && let Some(sugg) = Sugg::hir_opt(cx, a) + && let ExprKind::Binary(binop, a, b) = &expr.kind + && matches!(binop.node, BinOpKind::And | BinOpKind::Or) + && !stmt.span.from_expansion() + && expr.span.eq_ctxt(stmt.span) { span_lint_hir_and_then( cx, @@ -227,16 +228,14 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { stmt.span, "boolean short circuit operator in statement may be clearer using an explicit test", |diag| { - let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; - diag.span_suggestion( - stmt.span, - "replace it with", - format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),), - Applicability::MachineApplicable, // snippet - ); + let mut app = Applicability::MachineApplicable; + let test = Sugg::hir_with_context(cx, a, expr.span.ctxt(), "_", &mut app); + let test = if binop.node == BinOpKind::Or { !test } else { test }; + let then = Sugg::hir_with_context(cx, b, expr.span.ctxt(), "_", &mut app); + diag.span_suggestion(stmt.span, "replace it with", format!("if {test} {{ {then}; }}"), app); }, ); - }; + } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs index 748289454bea5..d52fe7e7d5b9c 100644 --- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { }) => impl_params.push((path.segments[0].ident.to_string(), path.span)), GenericArg::Type(_) => return, _ => (), - }; + } } // find the type that the Impl is for diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 29dcbaa9e62a3..06e92985e6648 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::Impl { .. } | hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span), - }; + } let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index f28b431ab997e..e9ec23b1efa61 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { // this prevents ICEs such as when self is a type parameter or a primitive type // (see #10887, #11063) && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res - && cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug]) + && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id) // don't trigger if this impl was derived && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) && !item.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 05aa425de9edf..bba1b63be277f 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::Impl { .. } | hir::ItemKind::Use(..) => {}, - }; + } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/multi_assignments.rs b/src/tools/clippy/clippy_lints/src/multi_assignments.rs index 9a6b1dfc52b5c..4383f28717dcb 100644 --- a/src/tools/clippy/clippy_lints/src/multi_assignments.rs +++ b/src/tools/clippy/clippy_lints/src/multi_assignments.rs @@ -56,10 +56,10 @@ impl EarlyLintPass for MultiAssignments { if let ExprKind::Assign(target, source, _) = &expr.kind { if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind { span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); - }; + } if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind { span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 79252bba74d72..aad6ae52a6db0 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -171,7 +171,7 @@ fn collect_unsafe_exprs<'tcx>( }, _ => {}, - }; + } Continue::<(), _>(Descend::Yes) }); diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 3884149645883..86c084423b713 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - }; + } } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index a67addea94866..4e19a2f409dd3 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -107,6 +107,10 @@ struct LocalAssign { impl LocalAssign { fn from_expr(expr: &Expr<'_>, span: Span) -> Option { + if expr.span.from_expansion() { + return None; + } + if let ExprKind::Assign(lhs, rhs, _) = expr.kind { if lhs.span.from_expansion() { return None; @@ -336,7 +340,7 @@ fn check<'tcx>( ); }, _ => {}, - }; + } Some(()) } diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs index 9ee4e49327775..2e2916c957daa 100644 --- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -37,7 +37,9 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]); impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind { + if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind + && !item.span.from_expansion() + { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut app = Applicability::MaybeIncorrect; let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app); diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 8409d179b0f58..147654675ec91 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -89,16 +89,6 @@ declare_clippy_lint! { /// /// The `const` value should be stored inside a `static` item. /// - /// ### Known problems - /// When an enum has variants with interior mutability, use of its non - /// interior mutable variants can generate false positives. See issue - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) - /// - /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issues - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) - /// /// ### Example /// ```no_run /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 0caa19cd84436..852c3885f5689 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { } }, _ => {}, - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs new file mode 100644 index 0000000000000..312610db0423c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -0,0 +1,306 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{def_path_def_ids, fn_def_id, path_def_id}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Lints when `once_cell::sync::Lazy` or `lazy_static!` are used to define a static variable, + /// and suggests replacing such cases with `std::sync::LazyLock` instead. + /// + /// Note: This lint will not trigger in crate with `no_std` context, or with MSRV < 1.80.0. It + /// also will not trigger on `once_cell::sync::Lazy` usage in crates which use other types + /// from `once_cell`, such as `once_cell::race::OnceBox`. + /// + /// ### Why restrict this? + /// - Reduces the need for an extra dependency + /// - Enforce convention of using standard library types when possible + /// + /// ### Example + /// ```ignore + /// lazy_static! { + /// static ref FOO: String = "foo".to_uppercase(); + /// } + /// static BAR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| "BAR".to_lowercase()); + /// ``` + /// Use instead: + /// ```ignore + /// static FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "FOO".to_lowercase()); + /// static BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "BAR".to_lowercase()); + /// ``` + #[clippy::version = "1.81.0"] + pub NON_STD_LAZY_STATICS, + pedantic, + "lazy static that could be replaced by `std::sync::LazyLock`" +} + +/// A list containing functions with corresponding replacements in `LazyLock`. +/// +/// Some functions could be replaced as well if we have replaced `Lazy` to `LazyLock`, +/// therefore after suggesting replace the type, we need to make sure the function calls can be +/// replaced, otherwise the suggestions cannot be applied thus the applicability should be +/// `Unspecified` or `MaybeIncorret`. +static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ + ("once_cell::sync::Lazy::force", Some("std::sync::LazyLock::force")), + ("once_cell::sync::Lazy::get", None), // `std::sync::LazyLock::get` is experimental + ("once_cell::sync::Lazy::new", Some("std::sync::LazyLock::new")), + // Note: `Lazy::{into_value, get_mut, force_mut}` are not in the list. + // Because the lint only checks for `static`s, and using these functions with statics + // will either be a hard error or triggers `static_mut_ref` that will be hard errors. + // But keep in mind that if somehow we decide to expand this lint to catch non-statics, + // add those functions into the list. +]; + +pub struct NonStdLazyStatic { + msrv: Msrv, + lazy_static_lazy_static: Vec, + once_cell_crate: Vec, + once_cell_sync_lazy: Vec, + once_cell_sync_lazy_new: Vec, + sugg_map: FxIndexMap>, + lazy_type_defs: FxIndexMap, + uses_other_once_cell_types: bool, +} + +impl NonStdLazyStatic { + #[must_use] + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + lazy_static_lazy_static: Vec::new(), + once_cell_crate: Vec::new(), + once_cell_sync_lazy: Vec::new(), + once_cell_sync_lazy_new: Vec::new(), + sugg_map: FxIndexMap::default(), + lazy_type_defs: FxIndexMap::default(), + uses_other_once_cell_types: false, + } + } +} + +impl_lint_pass!(NonStdLazyStatic => [NON_STD_LAZY_STATICS]); + +/// Return if current MSRV does not meet the requirement for `lazy_cell` feature, +/// or current context has `no_std` attribute. +macro_rules! ensure_prerequisite { + ($msrv:expr, $cx:ident) => { + if !$msrv.meets(clippy_utils::msrvs::LAZY_CELL) || clippy_utils::is_no_std_crate($cx) { + return; + } + }; +} + +impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { + extract_msrv_attr!(LateContext); + + fn check_crate(&mut self, cx: &LateContext<'hir>) { + // Do not lint if current crate does not support `LazyLock`. + ensure_prerequisite!(self.msrv, cx); + + // Fetch def_ids for external paths + self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect(); + self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect(); + self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect(); + // And CrateNums for `once_cell` crate + self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect(); + + // Convert hardcoded fn replacement list into a map with def_id + for (path, sugg) in FUNCTION_REPLACEMENTS { + let path_vec: Vec<&str> = path.split("::").collect(); + for did in def_path_def_ids(cx.tcx, &path_vec) { + self.sugg_map.insert(did, sugg.map(ToOwned::to_owned)); + } + } + } + + fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + if let ItemKind::Static(..) = item.kind + && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span) + && self.lazy_static_lazy_static.contains(¯o_call.def_id) + { + span_lint( + cx, + NON_STD_LAZY_STATICS, + macro_call.span, + "this macro has been superceded by `std::sync::LazyLock`", + ); + return; + } + + if in_external_macro(cx.sess(), item.span) { + return; + } + + if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) { + self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info); + } + } + + fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &Expr<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + // All functions in the `FUNCTION_REPLACEMENTS` have only one args + if let ExprKind::Call(callee, [arg]) = expr.kind + && let Some(call_def_id) = fn_def_id(cx, expr) + && self.sugg_map.contains_key(&call_def_id) + && let ExprKind::Path(qpath) = arg.peel_borrows().kind + && let Some(arg_def_id) = cx.typeck_results().qpath_res(&qpath, arg.hir_id).opt_def_id() + && let Some(lazy_info) = self.lazy_type_defs.get_mut(&arg_def_id) + { + lazy_info.calls_span_and_id.insert(callee.span, call_def_id); + } + } + + fn check_ty(&mut self, cx: &LateContext<'hir>, ty: &'hir rustc_hir::Ty<'hir, rustc_hir::AmbigArg>) { + ensure_prerequisite!(self.msrv, cx); + + // Record if types from `once_cell` besides `sync::Lazy` are used. + if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind + && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id() + // Is from `once_cell` crate + && self.once_cell_crate.contains(&ty_def_id.krate) + // And is NOT `once_cell::sync::Lazy` + && !self.once_cell_sync_lazy.contains(&ty_def_id) + { + self.uses_other_once_cell_types = true; + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + if !self.uses_other_once_cell_types { + for (_, lazy_info) in &self.lazy_type_defs { + lazy_info.lint(cx, &self.sugg_map); + } + } + } +} + +struct LazyInfo { + /// Span of the [`hir::Ty`] without including args. + /// i.e.: + /// ```ignore + /// static FOO: Lazy = Lazy::new(...); + /// // ^^^^ + /// ``` + ty_span_no_args: Span, + /// `Span` and `DefId` of calls on `Lazy` type. + /// i.e.: + /// ```ignore + /// static FOO: Lazy = Lazy::new(...); + /// // ^^^^^^^^^ + /// ``` + calls_span_and_id: FxIndexMap, +} + +impl LazyInfo { + fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option { + // Check if item is a `once_cell:sync::Lazy` static. + if let ItemKind::Static(ty, _, body_id) = item.kind + && let Some(path_def_id) = path_def_id(cx, ty) + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && state.once_cell_sync_lazy.contains(&path_def_id) + { + let ty_span_no_args = path_span_without_args(path); + let body = cx.tcx.hir().body(body_id); + + // visit body to collect `Lazy::new` calls + let mut new_fn_calls = FxIndexMap::default(); + for_each_expr::<(), ()>(cx, body, |ex| { + if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) + && state.once_cell_sync_lazy_new.contains(&fn_did) + { + new_fn_calls.insert(call_span, fn_did); + } + std::ops::ControlFlow::Continue(()) + }); + + Some(LazyInfo { + ty_span_no_args, + calls_span_and_id: new_fn_calls, + }) + } else { + None + } + } + + fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap>) { + // Applicability might get adjusted to `Unspecified` later if any calls + // in `calls_span_and_id` are not replaceable judging by the `sugg_map`. + let mut appl = Applicability::MachineApplicable; + let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())]; + + for (span, def_id) in &self.calls_span_and_id { + let maybe_sugg = sugg_map.get(def_id).cloned().flatten(); + if let Some(sugg) = maybe_sugg { + suggs.push((*span, sugg)); + } else { + // If NO suggested replacement, not machine applicable + appl = Applicability::Unspecified; + } + } + + span_lint_and_then( + cx, + NON_STD_LAZY_STATICS, + self.ty_span_no_args, + "this type has been superceded by `LazyLock` in the standard library", + |diag| { + diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); + }, + ); + } +} + +/// Return the span of a given `Path` without including any of its args. +/// +/// NB: Re-write of a private function `rustc_lint::non_local_def::path_span_without_args`. +fn path_span_without_args(path: &hir::Path<'_>) -> Span { + path.segments + .last() + .and_then(|seg| seg.args) + .map_or(path.span, |args| path.span.until(args.span_ext)) +} + +/// Returns the `DefId` and `Span` of the callee if the given expression is a function call. +/// +/// NB: Modified from [`clippy_utils::fn_def_id`], to support calling in an static `Item`'s body. +fn fn_def_id_and_span_from_body(cx: &LateContext<'_>, expr: &Expr<'_>, body_id: BodyId) -> Option<(DefId, Span)> { + // FIXME: find a way to cache the result. + let typeck = cx.tcx.typeck_body(body_id); + match &expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + hir_id: path_hir_id, + span, + .. + }, + .., + ) => { + // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or + // deref to fn pointers, dyn Fn, impl Fn - #8850 + if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = + typeck.qpath_res(qpath, *path_hir_id) + { + Some((id, *span)) + } else { + None + } + }, + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 0eca788c78746..9d07a14718da1 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -104,7 +104,7 @@ impl ArithmeticSideEffects { if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { return false; - }; + } let int_type = substs.type_at(0); let unsigned_int_types = [ @@ -214,13 +214,13 @@ impl ArithmeticSideEffects { | hir::BinOpKind::Sub ) { return; - }; + } let (mut actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); let (mut actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); actual_lhs = expr_or_init(cx, actual_lhs); actual_rhs = expr_or_init(cx, actual_rhs); let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs(); - let rhs_ty = cx.typeck_results().expr_ty(actual_rhs).peel_refs(); + let rhs_ty = cx.typeck_results().expr_ty_adjusted(actual_rhs).peel_refs(); if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } @@ -283,7 +283,7 @@ impl ArithmeticSideEffects { if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { return; } - let instance_ty = cx.typeck_results().expr_ty(receiver); + let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); if !Self::is_integral(instance_ty) { return; } @@ -311,7 +311,7 @@ impl ArithmeticSideEffects { if ConstEvalCtxt::new(cx).eval(un_expr).is_some() { return; } - let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs(); if self.has_allowed_unary(ty) { return; } diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index b0d872e98fd4a..cf6b8992973a7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -104,7 +104,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) } else { expr_snip = arg_snip.to_string(); eq_impl = without_deref; - }; + } let span; let hint; diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs index 1a0bfd8b99703..10455d3b93a00 100644 --- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs @@ -127,7 +127,7 @@ pub(super) fn check<'tcx>( None, note, ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs index d72a2fc3b1ab5..54f50f11e0349 100644 --- a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs @@ -49,5 +49,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr lint_double_comparison!(==); }, _ => (), - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 8272d3643d420..01dc6a27c33e3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -120,7 +120,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ty::Array(arr_ty, _) = value { return matches!(arr_ty.kind(), ty::Float(_)); - }; + } matches!(value, ty::Float(_)) } diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 9e8a821c3f4e8..d9845bc3b0f74 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -262,7 +262,7 @@ declare_clippy_lint! { /// to `trailing_zeros` /// /// ### Why is this bad? - /// `x.trailing_zeros() > 4` is much clearer than `x & 15 + /// `x.trailing_zeros() >= 4` is much clearer than `x & 15 /// == 0` /// /// ### Known problems @@ -278,7 +278,7 @@ declare_clippy_lint! { /// /// ```no_run /// # let x: i32 = 1; - /// if x.trailing_zeros() > 4 { } + /// if x.trailing_zeros() >= 4 { } /// ``` #[clippy::version = "pre 1.29.0"] pub VERBOSE_BIT_MASK, diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index c83bdda347a66..691d7b904eff5 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( } else { check_non_const_operands(cx, e, lhs); } - }; + } } fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs index 54eea14833ffe..fc5565e821edd 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs @@ -21,6 +21,6 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: "any number modulo -1 will panic/overflow or result in 0", ); } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 794bef7b32143..55676522419c6 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -53,6 +53,6 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { ); } } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index 031f09310590b..421b2b7475553 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -43,7 +43,7 @@ impl EarlyLintPass for Precedence { cx, PRECEDENCE, expr.span, - "operator precedence can trip the unwary", + "operator precedence might not be obvious", "consider parenthesizing your expression", sugg, appl, diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 41a44de536b15..b4dadef57a3c5 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // avoid clippy::double_parens if !is_in_fn_call_arg { hint = hint.maybe_par(); - }; + } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 1b557730ecade..8d6b1c7274d93 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; @@ -49,6 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) && is_not_macro_export(item) + && !in_external_macro(cx.sess(), item.span) { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs index 81556f3961416..7bd4d6e993b47 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs @@ -215,6 +215,6 @@ impl LateLintPass<'_> for RedundantTypeAnnotations { }, _ => (), } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs index f54cafffb83c9..5dddf9263a359 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; -use clippy_utils::{expr_or_init, fn_def_id}; +use clippy_utils::{expr_or_init, fn_def_id, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -93,6 +93,7 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { && let ExprKind::Call(_, [repeat_expr]) = expr.kind && fn_def_id(cx, repeat_expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::vec_with_capacity, did)) && !repeat_expr.span.from_expansion() + && let Some(exec_context) = std_or_core(cx) { emit_lint( cx, @@ -100,7 +101,10 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { "iter::repeat", "none of the yielded `Vec`s will have the requested capacity", "if you intended to create an iterator that yields `Vec`s with an initial capacity, try", - format!("std::iter::repeat_with(|| {})", snippet(cx, repeat_expr.span, "..")), + format!( + "{exec_context}::iter::repeat_with(|| {})", + snippet(cx, repeat_expr.span, "..") + ), ); } } diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index dfaee8cc3054d..664e984fece3d 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::{Descend, for_each_expr, for_each_unconsumed_temporary}; +use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{ - binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, - path_to_local_id, span_contains_cfg, span_find_starting_semi, + binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, + leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, + span_find_starting_semi, }; use core::ops::ControlFlow; use rustc_ast::MetaItemInner; @@ -389,22 +390,8 @@ fn check_final_expr<'tcx>( } }; - if let Some(inner) = inner { - if for_each_unconsumed_temporary(cx, inner, |temporary_ty| { - if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) - && temporary_ty - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_break() - { - return; - } + if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { + return; } if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { 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 9737b84cdb9c7..2d989b1cf0ba0 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 @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_trait_def_id; use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, is_no_std_crate}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; @@ -125,7 +125,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { span, format!("{suggested_type} of `Range` that is only one element"), |diag| { - if should_emit_every_value { + if should_emit_every_value && !is_no_std_crate(cx) { diag.span_suggestion( span, "if you wanted a `Vec` that contains the entire range, try", diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index db1c75fc3dea9..f72ff10dd43c4 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -97,7 +97,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( && let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next() { return Some((pointee_ty, count)); - }; + } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods && let method_ident = method_path.ident.as_str() @@ -108,7 +108,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx.typeck_results().expr_ty(ptr_self).kind() { return Some((*pointee_ty, count)); - }; + } None } @@ -130,6 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { && pointee_ty == ty_used_for_size_of { span_lint_and_help(cx, SIZE_OF_IN_ELEMENT_COUNT, count_expr.span, LINT_MSG, None, HELP_MSG); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index d2d693eaa1f38..d26288adb3919 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -3,6 +3,7 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, + span_contains_comment, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -190,7 +191,7 @@ impl SlowVectorInit { InitializationType::Extend(e) | InitializationType::Resize(e) => { Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization"); }, - }; + } } fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &'static str) { @@ -206,6 +207,14 @@ impl SlowVectorInit { let span_to_replace = slow_fill .span .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); + + // If there is no comment in `span_to_replace`, Clippy can automatically fix the code. + let app = if span_contains_comment(cx.tcx.sess.source_map(), span_to_replace) { + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; + span_lint_and_sugg( cx, SLOW_VECTOR_INITIALIZATION, @@ -213,7 +222,7 @@ impl SlowVectorInit { msg, "consider replacing this with", format!("vec![0; {len_expr}]"), - Applicability::Unspecified, + app, ); } } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index ff11680051232..9b4c3d275ae73 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -296,7 +296,7 @@ fn check_xor_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { { let span = s1.span.to(s3.span); generate_swap_warning(block, cx, lhs0, rhs0, rhs1, rhs2, span, true); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index 531422798a68c..bed4e60ba62da 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -50,10 +50,7 @@ pub(super) fn check<'tcx>( } let args = last.args; let missing_generic = match args { - Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| match arg { - GenericArg::Infer(_) => true, - _ => false, - }), + Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| matches!(arg, GenericArg::Infer(_))), _ => true, }; if !missing_generic { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 3729dfd3e86f2..f27aaa2fa77aa 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { return false; - }; + } let int_ty = substs.type_at(0); if from_ty != int_ty { diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 99a55f9fc357e..008e09dd8bd12 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -119,7 +119,7 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & && let LitKind::Int(val, _) = lit.node { return (val == i as u128).then_some(lhs); - }; + } None }) diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 1a5fdf0cd64f2..2e97772407fd5 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -71,7 +71,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m Applicability::Unspecified, ); return true; - }; + } false }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs new file mode 100644 index 0000000000000..efbc536dcb4d1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs @@ -0,0 +1,109 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::leaks_droppable_temporary_with_limited_lifetime; +use rustc_errors::Applicability; +use rustc_hir::{Block, ExprKind, HirId, MatchSource, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::edition::Edition::Edition2021; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the presence of a semicolon at the end of + /// a `match` or `if` statement evaluating to `()`. + /// + /// ### Why is this bad? + /// The semicolon is not needed, and may be removed to + /// avoid confusion and visual clutter. + /// + /// ### Example + /// ```no_run + /// # let a: u32 = 42; + /// if a > 10 { + /// println!("a is greater than 10"); + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// # let a: u32 = 42; + /// if a > 10 { + /// println!("a is greater than 10"); + /// } + /// ``` + #[clippy::version = "1.86.0"] + pub UNNECESSARY_SEMICOLON, + pedantic, + "unnecessary semicolon after expression returning `()`" +} + +#[derive(Default)] +pub struct UnnecessarySemicolon { + last_statements: Vec, +} + +impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); + +impl UnnecessarySemicolon { + /// Enter or leave a block, remembering the last statement of the block. + fn handle_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>, enter: bool) { + // Up to edition 2021, removing the semicolon of the last statement of a block + // may result in the scrutinee temporary values to live longer than the block + // variables. To avoid this problem, we do not lint the last statement of an + // expressionless block. + if cx.tcx.sess.edition() <= Edition2021 + && block.expr.is_none() + && let Some(last_stmt) = block.stmts.last() + { + if enter { + self.last_statements.push(last_stmt.hir_id); + } else { + self.last_statements.pop(); + } + } + } + + /// Checks if `stmt` is the last statement in an expressionless block for edition ≤ 2021. + fn is_last_in_block(&self, stmt: &Stmt<'_>) -> bool { + self.last_statements + .last() + .is_some_and(|last_stmt_id| last_stmt_id == &stmt.hir_id) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { + fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + self.handle_block(cx, block, true); + } + + fn check_block_post(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + self.handle_block(cx, block, false); + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) { + // rustfmt already takes care of removing semicolons at the end + // of loops. + if let StmtKind::Semi(expr) = stmt.kind + && !stmt.span.from_expansion() + && !expr.span.from_expansion() + && matches!( + expr.kind, + ExprKind::If(..) | ExprKind::Match(_, _, MatchSource::Normal | MatchSource::Postfix) + ) + && cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit + { + if self.is_last_in_block(stmt) && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { + return; + } + + let semi_span = expr.span.shrink_to_hi().to(stmt.span.shrink_to_hi()); + span_lint_and_sugg( + cx, + UNNECESSARY_SEMICOLON, + semi_span, + "unnecessary semicolon", + "remove", + String::new(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs new file mode 100644 index 0000000000000..40ba70d451dbd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs @@ -0,0 +1,76 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Pat, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for struct patterns that match against unit variant. + /// + /// ### Why is this bad? + /// Struct pattern `{ }` or `{ .. }` is not needed for unit variant. + /// + /// ### Example + /// ```no_run + /// match Some(42) { + /// Some(v) => v, + /// None { .. } => 0, + /// }; + /// // Or + /// match Some(42) { + /// Some(v) => v, + /// None { } => 0, + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// match Some(42) { + /// Some(v) => v, + /// None => 0, + /// }; + /// ``` + #[clippy::version = "1.83.0"] + pub UNNEEDED_STRUCT_PATTERN, + style, + "using struct pattern to match against unit variant" +} + +declare_lint_pass!(UnneededStructPattern => [UNNEEDED_STRUCT_PATTERN]); + +impl LateLintPass<'_> for UnneededStructPattern { + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { + if !pat.span.from_expansion() + && let PatKind::Struct(path, [], _) = &pat.kind + && let QPath::Resolved(_, path) = path + && let Res::Def(DefKind::Variant, did) = path.res + { + let enum_did = cx.tcx.parent(did); + let variant = cx.tcx.adt_def(enum_did).variant_with_id(did); + + let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty(); + let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive(); + if !has_only_fields_brackets || non_exhaustive_activated { + return; + } + + if is_from_proc_macro(cx, *path) { + return; + } + + if let Some(brackets_span) = pat.span.trim_start(path.span) { + span_lint_and_sugg( + cx, + UNNEEDED_STRUCT_PATTERN, + brackets_span, + "struct pattern is not needed for a unit variant", + "remove the struct pattern", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 7c9455bf8abef..e65123b8a9492 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -182,7 +182,7 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { emit_lint(cx, expr.span, expr.hir_id, op, &[]); }, _ => {}, - }; + } } fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option { diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 7ffab81a5444c..5e452c6d2ac09 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,5 +1,5 @@ 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::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ @@ -12,6 +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::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -251,26 +252,25 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // ^^^ let (into_iter_recv, depth) = into_iter_deep_call(cx, into_iter_recv); - let plural = if depth == 0 { "" } else { "s" }; - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability( - cx, - into_iter_recv.span.source_callsite(), - "", - &mut applicability, - ) - .into_owned(); span_lint_and_then( cx, USELESS_CONVERSION, e.span, "explicit call to `.into_iter()` in function argument accepting `IntoIterator`", |diag| { - diag.span_suggestion( - e.span, + let receiver_span = into_iter_recv.span.source_callsite(); + let adjustments = adjustments(cx, into_iter_recv); + let mut sugg = if adjustments.is_empty() { + vec![] + } else { + vec![(receiver_span.shrink_to_lo(), adjustments)] + }; + let plural = if depth == 0 { "" } else { "s" }; + sugg.push((e.span.with_lo(receiver_span.hi()), String::new())); + diag.multipart_suggestion( format!("consider removing the `.into_iter()`{plural}"), sugg, - applicability, + Applicability::MachineApplicable, ); diag.span_note(span, "this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`"); }, @@ -431,3 +431,16 @@ fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) } false } + +fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String { + let mut prefix = String::new(); + for adj in cx.typeck_results().expr_adjustments(expr) { + match adj.kind { + Adjust::Deref(_) => prefix = format!("*{prefix}"), + Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })) => prefix = format!("&mut {prefix}"), + Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)) => prefix = format!("&{prefix}"), + _ => {}, + } + } + prefix +} 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 49aad881994e9..b8bcb9b375601 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 @@ -69,6 +69,6 @@ impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons { ), applicability, ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 0730b561bc29e..03c667846b611 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { }; if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { return; - }; + } // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` let callsite = expr.span.parent_callsite().unwrap_or(expr.span); diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 7fa070cd226be..68b7e1592e2ea 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -3,7 +3,7 @@ name = "clippy_utils" # begin autogenerated version version = "0.1.86" # end autogenerated version -edition = "2021" +edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index c267b804124af..251e3dfe41bed 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-2025-01-09 +nightly-2025-01-28 ``` diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index cd6290ced3344..179d42a8b5dc0 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -400,7 +400,9 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), - TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => (Pat::Str("dyn"), Pat::Str("")), + TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { + (Pat::Str("dyn"), Pat::Str("")) + }, // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index d46beddf73124..a660623f41850 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -12,7 +12,9 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind}; +use rustc_hir::{ + BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, +}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -451,8 +453,8 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } else { Some(val) } - } - PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value), + }, + PatExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(*body).value), PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id), } } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 60be7e4a4d395..6bb876322f24f 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -475,7 +475,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; - }; + } }, ExprKind::Path(QPath::Resolved(_, path)) if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?) diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 9e11a57d1b301..e471dfb6ef191 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -88,7 +88,7 @@ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; -use std::iter::{once, repeat}; +use std::iter::{once, repeat_n}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; @@ -116,15 +116,15 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, Ty, TyCtxt, - TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, + TyCtxt, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span, sym}; use rustc_target::abi::Integer; -use visitors::Visitable; +use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; @@ -814,7 +814,7 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &' e = ep; }, _ => break e, - }; + } }; result.reverse(); (result, root) @@ -2045,7 +2045,7 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t { return Some(expr); } - }; + } None } @@ -3420,7 +3420,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St })) .join("::") } else { - repeat(String::from("super")).take(go_up_by).chain(path).join("::") + repeat_n(String::from("super"), go_up_by).chain(path).join("::") } } @@ -3465,3 +3465,20 @@ pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } false } + +/// Returns true if `expr` creates any temporary whose type references a non-static lifetime and has +/// a significant drop and does not consume it. +pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + for_each_unconsumed_temporary(cx, expr, |temporary_ty| { + if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) + && temporary_ty + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_break() +} diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 45beb146eb6ab..f4c730ef118b0 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -251,7 +251,7 @@ impl<'a> PanicExpn<'a> { // This has no argument if name == "panic_cold_explicit" { return Some(Self::Empty); - }; + } let [arg, rest @ ..] = args else { return None; diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 5eb9b3b8f227b..605764cef89fe 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -184,9 +184,10 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) - .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) - .into_results_cursor(mir); + let maybe_storage_live_result = + MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) + .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) + .into_results_cursor(mir); let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); vis.visit_body(mir); vis.into_map(cx, maybe_storage_live_result) diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 2169a5fdd63ba..d73cb7e35611c 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -18,10 +18,10 @@ 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,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 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,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } + 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,74,0 { REPR_RUST } diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index 2c49df9d807f7..bb2a628211000 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -125,7 +125,7 @@ impl<'a> NumericLiteral<'a> { integer = &digits[..exp_start]; } else { fraction = Some(&digits[integer.len() + 1..exp_start]); - }; + } exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 104ae154e3695..287bdc9a6fd6f 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -29,13 +29,14 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) let def_id = body.source.def_id(); for local in &body.local_decls { - check_ty(tcx, local.ty, local.source_info.span)?; + check_ty(tcx, local.ty, local.source_info.span, msrv)?; } // impl trait is gone in MIR, so check the return type manually check_ty( tcx, tcx.fn_sig(def_id).instantiate_identity().output().skip_binder(), body.local_decls.iter().next().unwrap().source_info.span, + msrv, )?; for bb in &*body.basic_blocks { @@ -51,7 +52,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) Ok(()) } -fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, msrv: &Msrv) -> McfResult { for arg in ty.walk() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -62,7 +63,7 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { }; match ty.kind() { - ty::Ref(_, _, hir::Mutability::Mut) => { + ty::Ref(_, _, hir::Mutability::Mut) if !msrv.meets(msrvs::CONST_MUT_REFS) => { return Err((span, "mutable references in const fn are unstable".into())); }, ty::Alias(ty::Opaque, ..) => return Err((span, "`impl Trait` in const fn is unstable".into())), diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 32e7c2bbf7cb6..f2bbdda705852 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -118,7 +118,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { return true; } - }; + } }, _ => (), } @@ -1341,3 +1341,14 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> _ => None, } } + +/// Check if `ty` is an `Option` and return its argument type if it is. +pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) => cx + .tcx + .is_diagnostic_item(sym::Option, adt.did()) + .then(|| args.type_at(0)), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index b5cec31ba9dab..3398ff8af2f5a 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -55,7 +55,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty { && let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id)) { receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id); - }; + } let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false); let rhs = if type_is_inferable_from_arguments(cx, expr) { meet( diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index dcc763a8abd77..99984c41714bd 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -2,12 +2,11 @@ use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; -use rustc_hir::{self as hir, AmbigArg}; 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, StructTailExpr, UnOp, UnsafeSource, + self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, + ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index b0e4e3e3e573b..55e588f5ec736 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -6,7 +6,7 @@ readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] -edition = "2021" +edition = "2024" publish = false default-run = "lintcheck" diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index bd4fcc5e337e9..af243f94274d0 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -107,7 +107,7 @@ impl LintcheckConfig { } else { std::thread::available_parallelism().map_or(1, NonZero::get) }; - }; + } for lint_name in &mut config.lint_filter { *lint_name = format!( diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 03e2a24f6f989..e88d9f427becd 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -145,7 +145,7 @@ impl Crate { assert_eq!(status.code(), Some(0)); return Vec::new(); - }; + } if !config.fix { cmd.arg("--message-format=json"); @@ -313,7 +313,7 @@ fn lintcheck(config: LintcheckConfig) { filter }) .collect_into(&mut lint_level_args); - }; + } let crates: Vec = crates .into_iter() diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index b1f0a82b1f475..c15d1fe6cd348 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-01-09" +channel = "nightly-2025-01-28" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/Cargo.toml b/src/tools/clippy/rustc_tools_util/Cargo.toml index b63632916ba15..cba0256394823 100644 --- a/src/tools/clippy/rustc_tools_util/Cargo.toml +++ b/src/tools/clippy/rustc_tools_util/Cargo.toml @@ -7,6 +7,6 @@ readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["rustc", "tool", "git", "version", "hash"] categories = ["development-tools"] -edition = "2018" +edition = "2024" [dependencies] diff --git a/src/tools/clippy/rustfmt.toml b/src/tools/clippy/rustfmt.toml index 4248f42f654b1..0dc6adce7bfce 100644 --- a/src/tools/clippy/rustfmt.toml +++ b/src/tools/clippy/rustfmt.toml @@ -2,8 +2,8 @@ max_width = 120 comment_width = 100 match_block_trailing_comma = true wrap_comments = true -edition = "2021" +edition = "2024" error_on_line_overflow = true imports_granularity = "Module" -version = "Two" +style_edition = "2024" ignore = ["tests/ui/crashes/ice-10912.rs"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 68edefd3095cf..8201f332d3333 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -223,7 +223,7 @@ pub fn main() { if !has_sysroot_arg(args) { args.extend(vec!["--sysroot".into(), sys_root]); } - }; + } }; // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index e2e4d92df79f9..d1b1a1d232329 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -300,7 +300,9 @@ fn run_ui_cargo(cx: &TestContext) { } fn main() { - set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + unsafe { + set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + } let cx = TestContext::new(); diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index 64eba5e0888a4..565dcd73f582d 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -60,7 +60,7 @@ fn explore_directory(dir: &Path) -> Vec { } }, _ => {}, - }; + } } } } diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index ff178924bd154..ae5d8ef1d0b59 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -13,5 +13,9 @@ note: rustc running on note: compiler flags: -Z ui-testing -Z deduplicate-diagnostics=no +query stack during panic: +#0 [early_lint_checks] perform lints prior to macro expansion +#1 [hir_crate] getting the crate HIR +... and 3 other queries... use `env RUST_BACKTRACE=1` to see the full query stack note: Clippy version: foo diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index 3f20407308516..f09106773c7ee 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -170,6 +170,7 @@ pub fn hard_coded_allowed() { let _ = Saturating(0u32) + Saturating(0u32); let _ = String::new() + ""; + let _ = String::new() + &String::new(); let _ = Wrapping(0u32) + Wrapping(0u32); let saturating: Saturating = Saturating(0u32); @@ -408,11 +409,14 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _n.wrapping_rem(_n); _n.wrapping_rem_euclid(_n); + _n.saturating_div(*Box::new(_n)); + // Unary _n = -_n; _n = -&_n; _custom = -_custom; _custom = -&_custom; + _ = -*Box::new(_n); } // Copied and pasted from the `integer_arithmetic` lint for comparison. @@ -534,4 +538,11 @@ pub fn issue_12318() { one.sub_assign(1); } +pub fn explicit_methods() { + use core::ops::Add; + let one: i32 = 1; + one.add(&one); + Box::new(one).add(one); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 78b1aca4b8a47..9b4cfb83fbb29 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -14,730 +14,760 @@ LL | let _ = 1f128 + 1f128; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:307:5 + --> tests/ui/arithmetic_side_effects.rs:173:13 + | +LL | let _ = String::new() + &String::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:308:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:308:5 + --> tests/ui/arithmetic_side_effects.rs:309:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:309:5 + --> tests/ui/arithmetic_side_effects.rs:310:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:310:5 + --> tests/ui/arithmetic_side_effects.rs:311:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:311:5 + --> tests/ui/arithmetic_side_effects.rs:312:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:312:5 + --> tests/ui/arithmetic_side_effects.rs:313:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:313:5 + --> tests/ui/arithmetic_side_effects.rs:314:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:314:5 + --> tests/ui/arithmetic_side_effects.rs:315:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:315:5 + --> tests/ui/arithmetic_side_effects.rs:316:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:316:5 + --> tests/ui/arithmetic_side_effects.rs:317:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:317:5 + --> tests/ui/arithmetic_side_effects.rs:318:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:318:5 + --> tests/ui/arithmetic_side_effects.rs:319:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:319:5 + --> tests/ui/arithmetic_side_effects.rs:320:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:320:5 + --> tests/ui/arithmetic_side_effects.rs:321:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:321:5 + --> tests/ui/arithmetic_side_effects.rs:322:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:322:5 + --> tests/ui/arithmetic_side_effects.rs:323:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:323:5 + --> tests/ui/arithmetic_side_effects.rs:324:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:324:5 + --> tests/ui/arithmetic_side_effects.rs:325:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:325:5 + --> tests/ui/arithmetic_side_effects.rs:326:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:326:5 + --> tests/ui/arithmetic_side_effects.rs:327:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:327:5 + --> tests/ui/arithmetic_side_effects.rs:328:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:328:5 + --> tests/ui/arithmetic_side_effects.rs:329:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:329:5 + --> tests/ui/arithmetic_side_effects.rs:330:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:330:5 + --> tests/ui/arithmetic_side_effects.rs:331:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:331:5 + --> tests/ui/arithmetic_side_effects.rs:332:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:332:5 + --> tests/ui/arithmetic_side_effects.rs:333:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:333:5 + --> tests/ui/arithmetic_side_effects.rs:334:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:334:5 + --> tests/ui/arithmetic_side_effects.rs:335:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:335:5 + --> tests/ui/arithmetic_side_effects.rs:336:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:336:5 + --> tests/ui/arithmetic_side_effects.rs:337:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:337:5 + --> tests/ui/arithmetic_side_effects.rs:338:5 | LL | _custom >>= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:338:5 + --> tests/ui/arithmetic_side_effects.rs:339:5 | LL | _custom >>= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:339:5 + --> tests/ui/arithmetic_side_effects.rs:340:5 | LL | _custom <<= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:340:5 + --> tests/ui/arithmetic_side_effects.rs:341:5 | LL | _custom <<= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:341:5 + --> tests/ui/arithmetic_side_effects.rs:342:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:342:5 + --> tests/ui/arithmetic_side_effects.rs:343:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:343:5 + --> tests/ui/arithmetic_side_effects.rs:344:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:344:5 + --> tests/ui/arithmetic_side_effects.rs:345:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:345:5 + --> tests/ui/arithmetic_side_effects.rs:346:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:346:5 + --> tests/ui/arithmetic_side_effects.rs:347:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:347:5 + --> tests/ui/arithmetic_side_effects.rs:348:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:348:5 + --> tests/ui/arithmetic_side_effects.rs:349:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:349:5 + --> tests/ui/arithmetic_side_effects.rs:350:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:350:5 + --> tests/ui/arithmetic_side_effects.rs:351:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:351:5 + --> tests/ui/arithmetic_side_effects.rs:352:5 | LL | _custom >>= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:352:5 + --> tests/ui/arithmetic_side_effects.rs:353:5 | LL | _custom >>= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:353:5 + --> tests/ui/arithmetic_side_effects.rs:354:5 | LL | _custom <<= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:354:5 + --> tests/ui/arithmetic_side_effects.rs:355:5 | LL | _custom <<= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:357:10 + --> tests/ui/arithmetic_side_effects.rs:358:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:358:10 + --> tests/ui/arithmetic_side_effects.rs:359:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:359:10 + --> tests/ui/arithmetic_side_effects.rs:360:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:360:10 + --> tests/ui/arithmetic_side_effects.rs:361:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:361:10 + --> tests/ui/arithmetic_side_effects.rs:362:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:362:10 + --> tests/ui/arithmetic_side_effects.rs:363:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:363:10 + --> tests/ui/arithmetic_side_effects.rs:364:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:364:10 + --> tests/ui/arithmetic_side_effects.rs:365:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:365:10 + --> tests/ui/arithmetic_side_effects.rs:366:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:366:10 + --> tests/ui/arithmetic_side_effects.rs:367:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:367:10 + --> tests/ui/arithmetic_side_effects.rs:368:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:368:10 + --> tests/ui/arithmetic_side_effects.rs:369:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:369:10 + --> tests/ui/arithmetic_side_effects.rs:370:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:370:10 + --> tests/ui/arithmetic_side_effects.rs:371:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:371:10 + --> tests/ui/arithmetic_side_effects.rs:372:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:372:10 + --> tests/ui/arithmetic_side_effects.rs:373:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:373:10 + --> tests/ui/arithmetic_side_effects.rs:374:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:374:10 + --> tests/ui/arithmetic_side_effects.rs:375:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:375:10 + --> tests/ui/arithmetic_side_effects.rs:376:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:376:15 + --> tests/ui/arithmetic_side_effects.rs:377:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:377:15 + --> tests/ui/arithmetic_side_effects.rs:378:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:378:15 + --> tests/ui/arithmetic_side_effects.rs:379:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:379:15 + --> tests/ui/arithmetic_side_effects.rs:380:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:380:15 + --> tests/ui/arithmetic_side_effects.rs:381:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:381:15 + --> tests/ui/arithmetic_side_effects.rs:382:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:382:15 + --> tests/ui/arithmetic_side_effects.rs:383:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:383:15 + --> tests/ui/arithmetic_side_effects.rs:384:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:384:15 + --> tests/ui/arithmetic_side_effects.rs:385:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:385:15 + --> tests/ui/arithmetic_side_effects.rs:386:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:386:15 + --> tests/ui/arithmetic_side_effects.rs:387:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:387:15 + --> tests/ui/arithmetic_side_effects.rs:388:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:388:15 + --> tests/ui/arithmetic_side_effects.rs:389:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:389:15 + --> tests/ui/arithmetic_side_effects.rs:390:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:390:15 + --> tests/ui/arithmetic_side_effects.rs:391:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:391:15 + --> tests/ui/arithmetic_side_effects.rs:392:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:392:15 + --> tests/ui/arithmetic_side_effects.rs:393:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:393:15 + --> tests/ui/arithmetic_side_effects.rs:394:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:394:15 + --> tests/ui/arithmetic_side_effects.rs:395:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:395:15 + --> tests/ui/arithmetic_side_effects.rs:396:15 | LL | _custom = _custom >> _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:396:15 + --> tests/ui/arithmetic_side_effects.rs:397:15 | LL | _custom = _custom >> &_custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:397:15 + --> tests/ui/arithmetic_side_effects.rs:398:15 | LL | _custom = Custom << _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:398:15 + --> tests/ui/arithmetic_side_effects.rs:399:15 | LL | _custom = &Custom << _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:401:23 + --> tests/ui/arithmetic_side_effects.rs:402:23 | LL | _n.saturating_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:402:21 + --> tests/ui/arithmetic_side_effects.rs:403:21 | LL | _n.wrapping_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:403:21 + --> tests/ui/arithmetic_side_effects.rs:404:21 | LL | _n.wrapping_rem(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:404:28 + --> tests/ui/arithmetic_side_effects.rs:405:28 | LL | _n.wrapping_rem_euclid(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:406:23 + --> tests/ui/arithmetic_side_effects.rs:407:23 | LL | _n.saturating_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:407:21 + --> tests/ui/arithmetic_side_effects.rs:408:21 | LL | _n.wrapping_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:408:21 + --> tests/ui/arithmetic_side_effects.rs:409:21 | LL | _n.wrapping_rem(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:409:28 + --> tests/ui/arithmetic_side_effects.rs:410:28 | LL | _n.wrapping_rem_euclid(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:412:10 + --> tests/ui/arithmetic_side_effects.rs:412:23 + | +LL | _n.saturating_div(*Box::new(_n)); + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:415:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:413:10 + --> tests/ui/arithmetic_side_effects.rs:416:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:414:15 + --> tests/ui/arithmetic_side_effects.rs:417:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:415:15 + --> tests/ui/arithmetic_side_effects.rs:418:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:424:5 + --> tests/ui/arithmetic_side_effects.rs:419:9 + | +LL | _ = -*Box::new(_n); + | ^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:428:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:425:5 + --> tests/ui/arithmetic_side_effects.rs:429:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:426:5 + --> tests/ui/arithmetic_side_effects.rs:430:5 | LL | 1 % i / 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:427:5 + --> tests/ui/arithmetic_side_effects.rs:431:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:428:5 + --> tests/ui/arithmetic_side_effects.rs:432:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:439:5 + --> tests/ui/arithmetic_side_effects.rs:443:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:440:5 + --> tests/ui/arithmetic_side_effects.rs:444:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:441:5 + --> tests/ui/arithmetic_side_effects.rs:445:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:443:5 + --> tests/ui/arithmetic_side_effects.rs:447:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:445:5 + --> tests/ui/arithmetic_side_effects.rs:449:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:446:5 + --> tests/ui/arithmetic_side_effects.rs:450:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:448:5 + --> tests/ui/arithmetic_side_effects.rs:452:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:450:5 + --> tests/ui/arithmetic_side_effects.rs:454:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:451:5 + --> tests/ui/arithmetic_side_effects.rs:455:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:461:5 + --> tests/ui/arithmetic_side_effects.rs:465:5 | LL | 10 / a | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:515:9 + --> tests/ui/arithmetic_side_effects.rs:519:9 | LL | x / maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:519:9 + --> tests/ui/arithmetic_side_effects.rs:523:9 | LL | x % maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:530:5 + --> tests/ui/arithmetic_side_effects.rs:534:5 | LL | one.add_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:534:5 + --> tests/ui/arithmetic_side_effects.rs:538:5 | LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 123 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:544:5 + | +LL | one.add(&one); + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:545:5 + | +LL | Box::new(one).add(one); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 128 previous errors diff --git a/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs b/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs index 420232f9f8d89..e3205193ccea0 100644 --- a/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs +++ b/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs @@ -6,3 +6,24 @@ pub enum ErrorKind { #[doc(hidden)] Uncategorized, } + +#[non_exhaustive] +pub enum ExtNonExhaustiveEnum { + Unit, + Tuple(i32), + Struct { field: i32 }, +} + +pub enum ExtNonExhaustiveVariant { + ExhaustiveUnit, + #[non_exhaustive] + Unit, + #[non_exhaustive] + Tuple(i32), + #[non_exhaustive] + StructNoField {}, + #[non_exhaustive] + Struct { + field: i32, + }, +} diff --git a/src/tools/clippy/tests/ui/bytes_nth.fixed b/src/tools/clippy/tests/ui/bytes_nth.fixed index 11deb2390fd49..da35fcb55e5a6 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.fixed +++ b/src/tools/clippy/tests/ui/bytes_nth.fixed @@ -1,4 +1,5 @@ #![allow(clippy::unnecessary_operation)] +#![allow(clippy::sliced_string_as_bytes)] #![warn(clippy::bytes_nth)] fn main() { diff --git a/src/tools/clippy/tests/ui/bytes_nth.rs b/src/tools/clippy/tests/ui/bytes_nth.rs index 62d9c7a5ea79b..5dbe84ecec8b7 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.rs +++ b/src/tools/clippy/tests/ui/bytes_nth.rs @@ -1,4 +1,5 @@ #![allow(clippy::unnecessary_operation)] +#![allow(clippy::sliced_string_as_bytes)] #![warn(clippy::bytes_nth)] fn main() { diff --git a/src/tools/clippy/tests/ui/bytes_nth.stderr b/src/tools/clippy/tests/ui/bytes_nth.stderr index c6f21576c3dbf..c5f341cb37f6c 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.stderr +++ b/src/tools/clippy/tests/ui/bytes_nth.stderr @@ -1,5 +1,5 @@ error: called `.bytes().nth()` on a `String` - --> tests/ui/bytes_nth.rs:6:13 + --> tests/ui/bytes_nth.rs:7:13 | LL | let _ = s.bytes().nth(3); | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3).copied()` @@ -8,13 +8,13 @@ LL | let _ = s.bytes().nth(3); = help: to override `-D warnings` add `#[allow(clippy::bytes_nth)]` error: called `.bytes().nth().unwrap()` on a `String` - --> tests/ui/bytes_nth.rs:7:14 + --> tests/ui/bytes_nth.rs:8:14 | LL | let _ = &s.bytes().nth(3).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.as_bytes()[3]` error: called `.bytes().nth()` on a `str` - --> tests/ui/bytes_nth.rs:8:13 + --> tests/ui/bytes_nth.rs:9:13 | LL | let _ = s[..].bytes().nth(3); | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3).copied()` diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.fixed b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed new file mode 100644 index 0000000000000..1d4c3dd9dcc47 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed @@ -0,0 +1,16 @@ +// Test for https://github.com/rust-lang/rust-clippy/issues/11230 +#![warn(clippy::explicit_iter_loop)] +#![warn(clippy::needless_collect)] + +// explicit_iter_loop +fn main() { + const A: &[for<'a> fn(&'a ())] = &[]; + for v in A {} +} + +// needless_collect +trait Helper<'a>: Iterator {} + +fn x(w: &mut dyn for<'a> Helper<'a>) { + w.next().is_none(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs index 94044e9435ed4..a16fb271497cf 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs @@ -1,6 +1,16 @@ // Test for https://github.com/rust-lang/rust-clippy/issues/11230 +#![warn(clippy::explicit_iter_loop)] +#![warn(clippy::needless_collect)] +// explicit_iter_loop fn main() { const A: &[for<'a> fn(&'a ())] = &[]; for v in A.iter() {} } + +// needless_collect +trait Helper<'a>: Iterator {} + +fn x(w: &mut dyn for<'a> Helper<'a>) { + w.collect::>().is_empty(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.stderr b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr new file mode 100644 index 0000000000000..7167d90e456e9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr @@ -0,0 +1,20 @@ +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> tests/ui/crashes/ice-11230.rs:8:14 + | +LL | for v in A.iter() {} + | ^^^^^^^^ help: to write this more concisely, try: `A` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` + +error: avoid using `collect()` when not needed + --> tests/ui/crashes/ice-11230.rs:15:7 + | +LL | w.collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.fixed b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed index ca5721cbb2bae..d996b1db08a63 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11422.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::ops::*; -fn gen() -> impl PartialOrd + Debug {} +fn r#gen() -> impl PartialOrd + Debug {} struct Bar {} trait Foo {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.rs b/src/tools/clippy/tests/ui/crashes/ice-11422.rs index 355ec2480bba4..eb89b7c38f43d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11422.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::ops::*; -fn gen() -> impl PartialOrd + PartialEq + Debug {} +fn r#gen() -> impl PartialOrd + PartialEq + Debug {} struct Bar {} trait Foo {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.stderr b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr index a340977f4699d..67944e4e6e80d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11422.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr @@ -1,15 +1,15 @@ error: this bound is already specified as the supertrait of `PartialOrd` - --> tests/ui/crashes/ice-11422.rs:6:31 + --> tests/ui/crashes/ice-11422.rs:6:33 | -LL | fn gen() -> impl PartialOrd + PartialEq + Debug {} - | ^^^^^^^^^ +LL | fn r#gen() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ | = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::implied_bounds_in_impls)]` help: try removing this bound | -LL - fn gen() -> impl PartialOrd + PartialEq + Debug {} -LL + fn gen() -> impl PartialOrd + Debug {} +LL - fn r#gen() -> impl PartialOrd + PartialEq + Debug {} +LL + fn r#gen() -> impl PartialOrd + Debug {} | error: aborting due to 1 previous error 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 0822cc7c6350a..8e2ed1bbd18d6 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed @@ -1,4 +1,5 @@ #![warn(clippy::doc_lazy_continuation)] +#![allow(clippy::doc_overindented_list_items)] /// 1. nest here /// lazy continuation 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 068de140e00a0..1da11d8fae26a 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs @@ -1,4 +1,5 @@ #![warn(clippy::doc_lazy_continuation)] +#![allow(clippy::doc_overindented_list_items)] /// 1. nest here /// lazy continuation diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr index b38f43b7555f1..cea6157119f75 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr @@ -1,5 +1,5 @@ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:4:5 + --> tests/ui/doc/doc_lazy_list.rs:5:5 | LL | /// lazy continuation | ^ @@ -13,7 +13,7 @@ LL | /// lazy continuation | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:9:5 + --> tests/ui/doc/doc_lazy_list.rs:10:5 | LL | /// lazy list continuations don't make warnings with this lint | ^ @@ -25,7 +25,7 @@ LL | /// lazy list continuations don't make warnings with this lint | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:11:5 + --> tests/ui/doc/doc_lazy_list.rs:12:5 | LL | /// because they don't have the | ^ @@ -37,7 +37,7 @@ LL | /// because they don't have the | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:16:5 + --> tests/ui/doc/doc_lazy_list.rs:17:5 | LL | /// lazy continuation | ^ @@ -49,7 +49,7 @@ LL | /// lazy continuation | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:21:5 + --> tests/ui/doc/doc_lazy_list.rs:22:5 | LL | /// lazy list continuations don't make warnings with this lint | ^ @@ -61,7 +61,7 @@ LL | /// lazy list continuations don't make warnings with this lint | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:23:5 + --> tests/ui/doc/doc_lazy_list.rs:24:5 | LL | /// because they don't have the | ^ @@ -73,7 +73,7 @@ LL | /// because they don't have the | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:28:5 + --> tests/ui/doc/doc_lazy_list.rs:29:5 | LL | /// lazy continuation | ^ @@ -85,7 +85,7 @@ LL | /// lazy continuation | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:33:5 + --> tests/ui/doc/doc_lazy_list.rs:34:5 | LL | /// this will warn on the lazy continuation | ^ @@ -97,7 +97,7 @@ LL | /// this will warn on the lazy continuation | ++++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:35:5 + --> tests/ui/doc/doc_lazy_list.rs:36:5 | LL | /// and so should this | ^^^^ @@ -109,7 +109,7 @@ LL | /// and so should this | ++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:56:5 + --> tests/ui/doc/doc_lazy_list.rs:57:5 | LL | /// 'protocol_descriptors': [ | ^ @@ -121,7 +121,7 @@ LL | /// 'protocol_descriptors': [ | + error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:75:5 + --> tests/ui/doc/doc_lazy_list.rs:76:5 | LL | /// ] | ^ diff --git a/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.fixed b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.fixed new file mode 100644 index 0000000000000..940cff48c1e9e --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.fixed @@ -0,0 +1,28 @@ +#![warn(clippy::doc_overindented_list_items)] + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn foo() {} + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn bar() {} + +#[rustfmt::skip] +/// * first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// * second list item +fn baz() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.rs b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.rs new file mode 100644 index 0000000000000..77f3ee8a64d42 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.rs @@ -0,0 +1,28 @@ +#![warn(clippy::doc_overindented_list_items)] + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn foo() {} + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn bar() {} + +#[rustfmt::skip] +/// * first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// * second list item +fn baz() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.stderr b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.stderr new file mode 100644 index 0000000000000..ff201ba5eb94a --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.stderr @@ -0,0 +1,41 @@ +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:5:5 + | +LL | /// overindented line + | ^^^^^^^ help: try using ` ` (2 spaces) + | + = note: `-D clippy::doc-overindented-list-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_overindented_list_items)]` + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:7:5 + | +LL | /// this is overindented line too + | ^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:14:7 + | +LL | /// overindented line + | ^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:16:7 + | +LL | /// this is overindented line too + | ^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:23:5 + | +LL | /// overindented line + | ^^^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:25:5 + | +LL | /// this is overindented line too + | ^^^^^ help: try using ` ` (2 spaces) + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.fixed b/src/tools/clippy/tests/ui/drain_collect_nostd.fixed new file mode 100644 index 0000000000000..a4ab2956f2a62 --- /dev/null +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::drain_collect)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn remove_all(v: &mut Vec) -> Vec { + core::mem::take(v) +} diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.rs b/src/tools/clippy/tests/ui/drain_collect_nostd.rs new file mode 100644 index 0000000000000..a8be1ce6bbd3b --- /dev/null +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.rs @@ -0,0 +1,8 @@ +#![warn(clippy::drain_collect)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn remove_all(v: &mut Vec) -> Vec { + v.drain(..).collect() +} diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr new file mode 100644 index 0000000000000..91b38932fee79 --- /dev/null +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr @@ -0,0 +1,11 @@ +error: you seem to be trying to move all elements into a new `Vec` + --> tests/ui/drain_collect_nostd.rs:7:5 + | +LL | v.drain(..).collect() + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)` + | + = note: `-D clippy::drain-collect` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::drain_collect)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/eta_nostd.fixed b/src/tools/clippy/tests/ui/eta_nostd.fixed new file mode 100644 index 0000000000000..23059c52b67f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/eta_nostd.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::redundant_closure)] +#![no_std] + +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_13895() { + let _: Option> = true.then(alloc::vec::Vec::new); +} diff --git a/src/tools/clippy/tests/ui/eta_nostd.rs b/src/tools/clippy/tests/ui/eta_nostd.rs new file mode 100644 index 0000000000000..ae44ac348c64f --- /dev/null +++ b/src/tools/clippy/tests/ui/eta_nostd.rs @@ -0,0 +1,10 @@ +#![warn(clippy::redundant_closure)] +#![no_std] + +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_13895() { + let _: Option> = true.then(|| vec![]); +} diff --git a/src/tools/clippy/tests/ui/eta_nostd.stderr b/src/tools/clippy/tests/ui/eta_nostd.stderr new file mode 100644 index 0000000000000..4dfef43efa476 --- /dev/null +++ b/src/tools/clippy/tests/ui/eta_nostd.stderr @@ -0,0 +1,11 @@ +error: redundant closure + --> tests/ui/eta_nostd.rs:9:40 + | +LL | let _: Option> = true.then(|| vec![]); + | ^^^^^^^^^ help: replace the closure with `Vec::new`: `alloc::vec::Vec::new` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed index c250162dfb8c0..67da45a348f97 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] #![allow(unused_imports)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs index 8adbb841c8ba2..423a7454bed7b 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] #![allow(unused_imports)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; diff --git a/src/tools/clippy/tests/ui/implicit_hasher.fixed b/src/tools/clippy/tests/ui/implicit_hasher.fixed index 2d6dc0274cf2a..971746ae95d73 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.fixed +++ b/src/tools/clippy/tests/ui/implicit_hasher.fixed @@ -70,7 +70,7 @@ pub fn map(map: &mut HashMap) {} pub fn set(set: &mut HashSet) {} #[inline_macros] -pub mod gen { +pub mod gen_ { use super::*; inline! { impl Foo for HashMap { diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs index 0a334357bd1b8..b34aa1f81374e 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.rs +++ b/src/tools/clippy/tests/ui/implicit_hasher.rs @@ -70,7 +70,7 @@ pub fn map(map: &mut HashMap) {} pub fn set(set: &mut HashSet) {} #[inline_macros] -pub mod gen { +pub mod gen_ { use super::*; inline! { impl Foo for HashMap { diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr index 48c6ebc209cf3..442f4789aacfe 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.stderr +++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr @@ -98,7 +98,7 @@ error: impl for `HashMap` should be generalized over different hashers LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ | - = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_mod_gen_` (in Nightly builds, run with -Z macro-backtrace for more info) help: add a type parameter for `BuildHasher` | LL ~ impl Foo for HashMap { diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index 1fb1df5b44253..f6eb5a9784acc 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -50,3 +50,14 @@ fn issue_13843() { let _ = 1_000_000_u32.div_ceil(6u32); } + +fn issue_13950() { + let x = 33u32; + let _ = x.div_ceil(8); + let _ = x.div_ceil(8); + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index 4f6d38f0d1450..2f063afe787dd 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -50,3 +50,14 @@ fn issue_13843() { let _ = (1_000_000 + 6u32 - 1) / 6u32; } + +fn issue_13950() { + let x = 33u32; + let _ = (x + 7) / 8; + let _ = (7 + x) / 8; + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index 3d87fe8e04090..0bac5d8ef1c12 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -85,5 +85,17 @@ error: manually reimplementing `div_ceil` LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` -error: aborting due to 14 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:56:13 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:57:13 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed index f32b78aa14d08..01c58151bc946 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed @@ -50,3 +50,14 @@ fn issue_13843() { let _ = 1_000_000_u32.div_ceil(6u32); } + +fn issue_13950() { + let x = 33u32; + let _ = x.div_ceil(8); + let _ = x.div_ceil(8); + + let y = -33i32; + let _ = y.div_ceil(-7); + let _ = y.div_ceil(-7); + let _ = y.div_ceil(-7); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs index 54d89fcbd4622..048ff401581f7 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs @@ -50,3 +50,14 @@ fn issue_13843() { let _ = (1_000_000 + 6u32 - 1) / 6u32; } + +fn issue_13950() { + let x = 33u32; + let _ = (x + 7) / 8; + let _ = (7 + x) / 8; + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr index c5e8c1a687cd8..807cfd82724e5 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr @@ -109,5 +109,35 @@ error: manually reimplementing `div_ceil` LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` -error: aborting due to 18 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:56:13 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:57:13 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:60:13 + | +LL | let _ = (y + -8) / -7; + | ^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:61:13 + | +LL | let _ = (-8 + y) / -7; + | ^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:62:13 + | +LL | let _ = (y - 8) / -7; + | ^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/manual_ok_err.fixed b/src/tools/clippy/tests/ui/manual_ok_err.fixed new file mode 100644 index 0000000000000..e7e0464c47877 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ok_err.fixed @@ -0,0 +1,91 @@ +#![warn(clippy::manual_ok_err)] + +fn funcall() -> Result { + todo!() +} + +fn main() { + let _ = funcall().ok(); + + let _ = funcall().ok(); + + let _ = funcall().err(); + + let _ = funcall().err(); + + let _ = funcall().ok(); + + let _ = funcall().err(); + + #[allow(clippy::redundant_pattern)] + let _ = funcall().ok(); + + struct S; + + impl std::ops::Neg for S { + type Output = Result; + + fn neg(self) -> Self::Output { + funcall() + } + } + + // Suggestion should be properly parenthesized + let _ = (-S).ok(); + + no_lint(); +} + +fn no_lint() { + let _ = match funcall() { + Ok(v) if v > 3 => Some(v), + _ => None, + }; + + let _ = match funcall() { + Err(_) => None, + Ok(3) => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + _ => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + Err(_) | Ok(3) => None, + Ok(v) => Some(v), + }; + + #[expect(clippy::redundant_pattern)] + let _ = match funcall() { + _v @ _ => None, + Ok(v) => Some(v), + }; + + // Content of `Option` and matching content of `Result` do + // not have the same type. + let _: Option<&dyn std::any::Any> = match Ok::<_, ()>(&1) { + Ok(v) => Some(v), + _ => None, + }; + + let _ = match Ok::<_, ()>(&1) { + _x => None, + Ok(v) => Some(v), + }; + + let _ = match Ok::<_, std::convert::Infallible>(1) { + Ok(3) => None, + Ok(v) => Some(v), + }; +} + +const fn cf(x: Result) -> Option { + // Do not lint in const code + match x { + Ok(v) => Some(v), + Err(_) => None, + } +} diff --git a/src/tools/clippy/tests/ui/manual_ok_err.rs b/src/tools/clippy/tests/ui/manual_ok_err.rs new file mode 100644 index 0000000000000..03ad773f47cf1 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ok_err.rs @@ -0,0 +1,125 @@ +#![warn(clippy::manual_ok_err)] + +fn funcall() -> Result { + todo!() +} + +fn main() { + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + Err(_) => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + _v => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Err(v) => Some(v), + Ok(_) => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Err(v) => Some(v), + _v => None, + }; + + let _ = if let Ok(v) = funcall() { + //~^ manual_ok_err + Some(v) + } else { + None + }; + + let _ = if let Err(v) = funcall() { + //~^ manual_ok_err + Some(v) + } else { + None + }; + + #[allow(clippy::redundant_pattern)] + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + _v @ _ => None, + }; + + struct S; + + impl std::ops::Neg for S { + type Output = Result; + + fn neg(self) -> Self::Output { + funcall() + } + } + + // Suggestion should be properly parenthesized + let _ = match -S { + //~^ manual_ok_err + Ok(v) => Some(v), + _ => None, + }; + + no_lint(); +} + +fn no_lint() { + let _ = match funcall() { + Ok(v) if v > 3 => Some(v), + _ => None, + }; + + let _ = match funcall() { + Err(_) => None, + Ok(3) => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + _ => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + Err(_) | Ok(3) => None, + Ok(v) => Some(v), + }; + + #[expect(clippy::redundant_pattern)] + let _ = match funcall() { + _v @ _ => None, + Ok(v) => Some(v), + }; + + // Content of `Option` and matching content of `Result` do + // not have the same type. + let _: Option<&dyn std::any::Any> = match Ok::<_, ()>(&1) { + Ok(v) => Some(v), + _ => None, + }; + + let _ = match Ok::<_, ()>(&1) { + _x => None, + Ok(v) => Some(v), + }; + + let _ = match Ok::<_, std::convert::Infallible>(1) { + Ok(3) => None, + Ok(v) => Some(v), + }; +} + +const fn cf(x: Result) -> Option { + // Do not lint in const code + match x { + Ok(v) => Some(v), + Err(_) => None, + } +} diff --git a/src/tools/clippy/tests/ui/manual_ok_err.stderr b/src/tools/clippy/tests/ui/manual_ok_err.stderr new file mode 100644 index 0000000000000..d0d5e2c81e96e --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ok_err.stderr @@ -0,0 +1,95 @@ +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:8:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | Err(_) => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + | + = note: `-D clippy::manual-ok-err` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_ok_err)]` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:14:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _v => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:20:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Err(v) => Some(v), +LL | | Ok(_) => None, +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:26:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Err(v) => Some(v), +LL | | _v => None, +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:32:13 + | +LL | let _ = if let Ok(v) = funcall() { + | _____________^ +LL | | +LL | | Some(v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:39:13 + | +LL | let _ = if let Err(v) = funcall() { + | _____________^ +LL | | +LL | | Some(v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:47:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _v @ _ => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:64:13 + | +LL | let _ = match -S { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _ => None, +LL | | }; + | |_____^ help: replace with: `(-S).ok()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_repeat_n.fixed b/src/tools/clippy/tests/ui/manual_repeat_n.fixed new file mode 100644 index 0000000000000..4235b02a89e39 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_repeat_n.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::manual_repeat_n)] + +use std::iter::repeat; + +fn main() { + let _ = std::iter::repeat_n(10, 3); + + let _ = std::iter::repeat_n(String::from("foo"), 4); + + for value in std::iter::repeat_n(5, 3) {} + + let _: Vec<_> = std::iter::repeat_n(String::from("bar"), 10).collect(); + + let _ = std::iter::repeat_n(vec![1, 2], 2); +} + +mod foo_lib { + pub fn iter() -> std::iter::Take> { + todo!() + } +} + +fn foo() { + let _ = match 1 { + 1 => foo_lib::iter(), + // Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`. + 2 => std::iter::repeat([1, 2].as_slice()).take(2), + _ => todo!(), + }; +} diff --git a/src/tools/clippy/tests/ui/manual_repeat_n.rs b/src/tools/clippy/tests/ui/manual_repeat_n.rs new file mode 100644 index 0000000000000..dbf9ac6a14afb --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_repeat_n.rs @@ -0,0 +1,30 @@ +#![warn(clippy::manual_repeat_n)] + +use std::iter::repeat; + +fn main() { + let _ = repeat(10).take(3); + + let _ = repeat(String::from("foo")).take(4); + + for value in std::iter::repeat(5).take(3) {} + + let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect(); + + let _ = repeat(vec![1, 2]).take(2); +} + +mod foo_lib { + pub fn iter() -> std::iter::Take> { + todo!() + } +} + +fn foo() { + let _ = match 1 { + 1 => foo_lib::iter(), + // Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`. + 2 => std::iter::repeat([1, 2].as_slice()).take(2), + _ => todo!(), + }; +} diff --git a/src/tools/clippy/tests/ui/manual_repeat_n.stderr b/src/tools/clippy/tests/ui/manual_repeat_n.stderr new file mode 100644 index 0000000000000..87395b3f8bf1b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_repeat_n.stderr @@ -0,0 +1,35 @@ +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:6:13 + | +LL | let _ = repeat(10).take(3); + | ^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(10, 3)` + | + = note: `-D clippy::manual-repeat-n` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_repeat_n)]` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:8:13 + | +LL | let _ = repeat(String::from("foo")).take(4); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("foo"), 4)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:10:18 + | +LL | for value in std::iter::repeat(5).take(3) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(5, 3)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:12:21 + | +LL | let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("bar"), 10)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:14:13 + | +LL | let _ = repeat(vec![1, 2]).take(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(vec![1, 2], 2)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.fixed b/src/tools/clippy/tests/ui/manual_str_repeat.fixed index 5f2f1bd9916d8..da6f36f53b0d4 100644 --- a/src/tools/clippy/tests/ui/manual_str_repeat.fixed +++ b/src/tools/clippy/tests/ui/manual_str_repeat.fixed @@ -1,4 +1,4 @@ -#![allow(non_local_definitions)] +#![allow(non_local_definitions, clippy::manual_repeat_n)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.rs b/src/tools/clippy/tests/ui/manual_str_repeat.rs index 3e3c7f4db4a27..686ed4fee7d1c 100644 --- a/src/tools/clippy/tests/ui/manual_str_repeat.rs +++ b/src/tools/clippy/tests/ui/manual_str_repeat.rs @@ -1,4 +1,4 @@ -#![allow(non_local_definitions)] +#![allow(non_local_definitions, clippy::manual_repeat_n)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed index cf520e71a64fc..18716e93d1e9c 100644 --- a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed @@ -14,7 +14,7 @@ fn do_something_interesting(x: usize, y: usize) -> usize { todo!() } -macro_rules! gen { +macro_rules! r#gen { () => { (0..10).map(|_| do_something()); }; @@ -45,7 +45,7 @@ fn main() { std::iter::repeat_with(|| do_something()).take(1); std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); // These should not be raised - gen!(); + r#gen!(); let lower = 2; let lower_fn = || 2; (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs index 298eee9ca3fa1..596afd51e61f3 100644 --- a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs @@ -14,7 +14,7 @@ fn do_something_interesting(x: usize, y: usize) -> usize { todo!() } -macro_rules! gen { +macro_rules! r#gen { () => { (0..10).map(|_| do_something()); }; @@ -45,7 +45,7 @@ fn main() { (9..=9).map(|_| do_something()); (1..=1 << 4).map(|_| do_something()); // These should not be raised - gen!(); + r#gen!(); let lower = 2; let lower_fn = || 2; (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed new file mode 100644 index 0000000000000..65e59774905c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::map_with_unused_argument_over_ranges)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn nostd(v: &mut [i32]) { + let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect(); +} diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.rs b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.rs new file mode 100644 index 0000000000000..dda7a69b33f96 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.rs @@ -0,0 +1,8 @@ +#![warn(clippy::map_with_unused_argument_over_ranges)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn nostd(v: &mut [i32]) { + let _: Vec<_> = (0..10).map(|_| 3 + 1).collect(); +} diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr new file mode 100644 index 0000000000000..d47f3d09175b2 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr @@ -0,0 +1,15 @@ +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges_nostd.rs:7:21 + | +LL | let _: Vec<_> = (0..10).map(|_| 3 + 1).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-with-unused-argument-over-ranges` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]` +help: remove the explicit range and use `repeat_n` + | +LL | let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect(); + | ~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/match_bool.fixed b/src/tools/clippy/tests/ui/match_bool.fixed new file mode 100644 index 0000000000000..61a8e54fa10ce --- /dev/null +++ b/src/tools/clippy/tests/ui/match_bool.fixed @@ -0,0 +1,58 @@ +#![deny(clippy::match_bool)] +#![allow(clippy::nonminimal_bool, clippy::eq_op)] + +fn match_bool() { + let test: bool = true; + + if test { 0 } else { 42 }; + + let option = 1; + if option == 1 { 1 } else { 0 }; + + if !test { + println!("Noooo!"); + }; + + if !test { + println!("Noooo!"); + }; + + if !(test && test) { + println!("Noooo!"); + }; + + if !test { + println!("Noooo!"); + } else { + println!("Yes!"); + }; + + // Not linted + match option { + 1..=10 => 1, + 11..=20 => 2, + _ => 3, + }; + + // Don't lint + let _ = match test { + #[cfg(feature = "foo")] + true if option == 5 => 10, + true => 0, + false => 1, + }; + + let _ = if test && option == 5 { 10 } else { 1 }; + + let _ = if !test && option == 5 { 10 } else { 1 }; + + if test && option == 5 { println!("Hello") }; + + if !(test && option == 5) { println!("Hello") }; + + if !test && option == 5 { println!("Hello") }; + + if !(!test && option == 5) { println!("Hello") }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.rs b/src/tools/clippy/tests/ui/match_bool.rs index f84af393e47fa..9c81d2917869b 100644 --- a/src/tools/clippy/tests/ui/match_bool.rs +++ b/src/tools/clippy/tests/ui/match_bool.rs @@ -1,24 +1,24 @@ -//@no-rustfix: overlapping suggestions #![deny(clippy::match_bool)] +#![allow(clippy::nonminimal_bool, clippy::eq_op)] fn match_bool() { let test: bool = true; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression true => 0, false => 42, }; let option = 1; match option == 1 { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression true => 1, false => 0, }; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression true => (), false => { println!("Noooo!"); @@ -26,7 +26,7 @@ fn match_bool() { }; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression false => { println!("Noooo!"); }, @@ -34,11 +34,7 @@ fn match_bool() { }; match test && test { - //~^ ERROR: this boolean expression can be simplified - //~| NOTE: `-D clippy::nonminimal-bool` implied by `-D warnings` - //~| ERROR: you seem to be trying to match on a boolean expression - //~| ERROR: equal expressions as operands to `&&` - //~| NOTE: `#[deny(clippy::eq_op)]` on by default + //~^ ERROR: `match` on a boolean expression false => { println!("Noooo!"); }, @@ -46,7 +42,7 @@ fn match_bool() { }; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression false => { println!("Noooo!"); }, @@ -69,6 +65,42 @@ fn match_bool() { true => 0, false => 1, }; + + let _ = match test { + //~^ ERROR: `match` on a boolean expression + true if option == 5 => 10, + _ => 1, + }; + + let _ = match test { + //~^ ERROR: `match` on a boolean expression + false if option == 5 => 10, + _ => 1, + }; + + match test { + //~^ ERROR: `match` on a boolean expression + true if option == 5 => println!("Hello"), + _ => (), + }; + + match test { + //~^ ERROR: `match` on a boolean expression + true if option == 5 => (), + _ => println!("Hello"), + }; + + match test { + //~^ ERROR: `match` on a boolean expression + false if option == 5 => println!("Hello"), + _ => (), + }; + + match test { + //~^ ERROR: `match` on a boolean expression + false if option == 5 => (), + _ => println!("Hello"), + }; } fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.stderr b/src/tools/clippy/tests/ui/match_bool.stderr index fb24e67eceefd..a4e504a0a82e4 100644 --- a/src/tools/clippy/tests/ui/match_bool.stderr +++ b/src/tools/clippy/tests/ui/match_bool.stderr @@ -1,13 +1,4 @@ -error: this boolean expression can be simplified - --> tests/ui/match_bool.rs:36:11 - | -LL | match test && test { - | ^^^^^^^^^^^^ help: try: `test` - | - = note: `-D clippy::nonminimal-bool` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` - -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:7:5 | LL | / match test { @@ -18,12 +9,12 @@ LL | | }; | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }` | note: the lint level is defined here - --> tests/ui/match_bool.rs:2:9 + --> tests/ui/match_bool.rs:1:9 | LL | #![deny(clippy::match_bool)] | ^^^^^^^^^^^^^^^^^^ -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:14:5 | LL | / match option == 1 { @@ -33,7 +24,7 @@ LL | | false => 0, LL | | }; | |_____^ help: consider using an `if`/`else` expression: `if option == 1 { 1 } else { 0 }` -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:20:5 | LL | / match test { @@ -52,7 +43,7 @@ LL + println!("Noooo!"); LL ~ }; | -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:28:5 | LL | / match test { @@ -71,11 +62,14 @@ LL + println!("Noooo!"); LL ~ }; | -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:36:5 | LL | / match test && test { -... | +LL | | +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, LL | | _ => (), LL | | }; | |_____^ @@ -87,16 +81,8 @@ LL + println!("Noooo!"); LL ~ }; | -error: equal expressions as operands to `&&` - --> tests/ui/match_bool.rs:36:11 - | -LL | match test && test { - | ^^^^^^^^^^^^ - | - = note: `#[deny(clippy::eq_op)]` on by default - -error: you seem to be trying to match on a boolean expression - --> tests/ui/match_bool.rs:48:5 +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:44:5 | LL | / match test { LL | | @@ -109,12 +95,74 @@ LL | | }; | help: consider using an `if`/`else` expression | -LL ~ if test { -LL + println!("Yes!"); -LL + } else { +LL ~ if !test { LL + println!("Noooo!"); +LL + } else { +LL + println!("Yes!"); LL ~ }; | -error: aborting due to 8 previous errors +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:69:13 + | +LL | let _ = match test { + | _____________^ +LL | | +LL | | true if option == 5 => 10, +LL | | _ => 1, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test && option == 5 { 10 } else { 1 }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:75:13 + | +LL | let _ = match test { + | _____________^ +LL | | +LL | | false if option == 5 => 10, +LL | | _ => 1, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !test && option == 5 { 10 } else { 1 }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:81:5 + | +LL | / match test { +LL | | +LL | | true if option == 5 => println!("Hello"), +LL | | _ => (), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test && option == 5 { println!("Hello") }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:87:5 + | +LL | / match test { +LL | | +LL | | true if option == 5 => (), +LL | | _ => println!("Hello"), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !(test && option == 5) { println!("Hello") }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:93:5 + | +LL | / match test { +LL | | +LL | | false if option == 5 => println!("Hello"), +LL | | _ => (), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !test && option == 5 { println!("Hello") }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:99:5 + | +LL | / match test { +LL | | +LL | | false if option == 5 => (), +LL | | _ => println!("Hello"), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !(!test && option == 5) { println!("Hello") }` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index fdde68790a8c7..cdfdcd5007a82 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -210,3 +210,9 @@ mod with_ty_alias { let _: Foo = 1; } } + +// Do not lint because mutable references in const functions are unstable in 1.82 +#[clippy::msrv = "1.82"] +fn mut_add(x: &mut i32) { + *x += 1; +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index dd9dedcdd0441..689060468c574 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -213,3 +213,8 @@ mod extern_fn { const extern "system-unwind" fn system_unwind() {} //~^ ERROR: this could be a `const fn` } + +const fn mut_add(x: &mut i32) { + //~^ ERROR: this could be a `const fn` + *x += 1; +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index f974478540cd5..492c47d7e49b5 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -213,3 +213,8 @@ mod extern_fn { extern "system-unwind" fn system_unwind() {} //~^ ERROR: this could be a `const fn` } + +fn mut_add(x: &mut i32) { + //~^ ERROR: this could be a `const fn` + *x += 1; +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index 33836bdfe9f8a..a06703e2ebf23 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -316,5 +316,19 @@ help: make the function `const` LL | const extern "system-unwind" fn system_unwind() {} | +++++ -error: aborting due to 24 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:217:1 + | +LL | / fn mut_add(x: &mut i32) { +LL | | +LL | | *x += 1; +LL | | } + | |_^ + | +help: make the function `const` + | +LL | const fn mut_add(x: &mut i32) { + | +++++ + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.fixed b/src/tools/clippy/tests/ui/needless_as_bytes.fixed index 042342311fdf1..74b4ba5be7989 100644 --- a/src/tools/clippy/tests/ui/needless_as_bytes.fixed +++ b/src/tools/clippy/tests/ui/needless_as_bytes.fixed @@ -1,5 +1,6 @@ #![warn(clippy::needless_as_bytes)] #![allow(clippy::const_is_empty)] +#![feature(exact_size_is_empty)] struct S; @@ -7,6 +8,9 @@ impl S { fn as_bytes(&self) -> &[u8] { &[] } + fn bytes(&self) -> &[u8] { + &[] + } } fn main() { @@ -15,6 +19,11 @@ fn main() { println!("len = {}", "some string".len()); //~^ needless_as_bytes } + if "some string".is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".len()); + //~^ needless_as_bytes + } let s = String::from("yet another string"); if s.is_empty() { @@ -22,6 +31,11 @@ fn main() { println!("len = {}", s.len()); //~^ needless_as_bytes } + if s.is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.len()); + //~^ needless_as_bytes + } // Do not lint let _ = S.as_bytes().is_empty(); @@ -36,6 +50,18 @@ fn main() { }; } m!(1).as_bytes().len(); + let _ = S.bytes().is_empty(); + let _ = S.bytes().len(); + let _ = (&String::new() as &dyn Bytes).bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".bytes() + }; + } + m!(1).bytes().len(); m!(2).len(); } @@ -48,3 +74,13 @@ impl AsBytes for String { &[] } } + +pub trait Bytes { + fn bytes(&self) -> &[u8]; +} + +impl Bytes for String { + fn bytes(&self) -> &[u8] { + &[] + } +} diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.rs b/src/tools/clippy/tests/ui/needless_as_bytes.rs index c481e041e0abf..ffcce60bbbef2 100644 --- a/src/tools/clippy/tests/ui/needless_as_bytes.rs +++ b/src/tools/clippy/tests/ui/needless_as_bytes.rs @@ -1,5 +1,6 @@ #![warn(clippy::needless_as_bytes)] #![allow(clippy::const_is_empty)] +#![feature(exact_size_is_empty)] struct S; @@ -7,6 +8,9 @@ impl S { fn as_bytes(&self) -> &[u8] { &[] } + fn bytes(&self) -> &[u8] { + &[] + } } fn main() { @@ -15,6 +19,11 @@ fn main() { println!("len = {}", "some string".as_bytes().len()); //~^ needless_as_bytes } + if "some string".bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".bytes().len()); + //~^ needless_as_bytes + } let s = String::from("yet another string"); if s.as_bytes().is_empty() { @@ -22,6 +31,11 @@ fn main() { println!("len = {}", s.as_bytes().len()); //~^ needless_as_bytes } + if s.bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.bytes().len()); + //~^ needless_as_bytes + } // Do not lint let _ = S.as_bytes().is_empty(); @@ -36,6 +50,18 @@ fn main() { }; } m!(1).as_bytes().len(); + let _ = S.bytes().is_empty(); + let _ = S.bytes().len(); + let _ = (&String::new() as &dyn Bytes).bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".bytes() + }; + } + m!(1).bytes().len(); m!(2).len(); } @@ -48,3 +74,13 @@ impl AsBytes for String { &[] } } + +pub trait Bytes { + fn bytes(&self) -> &[u8]; +} + +impl Bytes for String { + fn bytes(&self) -> &[u8] { + &[] + } +} diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.stderr b/src/tools/clippy/tests/ui/needless_as_bytes.stderr index 3391238a142bf..138c6630ae7d9 100644 --- a/src/tools/clippy/tests/ui/needless_as_bytes.stderr +++ b/src/tools/clippy/tests/ui/needless_as_bytes.stderr @@ -1,5 +1,5 @@ -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:13:8 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:17:8 | LL | if "some string".as_bytes().is_empty() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` @@ -7,23 +7,47 @@ LL | if "some string".as_bytes().is_empty() { = note: `-D clippy::needless-as-bytes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:15:30 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:19:30 | LL | println!("len = {}", "some string".as_bytes().len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:20:8 +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:22:8 + | +LL | if "some string".bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` + +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:24:30 + | +LL | println!("len = {}", "some string".bytes().len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` + +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:29:8 | LL | if s.as_bytes().is_empty() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:22:30 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:31:30 | LL | println!("len = {}", s.as_bytes().len()); | ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` -error: aborting due to 4 previous errors +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:34:8 + | +LL | if s.bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` + +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:36:30 + | +LL | println!("len = {}", s.bytes().len()); + | ^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index 6db870490445e..b4bd53ce7bf7f 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -270,3 +270,14 @@ fn issue8911() -> u32 { 3 } + +macro_rules! issue13776_mac { + ($var:expr, $val:literal) => { + $var = $val; + }; +} + +fn issue13776() { + let x; + issue13776_mac!(x, 10); // should not lint +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index c1e86212a08b8..e25483625a68c 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -270,3 +270,14 @@ fn issue8911() -> u32 { 3 } + +macro_rules! issue13776_mac { + ($var:expr, $val:literal) => { + $var = $val; + }; +} + +fn issue13776() { + let x; + issue13776_mac!(x, 10); // should not lint +} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index 8196d608abd21..86cf9a9cdb635 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -6,6 +6,7 @@ clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, + clippy::redundant_allocation, clippy::unnecessary_wraps, dyn_drop, clippy::get_first @@ -443,11 +444,20 @@ mod issue7296 { fn implicit_mut(&mut self) -> &() { &() } - - fn explicit<'a>(self: &'a Arc) -> &'a () { + #[clippy::msrv = "1.81"] + fn explicit(self: &Arc) -> &() { + &() + } + #[clippy::msrv = "1.81"] + fn explicit_mut(self: &mut Rc) -> &() { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a () { &() } - fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + #[clippy::msrv = "1.80"] + fn explicit_mut_older<'a>(self: &'a mut Rc) -> &'a () { &() } @@ -462,8 +472,16 @@ mod issue7296 { &() } - fn explicit<'a>(self: &'a Arc) -> &'a (); - fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + #[clippy::msrv = "1.81"] + fn explicit(self: &Arc) -> &(); + #[clippy::msrv = "1.81"] + fn explicit_provided(self: &Arc) -> &() { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.80"] + fn explicit_provided_older<'a>(self: &'a Arc) -> &'a () { &() } @@ -576,4 +594,85 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +mod issue13923 { + struct Py<'py> { + data: &'py str, + } + + enum Content<'t, 'py> { + Py(Py<'py>), + T1(&'t str), + T2(&'t str), + } + + enum ContentString<'t> { + T1(&'t str), + T2(&'t str), + } + + impl<'t, 'py> ContentString<'t> { + // `'py` cannot be elided + fn map_content1(self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&self` + fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&'_ self` + fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` should not be elided as the default lifetime, even if working, could be named as `'t` + fn map_content4(self, f: impl FnOnce(&'t str) -> &'t str, o: &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&Self` + fn map_content5( + self: std::pin::Pin<&Self>, + f: impl FnOnce(&'t str) -> &'t str, + o: &'t str, + ) -> Content<'t, '_> { + match *self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + struct Cx<'a, 'b> { + a: &'a u32, + b: &'b u32, + } + + // `'c` cannot be elided because we have several input lifetimes + fn one_explicit<'b>(x: Cx<'_, 'b>) -> &'b u32 { + x.b + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index b55dd99c46d05..1ee0f4c6092ae 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -6,6 +6,7 @@ clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, + clippy::redundant_allocation, clippy::unnecessary_wraps, dyn_drop, clippy::get_first @@ -443,13 +444,22 @@ mod issue7296 { fn implicit_mut<'a>(&'a mut self) -> &'a () { &() } - + #[clippy::msrv = "1.81"] fn explicit<'a>(self: &'a Arc) -> &'a () { &() } + #[clippy::msrv = "1.81"] fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { &() } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a () { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_mut_older<'a>(self: &'a mut Rc) -> &'a () { + &() + } fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { &() @@ -462,10 +472,18 @@ mod issue7296 { &() } + #[clippy::msrv = "1.81"] fn explicit<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.81"] fn explicit_provided<'a>(self: &'a Arc) -> &'a () { &() } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.80"] + fn explicit_provided_older<'a>(self: &'a Arc) -> &'a () { + &() + } fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { @@ -576,4 +594,85 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +mod issue13923 { + struct Py<'py> { + data: &'py str, + } + + enum Content<'t, 'py> { + Py(Py<'py>), + T1(&'t str), + T2(&'t str), + } + + enum ContentString<'t> { + T1(&'t str), + T2(&'t str), + } + + impl<'t, 'py> ContentString<'t> { + // `'py` cannot be elided + fn map_content1(self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&self` + fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&'_ self` + fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` should not be elided as the default lifetime, even if working, could be named as `'t` + fn map_content4(self, f: impl FnOnce(&'t str) -> &'t str, o: &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&Self` + fn map_content5( + self: std::pin::Pin<&Self>, + f: impl FnOnce(&'t str) -> &'t str, + o: &'t str, + ) -> Content<'t, 'py> { + match *self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + struct Cx<'a, 'b> { + a: &'a u32, + b: &'b u32, + } + + // `'c` cannot be elided because we have several input lifetimes + fn one_explicit<'b>(x: Cx<'_, 'b>) -> &'b u32 { + &x.b + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr index e56c914cc86d6..465d529bf16c5 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: elided lifetime has a name - --> tests/ui/needless_lifetimes.rs:266:52 + --> tests/ui/needless_lifetimes.rs:267:52 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | -- ^ this elided lifetime gets resolved as `'a` @@ -10,7 +10,7 @@ LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:17:23 + --> tests/ui/needless_lifetimes.rs:18:23 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^ ^^ ^^ ^^ @@ -24,7 +24,7 @@ LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} | error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:19:24 + --> tests/ui/needless_lifetimes.rs:20:24 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^ ^^ ^^ ^^ @@ -36,7 +36,7 @@ LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:29:15 + --> tests/ui/needless_lifetimes.rs:30:15 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^ ^^ ^^ @@ -48,7 +48,7 @@ LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:41:31 + --> tests/ui/needless_lifetimes.rs:42:31 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^ ^^ @@ -60,7 +60,7 @@ LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:48:27 + --> tests/ui/needless_lifetimes.rs:49:27 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^ ^^ @@ -72,7 +72,7 @@ LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:65:26 + --> tests/ui/needless_lifetimes.rs:66:26 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^ ^^ @@ -84,7 +84,7 @@ LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:72:22 + --> tests/ui/needless_lifetimes.rs:73:22 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^ ^^ @@ -96,7 +96,7 @@ LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:81:21 + --> tests/ui/needless_lifetimes.rs:82:21 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^ ^^ ^^ @@ -108,7 +108,7 @@ LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:86:28 + --> tests/ui/needless_lifetimes.rs:87:28 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^ ^^ ^^ @@ -120,7 +120,7 @@ LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> | error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:98:21 + --> tests/ui/needless_lifetimes.rs:99:21 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^ ^^ ^^ ^^ @@ -132,7 +132,7 @@ LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:122:15 + --> tests/ui/needless_lifetimes.rs:123:15 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^ ^^ ^^ @@ -144,7 +144,7 @@ LL + fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:152:21 + --> tests/ui/needless_lifetimes.rs:153:21 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^ ^^ ^^ @@ -156,7 +156,7 @@ LL + fn self_and_out(&self) -> &u8 { | error: the following explicit lifetimes could be elided: 't - --> tests/ui/needless_lifetimes.rs:159:30 + --> tests/ui/needless_lifetimes.rs:160:30 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^ ^^ @@ -168,7 +168,7 @@ LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:166:26 + --> tests/ui/needless_lifetimes.rs:167:26 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^ ^^ @@ -180,7 +180,7 @@ LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { | error: the following explicit lifetimes could be elided: 's, 't - --> tests/ui/needless_lifetimes.rs:170:29 + --> tests/ui/needless_lifetimes.rs:171:29 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^ ^^ ^^ ^^ @@ -192,7 +192,7 @@ LL + fn distinct_self_and_in(&self, _x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:189:19 + --> tests/ui/needless_lifetimes.rs:190:19 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^ ^^ ^^ @@ -204,7 +204,7 @@ LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:207:25 + --> tests/ui/needless_lifetimes.rs:208:25 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^ ^^ @@ -216,7 +216,7 @@ LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:215:21 + --> tests/ui/needless_lifetimes.rs:216:21 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^ ^^ @@ -228,7 +228,7 @@ LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:230:22 + --> tests/ui/needless_lifetimes.rs:231:22 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^ ^^ ^^ @@ -240,7 +240,7 @@ LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:236:18 + --> tests/ui/needless_lifetimes.rs:237:18 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^ ^^ ^^ @@ -252,7 +252,7 @@ LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:254:24 + --> tests/ui/needless_lifetimes.rs:255:24 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^ ^^ @@ -264,7 +264,7 @@ LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:262:20 + --> tests/ui/needless_lifetimes.rs:263:20 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^ ^^ @@ -276,7 +276,7 @@ LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:266:30 + --> tests/ui/needless_lifetimes.rs:267:30 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^ ^^ ^ @@ -288,7 +288,7 @@ LL + fn named_input_elided_output(_arg: &str) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:274:19 + --> tests/ui/needless_lifetimes.rs:275:19 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^ ^^ @@ -300,7 +300,7 @@ LL + fn trait_bound_ok>(_: &u8, _: T) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:310:24 + --> tests/ui/needless_lifetimes.rs:311:24 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^ ^^ ^^ @@ -312,7 +312,7 @@ LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:317:24 + --> tests/ui/needless_lifetimes.rs:318:24 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^ ^^ @@ -324,7 +324,7 @@ LL + fn needless_lt(x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:321:24 + --> tests/ui/needless_lifetimes.rs:322:24 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^ ^^ @@ -336,7 +336,7 @@ LL + fn needless_lt(_x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:332:10 + --> tests/ui/needless_lifetimes.rs:333:10 | LL | impl<'a> Foo for Baz<'a> {} | ^^ ^^ @@ -348,7 +348,7 @@ LL + impl Foo for Baz<'_> {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:334:16 + --> tests/ui/needless_lifetimes.rs:335:16 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^ ^^ ^^ @@ -360,7 +360,7 @@ LL + fn baz(&self) -> impl Foo + '_ { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:366:55 + --> tests/ui/needless_lifetimes.rs:367:55 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -372,7 +372,7 @@ LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(& | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:375:26 + --> tests/ui/needless_lifetimes.rs:376:26 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^ ^^ ^^ @@ -384,7 +384,7 @@ LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:387:30 + --> tests/ui/needless_lifetimes.rs:388:30 | LL | fn where_clause_elidable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^ ^^ ^^ @@ -396,7 +396,7 @@ LL + fn where_clause_elidable(i: &i32, f: T) -> &i32 | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:402:28 + --> tests/ui/needless_lifetimes.rs:403:28 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -408,7 +408,7 @@ LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:415:28 + --> tests/ui/needless_lifetimes.rs:416:28 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^ ^^ @@ -420,7 +420,7 @@ LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:418:28 + --> tests/ui/needless_lifetimes.rs:419:28 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^ ^^ @@ -432,7 +432,7 @@ LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:440:21 + --> tests/ui/needless_lifetimes.rs:441:21 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -444,7 +444,7 @@ LL + fn implicit(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:443:25 + --> tests/ui/needless_lifetimes.rs:444:25 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^ ^^ ^^ @@ -456,7 +456,31 @@ LL + fn implicit_mut(&mut self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:454:31 + --> tests/ui/needless_lifetimes.rs:448:21 + | +LL | fn explicit<'a>(self: &'a Arc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit<'a>(self: &'a Arc) -> &'a () { +LL + fn explicit(self: &Arc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:452:25 + | +LL | fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { +LL + fn explicit_mut(self: &mut Rc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:464:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -468,7 +492,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:460:21 + --> tests/ui/needless_lifetimes.rs:470:21 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^ ^^ ^^ @@ -480,7 +504,7 @@ LL + fn implicit(&self) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:461:30 + --> tests/ui/needless_lifetimes.rs:471:30 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -492,7 +516,31 @@ LL + fn implicit_provided(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:470:31 + --> tests/ui/needless_lifetimes.rs:476:21 + | +LL | fn explicit<'a>(self: &'a Arc) -> &'a (); + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit<'a>(self: &'a Arc) -> &'a (); +LL + fn explicit(self: &Arc) -> &(); + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:478:30 + | +LL | fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit_provided<'a>(self: &'a Arc) -> &'a () { +LL + fn explicit_provided(self: &Arc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:488:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^ ^^ ^^ @@ -504,7 +552,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:471:40 + --> tests/ui/needless_lifetimes.rs:489:40 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -516,7 +564,7 @@ LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:480:12 + --> tests/ui/needless_lifetimes.rs:498:12 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^ ^^ @@ -528,7 +576,7 @@ LL + fn foo(x: &u8, y: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:482:12 + --> tests/ui/needless_lifetimes.rs:500:12 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^ ^^ @@ -540,7 +588,7 @@ LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:489:18 + --> tests/ui/needless_lifetimes.rs:507:18 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ @@ -552,7 +600,7 @@ LL + fn one_input(x: &u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:494:42 + --> tests/ui/needless_lifetimes.rs:512:42 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^ ^^ @@ -564,7 +612,7 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:510:22 + --> tests/ui/needless_lifetimes.rs:528:22 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ @@ -576,5 +624,64 @@ LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { LL + fn one_input(x: &u8) -> &u8 { | -error: aborting due to 48 previous errors +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:623:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +LL | // `'py` can be elided because of `&self` +LL | fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&self` +LL ~ fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + | + +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:633:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +LL | // `'py` can be elided because of `&'_ self` +LL | fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&'_ self` +LL ~ fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + | + +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:653:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +... +LL | ) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&Self` +... +LL | o: &'t str, +LL ~ ) -> Content<'t, '_> { + | + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_lifetimes.rs:674:9 + | +LL | &x.b + | ^^^^ help: change this to: `x.b` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` + +error: aborting due to 56 previous errors diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs new file mode 100644 index 0000000000000..85fb4e66079fb --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs @@ -0,0 +1,20 @@ +//! **FAKE** lazy_static crate. + +#[macro_export] +macro_rules! lazy_static { + (static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + static $N : &::core::marker::PhantomData<$T> = &::core::marker::PhantomData; + + $crate::lazy_static! { $($t)* } + }; + () => () +} + +#[macro_export] +macro_rules! external { + () => { + $crate::lazy_static! { + static ref LZ_DERP: u32 = 12; + } + }; +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs new file mode 100644 index 0000000000000..e860a0f7572c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs @@ -0,0 +1,55 @@ +//! **FAKE** once_cell crate. + +pub mod sync { + use std::marker::PhantomData; + + pub struct Lazy T> { + cell: PhantomData, + init: F, + } + unsafe impl Sync for Lazy {} + impl Lazy { + pub const fn new(f: F) -> Lazy { + Lazy { + cell: PhantomData, + init: f, + } + } + + pub fn into_value(this: Lazy) -> Result { + unimplemented!() + } + + pub fn force(_this: &Lazy) -> &T { + unimplemented!() + } + + pub fn force_mut(_this: &mut Lazy) -> &mut T { + unimplemented!() + } + + pub fn get(_this: &Lazy) -> Option<&T> { + unimplemented!() + } + + pub fn get_mut(_this: &mut Lazy) -> Option<&mut T> { + unimplemented!() + } + } +} +pub mod race { + pub struct OnceBox(T); + + impl OnceBox { + pub fn get(&self) -> Option<&T> { + Some(&self.0) + } + } +} + +#[macro_export] +macro_rules! external { + () => { + static OC_DERP: $crate::sync::Lazy = $crate::sync::Lazy::new(|| 12); + }; +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed new file mode 100644 index 0000000000000..f7c56b6fffe81 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -0,0 +1,72 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + let x = "bar"; + x.to_uppercase() +}); +static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_QUX: std::sync::LazyLock = { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + if "qux".len() == 3 { + std::sync::LazyLock::new(|| "qux".to_uppercase()) + } else if "qux".is_ascii() { + std::sync::LazyLock::new(|| "qux".to_lowercase()) + } else { + std::sync::LazyLock::new(|| "qux".to_string()) + } +}; + +fn non_static() { + let _: Lazy = Lazy::new(|| 1); + let _: Lazy = Lazy::new(|| String::from("hello")); + #[allow(clippy::declare_interior_mutable_const)] + const DONT_DO_THIS: Lazy = Lazy::new(|| 1); +} + +mod once_cell_lazy_with_fns { + use once_cell::sync::Lazy; + + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_replaceable_fns() { + let _ = std::sync::LazyLock::force(&LAZY_FOO); + let _ = std::sync::LazyLock::force(&LAZY_BAR); + unsafe { + let _ = std::sync::LazyLock::force(&LAZY_BAZ); + } + } +} + +#[clippy::msrv = "1.79"] +mod msrv_not_meet { + use lazy_static::lazy_static; + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + + lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; + } +} + +mod external_macros { + once_cell::external!(); + lazy_static::external!(); +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs new file mode 100644 index 0000000000000..90bc428137cea --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -0,0 +1,72 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_BAR: Lazy = Lazy::new(|| { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + let x = "bar"; + x.to_uppercase() +}); +static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_QUX: Lazy = { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + if "qux".len() == 3 { + Lazy::new(|| "qux".to_uppercase()) + } else if "qux".is_ascii() { + Lazy::new(|| "qux".to_lowercase()) + } else { + Lazy::new(|| "qux".to_string()) + } +}; + +fn non_static() { + let _: Lazy = Lazy::new(|| 1); + let _: Lazy = Lazy::new(|| String::from("hello")); + #[allow(clippy::declare_interior_mutable_const)] + const DONT_DO_THIS: Lazy = Lazy::new(|| 1); +} + +mod once_cell_lazy_with_fns { + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_replaceable_fns() { + let _ = Lazy::force(&LAZY_FOO); + let _ = Lazy::force(&LAZY_BAR); + unsafe { + let _ = Lazy::force(&LAZY_BAZ); + } + } +} + +#[clippy::msrv = "1.79"] +mod msrv_not_meet { + use lazy_static::lazy_static; + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + + lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; + } +} + +mod external_macros { + once_cell::external!(); + lazy_static::external!(); +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr new file mode 100644 index 0000000000000..f956f4b8d52a4 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr @@ -0,0 +1,100 @@ +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:11:18 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | + = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:13:18 + | +LL | static LAZY_BAR: Lazy = Lazy::new(|| { + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:18:18 + | +LL | static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:20:18 + | +LL | static LAZY_QUX: Lazy = { + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_QUX: std::sync::LazyLock = { +LL | +LL | if "qux".len() == 3 { +LL ~ std::sync::LazyLock::new(|| "qux".to_uppercase()) +LL | } else if "qux".is_ascii() { +LL ~ std::sync::LazyLock::new(|| "qux".to_lowercase()) +LL | } else { +LL ~ std::sync::LazyLock::new(|| "qux".to_string()) + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:41:22 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); +LL | +... +LL | fn calling_replaceable_fns() { +LL ~ let _ = std::sync::LazyLock::force(&LAZY_FOO); + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:43:22 + | +LL | static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); +LL | +... +LL | let _ = Lazy::force(&LAZY_FOO); +LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAR); + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:45:26 + | +LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); +LL | +... +LL | unsafe { +LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAZ); + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs new file mode 100644 index 0000000000000..6208612c69830 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs @@ -0,0 +1,20 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![no_std] + +use lazy_static::lazy_static; +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: Lazy = Lazy::new(|| 42); +static LAZY_BAR: Lazy = Lazy::new(|| { + let x: i32 = 0; + x.saturating_add(100) +}); + +lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs new file mode 100644 index 0000000000000..8701a4b7729ad --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs @@ -0,0 +1,12 @@ +//@aux-build:once_cell.rs + +#![warn(clippy::non_std_lazy_statics)] + +// Should not error, since we used a type besides `sync::Lazy` +fn use_once_cell_race(x: once_cell::race::OnceBox) { + let _foo = x.get(); +} + +use once_cell::sync::Lazy; + +static LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs new file mode 100644 index 0000000000000..34f8dd1ccb2ea --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs @@ -0,0 +1,43 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs +//@no-rustfix + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +mod once_cell_lazy { + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_irreplaceable_fns() { + let _ = Lazy::get(&LAZY_FOO); + + unsafe { + let _ = Lazy::get_mut(&mut LAZY_BAR); + let _ = Lazy::force_mut(&mut LAZY_BAZ); + } + } +} + +mod lazy_static_lazy_static { + use lazy_static::lazy_static; + + lazy_static! { + static ref LAZY_FOO: String = "foo".to_uppercase(); + } + //~^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + lazy_static! { + static ref LAZY_BAR: String = "bar".to_uppercase(); + static ref LAZY_BAZ: String = "baz".to_uppercase(); + } + //~^^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + //~| ERROR: this macro has been superceded by `std::sync::LazyLock` +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr new file mode 100644 index 0000000000000..66dc435f9823f --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr @@ -0,0 +1,66 @@ +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:31:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_FOO: String = "foo".to_uppercase(); +LL | | } + | |_____^ + | + = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` + +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_BAR: String = "bar".to_uppercase(); +LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); +LL | | } + | |_____^ + +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_BAR: String = "bar".to_uppercase(); +LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); +LL | | } + | |_____^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:11:22 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:13:26 + | +LL | static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static mut LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:15:26 + | +LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed index c5ee569800abd..bfe1c5e10cf8d 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed @@ -1,5 +1,18 @@ #![warn(clippy::obfuscated_if_else)] +#![allow(clippy::unnecessary_lazy_evaluations, clippy::unit_arg, clippy::unused_unit)] fn main() { if true { "a" } else { "b" }; + if true { "a" } else { "b" }; + + let a = 1; + if a == 1 { "a" } else { "b" }; + if a == 1 { "a" } else { "b" }; + + let partial = (a == 1).then_some("a"); + partial.unwrap_or("b"); // not lint + + let mut a = 0; + if true { a += 1 } else { () }; + if true { () } else { a += 2 }; } diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.rs b/src/tools/clippy/tests/ui/obfuscated_if_else.rs index 2b60c855a555a..0ded2a2ceedfb 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.rs +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.rs @@ -1,5 +1,18 @@ #![warn(clippy::obfuscated_if_else)] +#![allow(clippy::unnecessary_lazy_evaluations, clippy::unit_arg, clippy::unused_unit)] fn main() { true.then_some("a").unwrap_or("b"); + true.then(|| "a").unwrap_or("b"); + + let a = 1; + (a == 1).then_some("a").unwrap_or("b"); + (a == 1).then(|| "a").unwrap_or("b"); + + let partial = (a == 1).then_some("a"); + partial.unwrap_or("b"); // not lint + + let mut a = 0; + true.then_some(a += 1).unwrap_or(()); + true.then_some(()).unwrap_or(a += 2); } diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr index d4c2f9b331a82..9ce1f475c4803 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr @@ -1,5 +1,5 @@ -error: use of `.then_some(..).unwrap_or(..)` can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:4:5 +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:5:5 | LL | true.then_some("a").unwrap_or("b"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }` @@ -7,5 +7,35 @@ LL | true.then_some("a").unwrap_or("b"); = note: `-D clippy::obfuscated-if-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::obfuscated_if_else)]` -error: aborting due to 1 previous error +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:6:5 + | +LL | true.then(|| "a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:9:5 + | +LL | (a == 1).then_some("a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if a == 1 { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:10:5 + | +LL | (a == 1).then(|| "a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if a == 1 { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:16:5 + | +LL | true.then_some(a += 1).unwrap_or(()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { a += 1 } else { () }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:17:5 + | +LL | true.then_some(()).unwrap_or(a += 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { a += 2 }` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index 0d63e827d66ea..329422cb8a69b 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -1,4 +1,4 @@ -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:16:5 | LL | 1 << 2 + 3; @@ -7,61 +7,61 @@ LL | 1 << 2 + 3; = note: `-D clippy::precedence` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::precedence)]` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:17:5 | LL | 1 + 2 << 3; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:18:5 | LL | 4 >> 1 + 1; | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:19:5 | LL | 1 + 3 >> 2; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:20:5 | LL | 1 ^ 1 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:21:5 | LL | 3 | 2 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:22:5 | LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> 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 +error: operator precedence might not be obvious --> 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 +error: operator precedence might not be obvious --> 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 +error: operator precedence might not be obvious --> tests/ui/precedence.rs:26:5 | LL | 0x0F00 << 1 | 2; diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed index e1d845721a9c9..8882a4d50a5f8 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![allow(dead_code)] #![warn(clippy::redundant_pub_crate)] @@ -113,4 +114,10 @@ mod issue_8732 { pub(crate) use some_macro; // ok: macro exports are exempt } +proc_macros::external! { + mod priv_mod { + pub(crate) fn dummy() {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs index 4d7f44892d0c5..5c8cab9be161d 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.rs +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![allow(dead_code)] #![warn(clippy::redundant_pub_crate)] @@ -113,4 +114,10 @@ mod issue_8732 { pub(crate) use some_macro; // ok: macro exports are exempt } +proc_macros::external! { + mod priv_mod { + pub(crate) fn dummy() {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr index 8f1005ab9b73c..699e19b1abcf1 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr @@ -1,5 +1,5 @@ error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:6:5 + --> tests/ui/redundant_pub_crate.rs:7:5 | LL | pub(crate) fn g() {} // private due to m1 | ----------^^^^^ @@ -10,7 +10,7 @@ LL | pub(crate) fn g() {} // private due to m1 = help: to override `-D warnings` add `#[allow(clippy::redundant_pub_crate)]` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:11:9 + --> tests/ui/redundant_pub_crate.rs:12:9 | LL | pub(crate) fn g() {} // private due to m1_1 and m1 | ----------^^^^^ @@ -18,7 +18,7 @@ LL | pub(crate) fn g() {} // private due to m1_1 and m1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:15:5 + --> tests/ui/redundant_pub_crate.rs:16:5 | LL | pub(crate) mod m1_2 { | ----------^^^^^^^^^ @@ -26,7 +26,7 @@ LL | pub(crate) mod m1_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:18:9 + --> tests/ui/redundant_pub_crate.rs:19:9 | LL | pub(crate) fn g() {} // private due to m1_2 and m1 | ----------^^^^^ @@ -34,7 +34,7 @@ LL | pub(crate) fn g() {} // private due to m1_2 and m1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:24:9 + --> tests/ui/redundant_pub_crate.rs:25:9 | LL | pub(crate) fn g() {} // private due to m1 | ----------^^^^^ @@ -42,7 +42,7 @@ LL | pub(crate) fn g() {} // private due to m1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:31:5 + --> tests/ui/redundant_pub_crate.rs:32:5 | LL | pub(crate) fn g() {} // already crate visible due to m2 | ----------^^^^^ @@ -50,7 +50,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:36:9 + --> tests/ui/redundant_pub_crate.rs:37:9 | LL | pub(crate) fn g() {} // private due to m2_1 | ----------^^^^^ @@ -58,7 +58,7 @@ LL | pub(crate) fn g() {} // private due to m2_1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:40:5 + --> tests/ui/redundant_pub_crate.rs:41:5 | LL | pub(crate) mod m2_2 { | ----------^^^^^^^^^ @@ -66,7 +66,7 @@ LL | pub(crate) mod m2_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:43:9 + --> tests/ui/redundant_pub_crate.rs:44:9 | LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 | ----------^^^^^ @@ -74,7 +74,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:49:9 + --> tests/ui/redundant_pub_crate.rs:50:9 | LL | pub(crate) fn g() {} // already crate visible due to m2 | ----------^^^^^ @@ -82,7 +82,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:61:9 + --> tests/ui/redundant_pub_crate.rs:62:9 | LL | pub(crate) fn g() {} // private due to m3_1 | ----------^^^^^ @@ -90,7 +90,7 @@ LL | pub(crate) fn g() {} // private due to m3_1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:68:9 + --> tests/ui/redundant_pub_crate.rs:69:9 | LL | pub(crate) fn g() {} // already crate visible due to m3_2 | ----------^^^^^ @@ -98,7 +98,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m3_2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:81:5 + --> tests/ui/redundant_pub_crate.rs:82:5 | LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` | ----------^^^^^ @@ -106,7 +106,7 @@ LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:86:9 + --> tests/ui/redundant_pub_crate.rs:87:9 | LL | pub(crate) fn g() {} // private due to m4_1 | ----------^^^^^ @@ -114,7 +114,7 @@ LL | pub(crate) fn g() {} // private due to m4_1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:90:5 + --> tests/ui/redundant_pub_crate.rs:91:5 | LL | pub(crate) mod m4_2 { | ----------^^^^^^^^^ @@ -122,7 +122,7 @@ LL | pub(crate) mod m4_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:93:9 + --> tests/ui/redundant_pub_crate.rs:94:9 | LL | pub(crate) fn g() {} // private due to m4_2 | ----------^^^^^ diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.fixed b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.fixed new file mode 100644 index 0000000000000..ef316f1def41c --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::repeat_vec_with_capacity)] +#![allow(clippy::manual_repeat_n)] +#![no_std] +use core::iter; +extern crate alloc; +use alloc::vec::Vec; + +fn nostd() { + let _: Vec> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect(); +} diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.rs b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.rs new file mode 100644 index 0000000000000..83b418a566749 --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.rs @@ -0,0 +1,10 @@ +#![warn(clippy::repeat_vec_with_capacity)] +#![allow(clippy::manual_repeat_n)] +#![no_std] +use core::iter; +extern crate alloc; +use alloc::vec::Vec; + +fn nostd() { + let _: Vec> = iter::repeat(Vec::with_capacity(42)).take(123).collect(); +} diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr new file mode 100644 index 0000000000000..39364d09b9613 --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr @@ -0,0 +1,16 @@ +error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity + --> tests/ui/repeat_vec_with_capacity_nostd.rs:9:27 + | +LL | let _: Vec> = iter::repeat(Vec::with_capacity(42)).take(123).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: none of the yielded `Vec`s will have the requested capacity + = note: `-D clippy::repeat-vec-with-capacity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]` +help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try + | +LL | let _: Vec> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs index df9c2817f5083..87fd59ad31794 100644 --- a/src/tools/clippy/tests/ui/same_item_push.rs +++ b/src/tools/clippy/tests/ui/same_item_push.rs @@ -21,33 +21,43 @@ fn main() { let item = 2; for _ in 5..=20 { vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec: Vec = Vec::new(); for _ in 0..15 { let item = 2; vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec: Vec = Vec::new(); for _ in 0..15 { vec.push(13); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec = Vec::new(); for _ in 0..20 { vec.push(VALUE); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec = Vec::new(); let item = VALUE; for _ in 0..20 { vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` + } + + #[clippy::msrv = "1.81"] + fn older_msrv() { + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + //~^ ERROR: it looks like the same item is being pushed into this `Vec` + } } // ** non-linted cases ** diff --git a/src/tools/clippy/tests/ui/same_item_push.stderr b/src/tools/clippy/tests/ui/same_item_push.stderr index eb296ed4ce4ae..e3fa4f9cbcec2 100644 --- a/src/tools/clippy/tests/ui/same_item_push.stderr +++ b/src/tools/clippy/tests/ui/same_item_push.stderr @@ -1,44 +1,58 @@ -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:23:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` = note: `-D clippy::same-item-push` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::same_item_push)]` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:30:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:36:9 | LL | vec.push(13); | ^^^ | - = help: consider using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) + = help: consider using `vec![13;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(13, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:42:9 | LL | vec.push(VALUE); | ^^^ | - = help: consider using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) + = help: consider using `vec![VALUE;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(VALUE, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:49:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` -error: aborting due to 5 previous errors +error: it looks like the same item is being pushed into this `Vec` + --> tests/ui/same_item_push.rs:58:13 + | +LL | vec.push(item); + | ^^^ + | + = help: consider using `vec![item;SIZE]` + = help: or `vec.resize(NEW_SIZE, item)` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.fixed b/src/tools/clippy/tests/ui/short_circuit_statement.fixed index a9930ef4dbb61..a2bf07ac60523 100644 --- a/src/tools/clippy/tests/ui/short_circuit_statement.fixed +++ b/src/tools/clippy/tests/ui/short_circuit_statement.fixed @@ -3,8 +3,35 @@ fn main() { if f() { g(); } + //~^ ERROR: boolean short circuit operator in statement if !f() { g(); } + //~^ ERROR: boolean short circuit operator in statement if 1 != 2 { g(); } + //~^ ERROR: boolean short circuit operator in statement + if f() || g() { H * 2; } + //~^ ERROR: boolean short circuit operator in statement + if !(f() || g()) { H * 2; } + //~^ ERROR: boolean short circuit operator in statement + + macro_rules! mac { + ($f:ident or $g:ident) => { + $f() || $g() + }; + ($f:ident and $g:ident) => { + $f() && $g() + }; + () => { + f() && g() + }; + } + + if mac!() { mac!(); } + //~^ ERROR: boolean short circuit operator in statement + if !mac!() { mac!(); } + //~^ ERROR: boolean short circuit operator in statement + + // Do not lint if the expression comes from a macro + mac!(); } fn f() -> bool { @@ -14,3 +41,12 @@ fn f() -> bool { fn g() -> bool { false } + +struct H; + +impl std::ops::Mul for H { + type Output = bool; + fn mul(self, other: u32) -> Self::Output { + true + } +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.rs b/src/tools/clippy/tests/ui/short_circuit_statement.rs index 71f7c7f2abf77..bdba546ad8f63 100644 --- a/src/tools/clippy/tests/ui/short_circuit_statement.rs +++ b/src/tools/clippy/tests/ui/short_circuit_statement.rs @@ -3,8 +3,35 @@ fn main() { f() && g(); + //~^ ERROR: boolean short circuit operator in statement f() || g(); + //~^ ERROR: boolean short circuit operator in statement 1 == 2 || g(); + //~^ ERROR: boolean short circuit operator in statement + (f() || g()) && (H * 2); + //~^ ERROR: boolean short circuit operator in statement + (f() || g()) || (H * 2); + //~^ ERROR: boolean short circuit operator in statement + + macro_rules! mac { + ($f:ident or $g:ident) => { + $f() || $g() + }; + ($f:ident and $g:ident) => { + $f() && $g() + }; + () => { + f() && g() + }; + } + + mac!() && mac!(); + //~^ ERROR: boolean short circuit operator in statement + mac!() || mac!(); + //~^ ERROR: boolean short circuit operator in statement + + // Do not lint if the expression comes from a macro + mac!(); } fn f() -> bool { @@ -14,3 +41,12 @@ fn f() -> bool { fn g() -> bool { false } + +struct H; + +impl std::ops::Mul for H { + type Output = bool; + fn mul(self, other: u32) -> Self::Output { + true + } +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.stderr b/src/tools/clippy/tests/ui/short_circuit_statement.stderr index e7a8f2ca60ca1..ecf6676405b4e 100644 --- a/src/tools/clippy/tests/ui/short_circuit_statement.stderr +++ b/src/tools/clippy/tests/ui/short_circuit_statement.stderr @@ -8,16 +8,40 @@ LL | f() && g(); = help: to override `-D warnings` add `#[allow(clippy::short_circuit_statement)]` error: boolean short circuit operator in statement may be clearer using an explicit test - --> tests/ui/short_circuit_statement.rs:6:5 + --> tests/ui/short_circuit_statement.rs:7:5 | LL | f() || g(); | ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }` error: boolean short circuit operator in statement may be clearer using an explicit test - --> tests/ui/short_circuit_statement.rs:7:5 + --> tests/ui/short_circuit_statement.rs:9:5 | LL | 1 == 2 || g(); | ^^^^^^^^^^^^^^ help: replace it with: `if 1 != 2 { g(); }` -error: aborting due to 3 previous errors +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:11:5 + | +LL | (f() || g()) && (H * 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `if f() || g() { H * 2; }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:13:5 + | +LL | (f() || g()) || (H * 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `if !(f() || g()) { H * 2; }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:28:5 + | +LL | mac!() && mac!(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `if mac!() { mac!(); }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:30:5 + | +LL | mac!() || mac!(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `if !mac!() { mac!(); }` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs index 8468d1d7c7d42..39d550398d705 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs @@ -832,4 +832,36 @@ fn should_trigger_lint_in_while_let() { } } +async fn foo_async(mutex: &Mutex) -> Option> { + Some(mutex.lock().unwrap()) +} + +async fn should_trigger_lint_for_async(mutex: Mutex) -> i32 { + match *foo_async(&mutex).await.unwrap() { + n if n < 10 => n, + _ => 10, + } +} + +async fn should_not_trigger_lint_in_async_expansion(mutex: Mutex) -> i32 { + match foo_async(&mutex).await { + Some(guard) => *guard, + _ => 0, + } +} + +fn should_trigger_lint_in_match_expr() { + let mutex = Mutex::new(State {}); + + // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it + // is preserved until the end of the match, but there is no clear indication that this is the + // case. + let _ = match mutex.lock().unwrap().foo() { + //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + true => 0, + false => 1, + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr index 62030cbe70e7f..f99d862aa6b25 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr @@ -568,5 +568,37 @@ LL | } | = note: this might lead to deadlocks or other unexpected behavior -error: aborting due to 29 previous errors +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:840:11 + | +LL | match *foo_async(&mutex).await.unwrap() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | } + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = *foo_async(&mutex).await.unwrap(); +LL ~ match value { + | + +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:859:19 + | +LL | let _ = match mutex.lock().unwrap().foo() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().foo(); +LL ~ let _ = match value { + | + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/sliced_string_as_bytes.fixed b/src/tools/clippy/tests/ui/sliced_string_as_bytes.fixed new file mode 100644 index 0000000000000..469ad27a99b9f --- /dev/null +++ b/src/tools/clippy/tests/ui/sliced_string_as_bytes.fixed @@ -0,0 +1,34 @@ +#![allow(unused)] +#![warn(clippy::sliced_string_as_bytes)] + +use std::ops::{Index, Range}; + +struct Foo; + +struct Bar; + +impl Bar { + fn as_bytes(&self) -> &[u8] { + &[0, 1, 2, 3] + } +} + +impl Index> for Foo { + type Output = Bar; + + fn index(&self, _: Range) -> &Self::Output { + &Bar + } +} + +fn main() { + let s = "Lorem ipsum"; + let string: String = "dolor sit amet".to_owned(); + + let bytes = &s.as_bytes()[1..5]; + let bytes = &string.as_bytes()[1..]; + let bytes = &"consectetur adipiscing".as_bytes()[..=5]; + + let f = Foo; + let bytes = f[0..4].as_bytes(); +} diff --git a/src/tools/clippy/tests/ui/sliced_string_as_bytes.rs b/src/tools/clippy/tests/ui/sliced_string_as_bytes.rs new file mode 100644 index 0000000000000..4a4605e5a1aea --- /dev/null +++ b/src/tools/clippy/tests/ui/sliced_string_as_bytes.rs @@ -0,0 +1,34 @@ +#![allow(unused)] +#![warn(clippy::sliced_string_as_bytes)] + +use std::ops::{Index, Range}; + +struct Foo; + +struct Bar; + +impl Bar { + fn as_bytes(&self) -> &[u8] { + &[0, 1, 2, 3] + } +} + +impl Index> for Foo { + type Output = Bar; + + fn index(&self, _: Range) -> &Self::Output { + &Bar + } +} + +fn main() { + let s = "Lorem ipsum"; + let string: String = "dolor sit amet".to_owned(); + + let bytes = s[1..5].as_bytes(); + let bytes = string[1..].as_bytes(); + let bytes = "consectetur adipiscing"[..=5].as_bytes(); + + let f = Foo; + let bytes = f[0..4].as_bytes(); +} diff --git a/src/tools/clippy/tests/ui/sliced_string_as_bytes.stderr b/src/tools/clippy/tests/ui/sliced_string_as_bytes.stderr new file mode 100644 index 0000000000000..1342f4c01a484 --- /dev/null +++ b/src/tools/clippy/tests/ui/sliced_string_as_bytes.stderr @@ -0,0 +1,23 @@ +error: calling `as_bytes` after slicing a string + --> tests/ui/sliced_string_as_bytes.rs:28:17 + | +LL | let bytes = s[1..5].as_bytes(); + | ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]` + | + = note: `-D clippy::sliced-string-as-bytes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::sliced_string_as_bytes)]` + +error: calling `as_bytes` after slicing a string + --> tests/ui/sliced_string_as_bytes.rs:29:17 + | +LL | let bytes = string[1..].as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&string.as_bytes()[1..]` + +error: calling `as_bytes` after slicing a string + --> tests/ui/sliced_string_as_bytes.rs:30:17 + | +LL | let bytes = "consectetur adipiscing"[..=5].as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&"consectetur adipiscing".as_bytes()[..=5]` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.fixed b/src/tools/clippy/tests/ui/slow_vector_initialization.fixed new file mode 100644 index 0000000000000..a8570366e646d --- /dev/null +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.fixed @@ -0,0 +1,86 @@ +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] + +use std::iter::repeat; +fn main() { + resize_vector(); + extend_vector(); + mixed_extend_resize_vector(); + from_empty_vec(); +} + +fn extend_vector() { + // Extend with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Extend with len expression + let mut vec2 = vec![0; len - 10]; + + // Extend with mismatching expression should not be warned + let mut vec3 = Vec::with_capacity(24322); + vec3.extend(repeat(0).take(2)); + + let mut vec4 = vec![0; len]; +} + +fn mixed_extend_resize_vector() { + // Mismatching len + let mut mismatching_len = Vec::with_capacity(30); + mismatching_len.extend(repeat(0).take(40)); + + // Slow initialization + let mut resized_vec = vec![0; 30]; + + let mut extend_vec = vec![0; 30]; +} + +fn resize_vector() { + // Resize with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Resize mismatch len + let mut vec2 = Vec::with_capacity(200); + vec2.resize(10, 0); + + // Resize with len expression + let mut vec3 = vec![0; len - 10]; + + let mut vec4 = vec![0; len]; + + // Reinitialization should be warned + vec1 = vec![0; 10]; +} + +fn from_empty_vec() { + // Resize with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Resize with len expression + let mut vec3 = vec![0; len - 10]; + + // Reinitialization should be warned + vec1 = vec![0; 10]; + + vec1 = vec![0; 10]; + + macro_rules! x { + () => { + vec![] + }; + } + + // `vec![]` comes from another macro, don't warn + vec1 = x!(); + vec1.resize(10, 0); +} + +fn do_stuff(vec: &mut [u8]) {} + +fn extend_vector_with_manipulations_between() { + let len = 300; + let mut vec1: Vec = Vec::with_capacity(len); + do_stuff(&mut vec1); + vec1.extend(repeat(0).take(len)); +} diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs index 2ba87f4125000..4b30fad409e3a 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -1,4 +1,5 @@ -//@no-rustfix +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] + use std::iter::repeat; fn main() { resize_vector(); diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr index 7f4b9f7b67a41..4a25cafcddf26 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -1,5 +1,5 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:13:20 + --> tests/ui/slow_vector_initialization.rs:14:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -11,7 +11,7 @@ LL | | vec1.extend(repeat(0).take(len)); = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:19:20 + --> tests/ui/slow_vector_initialization.rs:20:20 | LL | let mut vec2 = Vec::with_capacity(len - 10); | ____________________^ @@ -20,7 +20,7 @@ LL | | vec2.extend(repeat(0).take(len - 10)); | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:27:20 + --> tests/ui/slow_vector_initialization.rs:28:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -29,7 +29,7 @@ LL | | vec4.extend(repeat(0).take(vec4.capacity())); | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:38:27 + --> tests/ui/slow_vector_initialization.rs:39:27 | LL | let mut resized_vec = Vec::with_capacity(30); | ___________________________^ @@ -38,7 +38,7 @@ LL | | resized_vec.resize(30, 0); | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:42:26 + --> tests/ui/slow_vector_initialization.rs:43:26 | LL | let mut extend_vec = Vec::with_capacity(30); | __________________________^ @@ -47,7 +47,7 @@ LL | | extend_vec.extend(repeat(0).take(30)); | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:50:20 + --> tests/ui/slow_vector_initialization.rs:51:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -56,7 +56,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:59:20 + --> tests/ui/slow_vector_initialization.rs:60:20 | LL | let mut vec3 = Vec::with_capacity(len - 10); | ____________________^ @@ -65,7 +65,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:63:20 + --> tests/ui/slow_vector_initialization.rs:64:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -74,7 +74,7 @@ LL | | vec4.resize(vec4.capacity(), 0); | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:68:12 + --> tests/ui/slow_vector_initialization.rs:69:12 | LL | vec1 = Vec::with_capacity(10); | ____________^ @@ -83,7 +83,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:76:20 + --> tests/ui/slow_vector_initialization.rs:77:20 | LL | let mut vec1 = Vec::new(); | ____________________^ @@ -92,7 +92,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:81:20 + --> tests/ui/slow_vector_initialization.rs:82:20 | LL | let mut vec3 = Vec::new(); | ____________________^ @@ -101,7 +101,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:86:12 + --> tests/ui/slow_vector_initialization.rs:87:12 | LL | vec1 = Vec::new(); | ____________^ @@ -110,7 +110,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:90:12 + --> tests/ui/slow_vector_initialization.rs:91:12 | LL | vec1 = vec![]; | ____________^ diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index efea28e7045c8..5a6e77a06b8f5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -82,3 +82,20 @@ fn msrv_1_81() { // is_none_or added in 1.82.0 let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 }); } + +fn with_refs(o: &mut Option) -> bool { + o.is_none_or(|n| n > 5) || (o as &Option).is_none_or(|n| n < 5) +} + +struct S; + +impl std::ops::Deref for S { + type Target = Option; + fn deref(&self) -> &Self::Target { + &Some(0) + } +} + +fn with_deref(o: &S) -> bool { + o.is_none_or(|n| n > 5) +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index 05a0ca816ef6d..5ba63121659fc 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -85,3 +85,20 @@ fn msrv_1_81() { // is_none_or added in 1.82.0 let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 }); } + +fn with_refs(o: &mut Option) -> bool { + o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +} + +struct S; + +impl std::ops::Deref for S { + type Target = Option; + fn deref(&self) -> &Self::Target { + &Some(0) + } +} + +fn with_deref(o: &S) -> bool { + o.map_or(true, |n| n > 5) +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index 2b78996d5f3e3..2ae327f0bf88e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -2,16 +2,25 @@ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:13:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) == Some(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` +help: use a standard comparison instead + | +LL | let _ = Some(5) == Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:14:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) != Some(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = Some(5) != Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:15:13 @@ -21,7 +30,12 @@ LL | let _ = Some(5).map_or(false, |n| { LL | | let _ = 1; LL | | n == 5 LL | | }); - | |______^ help: use a standard comparison instead: `Some(5) == Some(5)` + | |______^ + | +help: use a standard comparison instead + | +LL | let _ = Some(5) == Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:19:13 @@ -35,113 +49,243 @@ LL | | }); | help: use is_some_and instead | -LL ~ let _ = Some(5).is_some_and(|n| { -LL + let _ = n; -LL + 6 >= 5 -LL ~ }); +LL - let _ = Some(5).map_or(false, |n| { +LL + let _ = Some(5).is_some_and(|n| { | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:23:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(vec![5]).map_or(false, |n| n == [5]); +LL + let _ = Some(vec![5]).is_some_and(|n| n == [5]); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:24:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); +LL + let _ = Some(vec![1]).is_some_and(|n| vec![2] == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| n == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, |n| n == n); +LL + let _ = Some(5).is_some_and(|n| n == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:26:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); +LL + let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:27:13 | LL | let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::, i32>(vec![5]).is_ok_and(|n| n == [5])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); +LL + let _ = Ok::, i32>(vec![5]).is_ok_and(|n| n == [5]); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:28:13 | LL | let _ = Ok::(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Ok::(5) == Ok(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = Ok::(5) == Ok(5); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:29:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)).then(|| 1); + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, |n| n == 5); +LL + let _ = Some(5).is_none_or(|n| n == 5); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:31:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, |n| 5 == n); +LL + let _ = Some(5).is_none_or(|n| 5 == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:32:14 | LL | let _ = !Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = !(Some(5) == Some(5)); + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:33:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) || false; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)) || false; + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:34:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)) as usize; + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:58:13 | LL | let _ = r.map_or(false, |x| x == 7); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = r.map_or(false, |x| x == 7); +LL + let _ = r.is_ok_and(|x| x == 7); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:63:13 | LL | let _ = r.map_or(false, func); - | ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = r.map_or(false, func); +LL + let _ = r.is_ok_and(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:64:13 | LL | let _ = Some(5).map_or(false, func); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, func); +LL + let _ = Some(5).is_some_and(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:65:13 | LL | let _ = Some(5).map_or(true, func); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, func); +LL + let _ = Some(5).is_none_or(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:70:13 | LL | let _ = r.map_or(false, |x| x == 8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `r == Ok(8)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = r == Ok(8); + | ~~~~~~~~~~ + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:90:5 + | +LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +LL + o.is_none_or(|n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:90:34 + | +LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +LL + o.map_or(true, |n| n > 5) || (o as &Option).is_none_or(|n| n < 5) + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:103:5 + | +LL | o.map_or(true, |n| n > 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) +LL + o.is_none_or(|n| n > 5) + | -error: aborting due to 21 previous errors +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed new file mode 100644 index 0000000000000..7a3b79553dea8 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.stderr b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.stderr new file mode 100644 index 0000000000000..ccff33084172e --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.stderr @@ -0,0 +1,23 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:29:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:35:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:56:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed new file mode 100644 index 0000000000000..d186d5e7ebc4e --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.stderr b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.stderr new file mode 100644 index 0000000000000..4e526af2147db --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.stderr @@ -0,0 +1,29 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:29:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:35:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:47:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:56:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.fixed new file mode 100644 index 0000000000000..36d5c7806fe84 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.fixed @@ -0,0 +1,32 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs new file mode 100644 index 0000000000000..3028c5b27b34d --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + }; + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + }; + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.stderr b/src/tools/clippy/tests/ui/unnecessary_semicolon.stderr new file mode 100644 index 0000000000000..e6bf36e81e886 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.stderr @@ -0,0 +1,17 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:24:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:30:6 + | +LL | }; + | ^ help: remove + +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 index fdcac8fb08dcf..027dac419375b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -585,3 +585,9 @@ fn borrow_checks() { HashSet::::new().foo::<&str>(&"".to_owned()); HashSet::::new().get(&1.to_string()); } + +fn issue13624() -> impl IntoIterator { + let cow: Cow<'_, Vec> = Cow::Owned(vec![String::from("foo")]); + + cow.into_owned().into_iter() +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 10a9727a9a798..b89f3d552f84d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -585,3 +585,9 @@ fn borrow_checks() { HashSet::::new().foo::<&str>(&"".to_owned()); HashSet::::new().get(&1.to_string()); } + +fn issue13624() -> impl IntoIterator { + let cow: Cow<'_, Vec> = Cow::Owned(vec![String::from("foo")]); + + cow.into_owned().into_iter() +} diff --git a/src/tools/clippy/tests/ui/unneeded_struct_pattern.fixed b/src/tools/clippy/tests/ui/unneeded_struct_pattern.fixed new file mode 100644 index 0000000000000..5bd269896a6b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_struct_pattern.fixed @@ -0,0 +1,177 @@ +//@aux-build:non-exhaustive-enum.rs +#![allow( + clippy::manual_unwrap_or_default, + clippy::manual_unwrap_or, + clippy::redundant_pattern_matching +)] +#![warn(clippy::unneeded_struct_pattern)] + +extern crate non_exhaustive_enum; +use non_exhaustive_enum::*; + +fn noop() {} + +fn main() { + match Some(114514) { + Some(v) => v, + None => 0, + }; + + match Some(1919810) { + Some(v) => v, + None => 0, + }; + + match Some(123456) { + Some(v) => v, + None => 0, + }; + + match Some(Some(123456)) { + Some(Some(v)) => v, + Some(None) => 0, + None => 0, + }; + + if let None = Some(0) {} + if let None = Some(0) {} + if let Some(None) = Some(Some(0)) {} + let None = Some(0) else { panic!() }; + let None = Some(0) else { panic!() }; + let Some(None) = Some(Some(0)) else { panic!() }; + + enum Custom { + HasFields { + field: i32, + }, + HasBracketsNoFields {}, + NoBrackets, + #[non_exhaustive] + NoBracketsNonExhaustive, + Init, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields {} => 0, + Custom::NoBrackets => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields { .. } => 0, + Custom::NoBrackets => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets if true => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets | Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + if let Custom::HasFields { field: value } = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields {} = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields { .. } = Custom::Init { + noop(); + } + if let Custom::NoBrackets = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets | Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + + let Custom::HasFields { field: value } = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields {} = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields { .. } = Custom::Init else { + panic!() + }; + let Custom::NoBrackets = Custom::Init else { panic!() }; //~ ERROR: struct pattern is not needed for a unit variant + + let Custom::NoBrackets = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + + enum Refutable { + Variant, + } + + fn pat_in_fn_param_1(Refutable::Variant: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + fn pat_in_fn_param_2(Refutable::Variant: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + + for Refutable::Variant in [] {} //~ ERROR: struct pattern is not needed for a unit variant + for Refutable::Variant in [] {} //~ ERROR: struct pattern is not needed for a unit variant +} + +fn external_crate() { + use ExtNonExhaustiveVariant::*; + + match ExhaustiveUnit { + // Expected + ExhaustiveUnit => 0, + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + ExhaustiveUnit => 0, + // vvvvv Non-exhaustive variants, should all be ignored + Unit { .. } => 0, + Tuple { 0: field, .. } => field, + StructNoField { .. } => 0, + Struct { field, .. } => field, + _ => 0, + }; +} diff --git a/src/tools/clippy/tests/ui/unneeded_struct_pattern.rs b/src/tools/clippy/tests/ui/unneeded_struct_pattern.rs new file mode 100644 index 0000000000000..c7658617ad376 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_struct_pattern.rs @@ -0,0 +1,177 @@ +//@aux-build:non-exhaustive-enum.rs +#![allow( + clippy::manual_unwrap_or_default, + clippy::manual_unwrap_or, + clippy::redundant_pattern_matching +)] +#![warn(clippy::unneeded_struct_pattern)] + +extern crate non_exhaustive_enum; +use non_exhaustive_enum::*; + +fn noop() {} + +fn main() { + match Some(114514) { + Some(v) => v, + None {} => 0, + }; + + match Some(1919810) { + Some(v) => v, + None { .. } => 0, + }; + + match Some(123456) { + Some(v) => v, + None => 0, + }; + + match Some(Some(123456)) { + Some(Some(v)) => v, + Some(None {}) => 0, + None {} => 0, + }; + + if let None {} = Some(0) {} + if let None { .. } = Some(0) {} + if let Some(None {}) = Some(Some(0)) {} + let None {} = Some(0) else { panic!() }; + let None { .. } = Some(0) else { panic!() }; + let Some(None {}) = Some(Some(0)) else { panic!() }; + + enum Custom { + HasFields { + field: i32, + }, + HasBracketsNoFields {}, + NoBrackets, + #[non_exhaustive] + NoBracketsNonExhaustive, + Init, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields {} => 0, + Custom::NoBrackets {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields { .. } => 0, + Custom::NoBrackets { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets {} if true => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + if let Custom::HasFields { field: value } = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields {} = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields { .. } = Custom::Init { + noop(); + } + if let Custom::NoBrackets {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets { .. } = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive { .. } = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + + let Custom::HasFields { field: value } = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields {} = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields { .. } = Custom::Init else { + panic!() + }; + let Custom::NoBrackets {} = Custom::Init else { panic!() }; //~ ERROR: struct pattern is not needed for a unit variant + + let Custom::NoBrackets { .. } = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive {} = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive { .. } = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + + enum Refutable { + Variant, + } + + fn pat_in_fn_param_1(Refutable::Variant {}: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + fn pat_in_fn_param_2(Refutable::Variant { .. }: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + + for Refutable::Variant {} in [] {} //~ ERROR: struct pattern is not needed for a unit variant + for Refutable::Variant { .. } in [] {} //~ ERROR: struct pattern is not needed for a unit variant +} + +fn external_crate() { + use ExtNonExhaustiveVariant::*; + + match ExhaustiveUnit { + // Expected + ExhaustiveUnit => 0, + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + ExhaustiveUnit => 0, + // vvvvv Non-exhaustive variants, should all be ignored + Unit { .. } => 0, + Tuple { 0: field, .. } => field, + StructNoField { .. } => 0, + Struct { field, .. } => field, + _ => 0, + }; +} diff --git a/src/tools/clippy/tests/ui/unneeded_struct_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_struct_pattern.stderr new file mode 100644 index 0000000000000..3a7f595838020 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_struct_pattern.stderr @@ -0,0 +1,203 @@ +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:17:13 + | +LL | None {} => 0, + | ^^^ help: remove the struct pattern + | + = note: `-D clippy::unneeded-struct-pattern` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unneeded_struct_pattern)]` + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:22:13 + | +LL | None { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:32:18 + | +LL | Some(None {}) => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:33:13 + | +LL | None {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:36:16 + | +LL | if let None {} = Some(0) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:37:16 + | +LL | if let None { .. } = Some(0) {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:38:21 + | +LL | if let Some(None {}) = Some(Some(0)) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:39:13 + | +LL | let None {} = Some(0) else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:40:13 + | +LL | let None { .. } = Some(0) else { panic!() }; + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:41:18 + | +LL | let Some(None {}) = Some(Some(0)) else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:57:27 + | +LL | Custom::NoBrackets {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:58:40 + | +LL | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:65:27 + | +LL | Custom::NoBrackets { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:66:40 + | +LL | Custom::NoBracketsNonExhaustive { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:71:27 + | +LL | Custom::NoBrackets {} if true => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:76:27 + | +LL | Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:76:64 + | +LL | Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:89:30 + | +LL | if let Custom::NoBrackets {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:93:30 + | +LL | if let Custom::NoBrackets { .. } = Custom::Init { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:97:30 + | +LL | if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:97:67 + | +LL | if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:101:43 + | +LL | if let Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:105:43 + | +LL | if let Custom::NoBracketsNonExhaustive { .. } = Custom::Init { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:121:27 + | +LL | let Custom::NoBrackets {} = Custom::Init else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:123:27 + | +LL | let Custom::NoBrackets { .. } = Custom::Init else { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:127:40 + | +LL | let Custom::NoBracketsNonExhaustive {} = Custom::Init else { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:131:40 + | +LL | let Custom::NoBracketsNonExhaustive { .. } = Custom::Init else { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:140:44 + | +LL | fn pat_in_fn_param_1(Refutable::Variant {}: Refutable) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:141:44 + | +LL | fn pat_in_fn_param_2(Refutable::Variant { .. }: Refutable) {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:143:27 + | +LL | for Refutable::Variant {} in [] {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:144:27 + | +LL | for Refutable::Variant { .. } in [] {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:158:23 + | +LL | ExhaustiveUnit { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:164:23 + | +LL | ExhaustiveUnit {} => 0, + | ^^^ help: remove the struct pattern + +error: aborting due to 33 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 2f7edd92bb7c7..697d437b3885c 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -342,3 +342,56 @@ fn gen_identity(x: [T; 3]) -> Vec { x.into_iter().collect() //~^ useless_conversion } + +mod issue11819 { + fn takes_into_iter(_: impl IntoIterator) {} + + pub struct MyStruct { + my_field: T, + } + + impl MyStruct { + pub fn with_ref<'a>(&'a mut self) + where + &'a T: IntoIterator, + { + takes_into_iter(&self.my_field); + //~^ useless_conversion + } + + pub fn with_ref_mut<'a>(&'a mut self) + where + &'a mut T: IntoIterator, + { + takes_into_iter(&mut self.my_field); + //~^ useless_conversion + } + + pub fn with_deref(&mut self) + where + T: std::ops::Deref, + Y: IntoIterator + Copy, + { + takes_into_iter(*self.my_field); + //~^ useless_conversion + } + + pub fn with_reborrow<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref, + &'a Y: IntoIterator, + { + takes_into_iter(&*self.my_field); + //~^ useless_conversion + } + + pub fn with_reborrow_mut<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref + std::ops::DerefMut, + &'a mut Y: IntoIterator, + { + takes_into_iter(&mut *self.my_field); + //~^ useless_conversion + } + } +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index eacdf77f90520..4d8ad61a8c995 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -342,3 +342,56 @@ fn gen_identity(x: [T; 3]) -> Vec { x.into_iter().map(Into::into).collect() //~^ useless_conversion } + +mod issue11819 { + fn takes_into_iter(_: impl IntoIterator) {} + + pub struct MyStruct { + my_field: T, + } + + impl MyStruct { + pub fn with_ref<'a>(&'a mut self) + where + &'a T: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_ref_mut<'a>(&'a mut self) + where + &'a mut T: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_deref(&mut self) + where + T: std::ops::Deref, + Y: IntoIterator + Copy, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_reborrow<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref, + &'a Y: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_reborrow_mut<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref + std::ops::DerefMut, + &'a mut Y: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + } +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 6aeb382902ba1..ed50f3071862e 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -122,7 +122,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:189:7 | LL | b(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -134,7 +136,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:190:7 | LL | c(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:180:18 @@ -146,7 +150,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:191:7 | LL | d(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:183:12 @@ -158,7 +164,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> 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]` + | ^^^^^^^^^^------------------------ + | | + | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -170,7 +178,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> 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]` + | ^^^^^^^^^^------------------------------------ + | | + | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -182,7 +192,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:241:24 | LL | foo2::([1, 2, 3].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:220:12 @@ -194,7 +206,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:249:14 | LL | foo3([1, 2, 3].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:229:12 @@ -206,7 +220,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:258:16 | LL | S1.foo([1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` + | ^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:255:27 @@ -218,7 +234,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:277:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); - | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` + | ^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:264:20 @@ -274,5 +292,90 @@ error: useless conversion to the same type: `T` LL | x.into_iter().map(Into::into).collect() | ^^^^^^^^^^^^^^^^ help: consider removing -error: aborting due to 36 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:358:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:366:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&mut self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:375:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(*self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:384:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&*self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:393:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&mut *self.my_field); + | + +error: aborting due to 41 previous errors diff --git a/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.fixed b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.fixed new file mode 100644 index 0000000000000..03b34afa54ec9 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.fixed @@ -0,0 +1,52 @@ +#![warn(clippy::useless_nonzero_new_unchecked)] + +use std::num::{NonZero, NonZeroUsize}; + +#[clippy::msrv = "1.83"] +const fn func() -> NonZeroUsize { + const { NonZeroUsize::new(3).unwrap() } + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context +} + +#[clippy::msrv = "1.82"] +const fn func_older() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_performance_hit_if_linted() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(x) } +} + +macro_rules! uns { + ($expr:expr) => { + unsafe { $expr } + }; +} + +macro_rules! nzu { + () => { + NonZeroUsize::new_unchecked(1) + }; +} + +fn main() { + const _A: NonZeroUsize = NonZeroUsize::new(3).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + static _B: NonZero = NonZero::::new(42).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _C: usize = unsafe { NonZeroUsize::new(3).unwrap().get() }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const AUX: usize = 3; + const _D: NonZeroUsize = NonZeroUsize::new(AUX).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3)); + const _Y: NonZeroUsize = unsafe { nzu!() }; +} diff --git a/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.rs b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.rs new file mode 100644 index 0000000000000..d450e3a03ec58 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.rs @@ -0,0 +1,52 @@ +#![warn(clippy::useless_nonzero_new_unchecked)] + +use std::num::{NonZero, NonZeroUsize}; + +#[clippy::msrv = "1.83"] +const fn func() -> NonZeroUsize { + const { unsafe { NonZeroUsize::new_unchecked(3) } } + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context +} + +#[clippy::msrv = "1.82"] +const fn func_older() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_performance_hit_if_linted() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(x) } +} + +macro_rules! uns { + ($expr:expr) => { + unsafe { $expr } + }; +} + +macro_rules! nzu { + () => { + NonZeroUsize::new_unchecked(1) + }; +} + +fn main() { + const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + static _B: NonZero = unsafe { NonZero::::new_unchecked(42) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const AUX: usize = 3; + const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3)); + const _Y: NonZeroUsize = unsafe { nzu!() }; +} diff --git a/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.stderr b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.stderr new file mode 100644 index 0000000000000..adb146167633e --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.stderr @@ -0,0 +1,37 @@ +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:7:13 + | +LL | const { unsafe { NonZeroUsize::new_unchecked(3) } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + | + = note: `-D clippy::useless-nonzero-new-unchecked` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_nonzero_new_unchecked)]` + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:37:30 + | +LL | const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + +error: `NonZero::::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:40:30 + | +LL | static _B: NonZero = unsafe { NonZero::::new_unchecked(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZero::::new(42).unwrap()` + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:43:32 + | +LL | const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + | + = note: the fixed expression does not require an `unsafe` context + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:47:30 + | +LL | const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(AUX).unwrap()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs index e29898f068d37..ed357137095ba 100644 --- a/src/tools/clippy/tests/versioncheck.rs +++ b/src/tools/clippy/tests/versioncheck.rs @@ -88,5 +88,5 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { _ => { panic!("Failed to parse rustc version: {vsplit:?}"); }, - }; + } } diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index eadfd7107c77b..3d35116ebc178 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -29,7 +29,6 @@ users_on_vacation = [ "*" = [ "@Manishearth", "@llogiq", - "@xFrednet", "@Alexendoo", "@dswij", "@Jarcho", diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index c2197b89c566e..34d76ad642ecd 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -341,8 +341,8 @@ window.filters = { || !filters.levels_filter[lint.level] || !filters.applicabilities_filter[lint.applicability] || !(filters.version_filter["="] === null || lint.version === filters.version_filter["="]) - || !(filters.version_filter["≥"] === null || lint.version > filters.version_filter["≥"]) - || !(filters.version_filter["≤"] === null || lint.version < filters.version_filter["≤"]) + || !(filters.version_filter["≥"] === null || lint.version >= filters.version_filter["≥"]) + || !(filters.version_filter["≤"] === null || lint.version <= filters.version_filter["≤"]) ); if (lint.filteredOut || lint.searchFilteredOut) { lint.elem.style.display = "none";