diff --git a/Cargo.lock b/Cargo.lock index 271a2f7962cac..43b36305ef35c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,12 +351,6 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - [[package]] name = "camino" version = "1.1.10" @@ -569,22 +563,16 @@ dependencies = [ "color-print", "declare_clippy_lint", "filetime", - "futures", - "if_chain", "itertools", - "parking_lot", "pulldown-cmark", - "quote", "regex", "rustc_tools_util 0.4.2", "serde", "serde_json", - "syn 2.0.104", "tempfile", - "termize 0.1.1", - "tokio", + "termize", "toml 0.7.8", - "ui_test", + "ui_test 0.30.2", "walkdir", ] @@ -1322,95 +1310,6 @@ dependencies = [ "new_debug_unreachable", ] -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "generate-copyright" version = "0.1.0" @@ -1848,12 +1747,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "ignore" version = "0.4.23" @@ -1947,17 +1840,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "io-uring" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipc-channel" version = "0.19.0" @@ -2409,7 +2291,7 @@ dependencies = [ "smallvec", "tempfile", "tikv-jemalloc-sys", - "ui_test", + "ui_test 0.29.2", ] [[package]] @@ -2868,12 +2750,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkg-config" version = "0.3.32" @@ -3773,7 +3649,7 @@ dependencies = [ "serde", "serde_json", "termcolor", - "termize 0.2.0", + "termize", "tracing", "windows 0.61.3", ] @@ -4536,7 +4412,7 @@ dependencies = [ "rustc_serialize", "rustc_span", "rustc_target", - "termize 0.2.0", + "termize", "tracing", "windows 0.61.3", ] @@ -5035,12 +4911,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" -[[package]] -name = "slab" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" - [[package]] name = "smallvec" version = "1.15.1" @@ -5067,6 +4937,17 @@ dependencies = [ "color-eyre", ] +[[package]] +name = "spanned" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4b0c055fde758f086eb4a6e73410247df8a3837fd606d2caeeaf72aa566d" +dependencies = [ + "anyhow", + "bstr", + "color-eyre", +] + [[package]] name = "spdx-expression" version = "0.5.2" @@ -5305,16 +5186,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "termize" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "termize" version = "0.2.0" @@ -5480,21 +5351,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tokio" -version = "1.46.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" -dependencies = [ - "backtrace", - "bytes", - "io-uring", - "libc", - "mio", - "pin-project-lite", - "slab", -] - [[package]] name = "toml" version = "0.5.11" @@ -5726,7 +5582,33 @@ dependencies = [ "rustfix", "serde", "serde_json", - "spanned", + "spanned 0.3.0", +] + +[[package]] +name = "ui_test" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b56a6897cc4bb6f8daf1939b0b39cd9645856997f46f4d0b3e3cb7122dfe9251" +dependencies = [ + "annotate-snippets 0.11.5", + "anyhow", + "bstr", + "cargo-platform", + "cargo_metadata 0.18.1", + "color-eyre", + "colored", + "comma", + "crossbeam-channel", + "indicatif", + "levenshtein", + "prettydiff", + "regex", + "rustc_version", + "rustfix", + "serde", + "serde_json", + "spanned 0.4.1", ] [[package]] diff --git a/src/tools/clippy/.github/workflows/feature_freeze.yml b/src/tools/clippy/.github/workflows/feature_freeze.yml index a5f8d4bc145cc..7ad58af77d4ad 100644 --- a/src/tools/clippy/.github/workflows/feature_freeze.yml +++ b/src/tools/clippy/.github/workflows/feature_freeze.yml @@ -1,7 +1,11 @@ name: Feature freeze check on: - pull_request: + pull_request_target: + types: + - opened + branches: + - master paths: - 'clippy_lints/src/declared_lints.rs' @@ -9,6 +13,12 @@ jobs: auto-comment: runs-on: ubuntu-latest + permissions: + pull-requests: write + + # Do not in any case add code that runs anything coming from the the content + # of the pull request, as malicious code would be able to access the private + # GitHub token. steps: - name: Check PR Changes id: pr-changes @@ -19,7 +29,7 @@ jobs: run: | # Use GitHub API to create a comment on the PR PR_NUMBER=${{ github.event.pull_request.number }} - COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to August 1st and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team" + COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team\n\n@rustbot note Feature-freeze\n@rustbot blocked\n@rustbot label +A-lint\n" GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}" diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 70c805903d36e..003d03957399e 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -128,21 +128,27 @@ jobs: - name: Download JSON uses: actions/download-artifact@v4 + - name: Store PR number + run: echo ${{ github.event.pull_request.number }} > pr.txt + - name: Diff results - # GH's summery has a maximum size of 1024k: - # https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary - # That's why we first log to file and then to the summary and logs + # GH's summery has a maximum size of 1MiB: + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#step-isolation-and-limits + # We upload the full diff as an artifact in case it's truncated run: | - ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate >> truncated_diff.md - head -c 1024000 truncated_diff.md >> $GITHUB_STEP_SUMMARY - cat truncated_diff.md - ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json >> full_diff.md + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate | head -c 1M > $GITHUB_STEP_SUMMARY + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --write-summary summary.json > full_diff.md - name: Upload full diff uses: actions/upload-artifact@v4 with: - name: diff - if-no-files-found: ignore + name: full_diff + path: full_diff.md + + - name: Upload summary + uses: actions/upload-artifact@v4 + with: + name: summary path: | - full_diff.md - truncated_diff.md + summary.json + pr.txt diff --git a/src/tools/clippy/.github/workflows/lintcheck_summary.yml b/src/tools/clippy/.github/workflows/lintcheck_summary.yml new file mode 100644 index 0000000000000..52f52e155a07b --- /dev/null +++ b/src/tools/clippy/.github/workflows/lintcheck_summary.yml @@ -0,0 +1,106 @@ +name: Lintcheck summary + +# The workflow_run event runs in the context of the Clippy repo giving it write +# access, needed here to create a PR comment when the PR originates from a fork +# +# The summary artifact is a JSON file that we verify in this action to prevent +# the creation of arbitrary comments +# +# This action must not checkout/run code from the originating workflow_run +# or directly interpolate ${{}} untrusted fields into code +# +# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run +# https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections + +on: + workflow_run: + workflows: [Lintcheck] + types: [completed] + +# Restrict the default permission scope https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defining-access-for-the-github_token-scopes +permissions: + pull-requests: write + +jobs: + download: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: summary + path: untrusted + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - name: Format comment + uses: actions/github-script@v7 + with: + script: | + const fs = require("fs"); + const assert = require("assert/strict"); + + function validateName(s) { + assert.match(s, /^[a-z0-9_:]+$/); + return s; + } + + function validateInt(i) { + assert.ok(Number.isInteger(i)); + return i; + } + + function tryReadSummary() { + try { + return JSON.parse(fs.readFileSync("untrusted/summary.json")); + } catch { + return null; + } + } + + const prNumber = parseInt(fs.readFileSync("untrusted/pr.txt"), 10); + core.exportVariable("PR", prNumber.toString()); + + const untrustedSummary = tryReadSummary(); + if (!Array.isArray(untrustedSummary)) { + return; + } + + let summary = `Lintcheck changes for ${context.payload.workflow_run.head_sha} + + | Lint | Added | Removed | Changed | + | ---- | ----: | ------: | ------: | + `; + + for (const untrustedRow of untrustedSummary) { + const name = validateName(untrustedRow.name); + + const added = validateInt(untrustedRow.added); + const removed = validateInt(untrustedRow.removed); + const changed = validateInt(untrustedRow.changed); + + const id = name.replace("clippy::", "user-content-").replaceAll("_", "-"); + const url = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.payload.workflow_run.id}#${id}`; + + summary += `| [\`${name}\`](${url}) | ${added} | ${removed} | ${changed} |\n`; + } + + summary += "\nThis comment will be updated if you push new changes"; + + fs.writeFileSync("summary.md", summary); + + - name: Create/update comment + run: | + if [[ -f summary.md ]]; then + gh pr comment "$PR" --body-file summary.md --edit-last --create-if-none + else + # There were no changes detected by Lintcheck + # - If a comment exists from a previous run that did detect changes, edit it (--edit-last) + # - If no comment exists do not create one, the `gh` command exits with an error which + # `|| true` ignores + gh pr comment "$PR" --body "No changes for ${{ github.event.workflow_run.head_sha }}" --edit-last || true + fi + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 1278427b5a768..f25f9ab54dd29 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -28,13 +28,13 @@ declare_clippy_lint = { path = "declare_clippy_lint" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } clippy_lints_internal = { path = "clippy_lints_internal", optional = true } tempfile = { version = "3.20", optional = true } -termize = "0.1" +termize = "0.2" color-print = "0.3.4" anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.29.2" +ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" @@ -45,14 +45,6 @@ itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } -# UI test dependencies -if_chain = "1.0" -quote = "1.0.25" -syn = { version = "2.0", features = ["full"] } -futures = "0.3" -parking_lot = "0.12" -tokio = { version = "1", features = ["io-util"] } - [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } diff --git a/src/tools/clippy/book/src/development/infrastructure/backport.md b/src/tools/clippy/book/src/development/infrastructure/backport.md index 9526d8af1c9dc..47ea6a412c503 100644 --- a/src/tools/clippy/book/src/development/infrastructure/backport.md +++ b/src/tools/clippy/book/src/development/infrastructure/backport.md @@ -109,4 +109,4 @@ worth backporting this. When a PR is backported to Rust `beta`, label the PR with `beta-accepted`. This will then get picked up when [writing the changelog]. -[writing the changelog]: changelog_update.md#31-include-beta-accepted-prs +[writing the changelog]: changelog_update.md#4-include-beta-accepted-prs diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index eede6b78d9216..c96ff228b01af 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -38,7 +38,7 @@ Usually you want to write the changelog of the **upcoming stable release**. Make sure though, that `beta` was already branched in the Rust repository. To find the commit hash, issue the following command when in a `rust-lang/rust` -checkout: +checkout (most of the time on the `upstream/beta` branch): ``` git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" ``` @@ -48,16 +48,13 @@ git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into Once you've got the correct commit range, run ``` -util/fetch_prs_between.sh commit1 commit2 > changes.txt +util/fetch_prs_between.sh start_commit end_commit > changes.txt ``` -where `commit2` is the commit hash from the previous command and `commit1` -is the commit hash from the current CHANGELOG file. +where `end_commit` is the commit hash from the previous command and `start_commit` +is [the commit hash][beta_section] from the current CHANGELOG file. Open `changes.txt` file in your editor of choice. -When updating the changelog it's also a good idea to make sure that `commit1` is -already correct in the current changelog. - ### 3. Authoring the final changelog The above script should have dumped all the relevant PRs to the file you @@ -70,17 +67,7 @@ With the PRs filtered, you can start to take each PR and move the `changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it somewhat coherent. -The order should roughly be: - -1. New lints -2. Moves or deprecations of lints -3. Changes that expand what code existing lints cover -4. False positive fixes -5. ICE fixes -6. Documentation improvements -7. Others - -As section headers, we use: +The sections order should roughly be: ``` ### New Lints @@ -97,10 +84,10 @@ As section headers, we use: ### Others ``` -Please also be sure to update the Beta/Unreleased sections at the top with the -relevant commit ranges. +Please also be sure to update [the `Unreleased/Beta/In Rust Nightly` section][beta_section] at the top with the +relevant commits ranges and to add the `Rust ` section with release date and PR ranges. -#### 3.1 Include `beta-accepted` PRs +### 4. Include `beta-accepted` PRs Look for the [`beta-accepted`] label and make sure to also include the PRs with that label in the changelog. If you can, remove the `beta-accepted` labels @@ -109,7 +96,7 @@ that label in the changelog. If you can, remove the `beta-accepted` labels > _Note:_ Some of those PRs might even get backported to the previous `beta`. > Those have to be included in the changelog of the _previous_ release. -### 4. Update `clippy::version` attributes +### 5. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added lints contain the correct version. @@ -129,3 +116,4 @@ written for. If not, update the version to the changelog version. [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases [`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ +[beta_section]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#unreleased--beta--in-rust-nightly diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index e9b7f42a18310..992ed2c6aaaaa 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -892,6 +892,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) +* [`zero_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr) ## `pass-by-value-size-limit` diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 841facdca06db..555f54bcfb8b8 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -794,6 +794,7 @@ define_Conf! { unnested_or_patterns, unused_trait_names, use_self, + zero_ptr, )] msrv: Msrv = Msrv::default(), /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index c1b6b37070696..bd9e57c9f6dac 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -223,7 +223,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if check { return Err(Error::CheckFailed); } - fs::write(path, new_text.as_bytes())?; + fs::write(path, new_text)?; } Ok(()) } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 3361443196ab5..40aadf4589a77 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -2,7 +2,6 @@ rustc_private, exit_status_error, if_let_guard, - let_chains, os_str_slice, os_string_truncate, slice_split_once 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 8b6bfaebbe583..c410a5da775de 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 @@ -266,7 +266,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { .tcx .hir_attrs(item.hir_id()) .iter() - .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr{ .. }))) + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr { .. }))) { // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API. return; diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index ad0a4f8cdf35a..e3b125a8d5b9d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -18,7 +18,8 @@ pub(super) fn check<'tcx>( cast_to: &'tcx Ty<'_>, msrv: Msrv, ) -> bool { - if matches!(cast_to.kind, TyKind::Ptr(_)) + if let TyKind::Ptr(target) = cast_to.kind + && !matches!(target.ty.kind, TyKind::TraitObject(..)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index a2ecb5fb44ae0..2eebe84923274 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use clippy_utils::{expr_or_init, sym}; +use clippy_utils::{expr_or_init, is_in_const_context, sym}; use rustc_abi::IntegerType; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; @@ -168,7 +168,9 @@ pub(super) fn check( span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, msg, |diag| { diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ..."); - if !cast_from.is_floating_point() { + // TODO: Remove the condition for const contexts when `try_from` and other commonly used methods + // become const fn. + if !is_in_const_context(cx) && !cast_from.is_floating_point() { offer_suggestion(cx, expr, cast_expr, cast_to_span, diag); } }); diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index daae9a8bb0850..37accff5eaa81 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -878,7 +878,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); - zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir, self.msrv); if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) { manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir); diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs index a34af6bc226c4..f4738e7b0d51d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; @@ -7,10 +8,10 @@ use rustc_lint::LateContext; use super::ZERO_PTR; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>, msrv: Msrv) { if let TyKind::Ptr(ref mut_ty) = to.kind && is_integer_literal(from, 0) - && !is_in_const_context(cx) + && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::PTR_NULL)) && let Some(std_or_core) = std_or_core(cx) { let (msg, sugg_fn) = match mut_ty.mutbl { diff --git a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs index 2b659253763fd..6217fc4c8977c 100644 --- a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs +++ b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; +use clippy_utils::sugg::{self, Sugg}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt}; use rustc_session::declare_lint_pass; @@ -49,23 +50,18 @@ declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]); impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - // If this expression has an effective type of `&dyn Any` ... - { - let coerced_ty = cx.typeck_results().expr_ty_adjusted(e); - - let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else { - return; - }; - if !is_dyn_any(cx.tcx, coerced_ref_ty) { - return; - } + // If this expression was coerced to `&dyn Any` ... + if !cx.typeck_results().expr_adjustments(e).last().is_some_and(|adj| { + matches!(adj.kind, Adjust::Pointer(PointerCoercion::Unsize)) && is_ref_dyn_any(cx.tcx, adj.target) + }) { + return; } let expr_ty = cx.typeck_results().expr_ty(e); let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else { return; }; - // ... but only due to coercion ... + // ... but it's not actually `&dyn Any` ... if is_dyn_any(cx.tcx, expr_ref_ty) { return; } @@ -78,23 +74,37 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { } // ... that's probably not intended. - let (span, deref_count) = match e.kind { + let (target_expr, deref_count) = match e.kind { // If `e` was already an `&` expression, skip `*&` in the suggestion - ExprKind::AddrOf(_, _, referent) => (referent.span, depth), - _ => (e.span, depth + 1), + ExprKind::AddrOf(_, _, referent) => (referent, depth), + _ => (e, depth + 1), }; + let ty::Ref(_, _, mutability) = *cx.typeck_results().expr_ty_adjusted(e).kind() else { + return; + }; + let sugg = sugg::make_unop( + &format!("{}{}", mutability.ref_prefix_str(), str::repeat("*", deref_count)), + Sugg::hir(cx, target_expr, ".."), + ); span_lint_and_sugg( cx, COERCE_CONTAINER_TO_ANY, e.span, - format!("coercing `{expr_ty}` to `&dyn Any`"), + format!("coercing `{expr_ty}` to `{}dyn Any`", mutability.ref_prefix_str()), "consider dereferencing", - format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")), + sugg.to_string(), Applicability::MaybeIncorrect, ); } } +fn is_ref_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { + let ty::Ref(_, ref_ty, _) = *ty.kind() else { + return false; + }; + is_dyn_any(tcx, ref_ty) +} + fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { let ty::Dynamic(traits, ..) = ty.kind() else { return false; diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 5c64216dd92ca..d5d937d913385 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -14,18 +14,25 @@ use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does - /// Checks for methods with high cognitive complexity. + /// We used to think it measured how hard a method is to understand. /// /// ### Why is this bad? - /// Methods of high cognitive complexity tend to be hard to - /// both read and maintain. Also LLVM will tend to optimize small methods better. + /// Ideally, we would like to be able to measure how hard a function is + /// to understand given its context (what we call its Cognitive Complexity). + /// But that's not what this lint does. See "Known problems" /// /// ### Known problems - /// Sometimes it's hard to find a way to reduce the - /// complexity. + /// The true Cognitive Complexity of a method is not something we can + /// calculate using modern technology. This lint has been left in the + /// `nursery` so as to not mislead users into using this lint as a + /// measurement tool. /// - /// ### Example - /// You'll see it when you get the warning. + /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793) + /// + /// ### Lints to consider instead of this + /// + /// * [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting) + /// * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index d1a8590c59b4d..cf964d4b58044 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -89,6 +89,10 @@ impl EarlyLintPass for DisallowedScriptIdents { // Fast path for ascii-only idents. if !symbol_str.is_ascii() && let Some(script) = symbol_str.chars().find_map(|c| { + if c.is_ascii() { + return None; + } + c.script_extension() .iter() .find(|script| !self.whitelist.contains(script)) diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 9ee32fced8c44..3033ac0d0b0b7 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty}; +use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -99,13 +99,16 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { let mut panic_span = None; let typeck = cx.tcx.typeck_body(body_id); for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + if is_inside_always_const_context(cx.tcx, expr.hir_id) { + return ControlFlow::::Continue(()); + } + if let Some(macro_call) = root_macro_call_first_node(cx, expr) && (is_panic(cx, macro_call.def_id) || matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) )) - && !cx.tcx.hir_is_inside_const_context(expr.hir_id) && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) && panic_span.is_none() { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 5ea55e102dfe2..2bf52216b832a 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1249,7 +1249,9 @@ fn looks_like_refdef(doc: &str, range: Range) -> Option> { b'[' => { start = Some(i + offset); }, - b']' if let Some(start) = start => { + b']' if let Some(start) = start + && doc.as_bytes().get(i + offset + 1) == Some(&b':') => + { return Some(start..i + offset + 1); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 7dda3e0fdb91c..8ad092790718b 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -1,12 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_attr_data_structures::AttributeKind; -use rustc_attr_data_structures::find_attr; - declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index cc8e4d7d9e281..487db69027aff 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_entrypoint_fn; use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -7,7 +6,8 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Detects calls to the `exit()` function which terminates the program. + /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()` + /// immediately terminate the program. /// /// ### Why restrict this? /// `exit()` immediately terminates the program with no information other than an exit code. @@ -15,11 +15,24 @@ declare_clippy_lint! { /// /// Codebases may use this lint to require that all exits are performed either by panicking /// (which produces a message, a code location, and optionally a backtrace) - /// or by returning from `main()` (which is a single place to look). + /// or by calling `exit()` from `main()` (which is a single place to look). /// - /// ### Example + /// ### Good example /// ```no_run - /// std::process::exit(0) + /// fn main() { + /// std::process::exit(0); + /// } + /// ``` + /// + /// ### Bad example + /// ```no_run + /// fn main() { + /// other_function(); + /// } + /// + /// fn other_function() { + /// std::process::exit(0); + /// } /// ``` /// /// Use instead: @@ -36,7 +49,7 @@ declare_clippy_lint! { #[clippy::version = "1.41.0"] pub EXIT, restriction, - "detects `std::process::exit` calls" + "detects `std::process::exit` calls outside of `main`" } declare_lint_pass!(Exit => [EXIT]); @@ -48,10 +61,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit { && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) && let parent = cx.tcx.hir_get_parent_item(e.hir_id) - && let OwnerNode::Item(Item{kind: ItemKind::Fn{ .. }, ..}) = cx.tcx.hir_owner_node(parent) - // If the next item up is a function we check if it is an entry point + && let OwnerNode::Item(Item{kind: ItemKind::Fn{ ident, .. }, ..}) = cx.tcx.hir_owner_node(parent) + // If the next item up is a function we check if it isn't named "main" // and only then emit a linter warning - && !is_entrypoint_fn(cx, parent.to_def_id()) + + // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before, + // in compilation contexts like --all-targets (which include --tests), you get false positives + // because in a test context, main is not the entrypoint function + && ident.name != sym::main { span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index b3c9e8607589c..d5abaa547e8ec 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -2,8 +2,8 @@ use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, - numeric_literal, peel_blocks, sugg, sym, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, + is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, }; use rustc_ast::ast; use rustc_errors::Applicability; @@ -455,7 +455,6 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&' None } -// TODO: Fix rust-lang/rust-clippy#4735 fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { @@ -491,6 +490,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { return; }; + // Check if any variable in the expression has an ambiguous type (could be f32 or f64) + // see: https://github.com/rust-lang/rust-clippy/issues/14897 + if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _))) + && has_ambiguous_literal_in_expr(cx, recv) + { + return; + } + span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, diff --git a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs index 823cf0f43221c..e809987d75a00 100644 --- a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs @@ -1,11 +1,22 @@ use super::EMPTY_LOOP; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_in_panic_handler, is_no_std_crate}; +use clippy_utils::{is_in_panic_handler, is_no_std_crate, sym}; -use rustc_hir::{Block, Expr}; +use rustc_hir::{Block, Expr, ItemKind, Node}; use rustc_lint::LateContext; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { + let parent_hir_id = cx.tcx.parent_hir_id(expr.hir_id); + if let Node::Item(parent_node) = cx.tcx.hir_node(parent_hir_id) + && matches!(parent_node.kind, ItemKind::Fn { .. }) + && let attrs = cx.tcx.hir_attrs(parent_hir_id) + && attrs.iter().any(|attr| attr.has_name(sym::rustc_intrinsic)) + { + // Intrinsic functions are expanded into an empty loop when lowering the AST + // to simplify the job of later passes which might expect any function to have a body. + return; + } + if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) { 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 9ff82cdcb6648..1f9a943f13dcd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -245,17 +245,36 @@ fn replace_in_pattern( } match pat.kind { - PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else { - break 'a; - }; - let sn_pfx = binding_mode.prefix_str(); - let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); - if let Some(subpt) = opt_subpt { - let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); - return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + PatKind::Binding(ann, _id, binding_name, opt_subpt) => { + match (ident_map.get(&binding_name.name), opt_subpt) { + (Some((pat_to_put, binding_mode)), opt_subpt) => { + let sn_pfx = binding_mode.prefix_str(); + let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); + if let Some(subpt) = opt_subpt { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + } + return format!("{sn_pfx}{sn_ptp}"); + }, + (None, Some(subpt)) => { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + // scanning for a value that matches is not sensitive to order + #[expect(rustc::potential_query_instability)] + if ident_map.values().any(|(other_pat, _)| { + if let PatKind::Binding(_, _, other_name, _) = other_pat.kind { + other_name == binding_name + } else { + false + } + }) { + // this name is shadowed, and, therefore, not usable + return subpt; + } + let binding_pfx = ann.prefix_str(); + return format!("{binding_pfx}{binding_name} @ {subpt}"); + }, + (None, None) => break 'a, } - return format!("{sn_pfx}{sn_ptp}"); }, PatKind::Or(pats) => { let patterns = pats diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 51696b388800f..2d52a93f34e3e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -4,16 +4,15 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_indent; use itertools::Itertools; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; -use rustc_attr_data_structures::find_attr; -use rustc_attr_data_structures::AttributeKind; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index ede68f3094138..ae277da089fd2 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -332,9 +332,7 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => { - Self::LitBytes(byte_sym) - } + LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => Self::LitBytes(byte_sym), LitKind::Byte(val) => Self::LitInt(val.into()), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val.get()), diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs index 4a61c223d2c1e..93325ca488e4e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,14 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, sym}; +use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{Span, Symbol}; use super::MANUAL_IS_VARIANT_AND; @@ -62,54 +64,154 @@ pub(super) fn check( ); } -fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) { - if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo())) - && let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3))) - { - span_lint_and_sugg( - cx, - MANUAL_IS_VARIANT_AND, - parent.span, - format!( - "called `.map() {}= {}()`", - if op == BinOpKind::Eq { '=' } else { '!' }, - if is_option { "Some" } else { "Ok" }, - ), - "use", - if is_option && op == BinOpKind::Ne { - format!("{before_map_snippet}is_none_or{after_map_snippet}",) - } else { +#[derive(Clone, Copy, PartialEq)] +enum Flavor { + Option, + Result, +} + +impl Flavor { + const fn symbol(self) -> Symbol { + match self { + Self::Option => sym::Option, + Self::Result => sym::Result, + } + } + + const fn positive(self) -> Symbol { + match self { + Self::Option => sym::Some, + Self::Result => sym::Ok, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Op { + Eq, + Ne, +} + +impl std::fmt::Display for Op { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Eq => write!(f, "=="), + Self::Ne => write!(f, "!="), + } + } +} + +impl TryFrom for Op { + type Error = (); + fn try_from(op: BinOpKind) -> Result { + match op { + BinOpKind::Eq => Ok(Self::Eq), + BinOpKind::Ne => Ok(Self::Ne), + _ => Err(()), + } + } +} + +/// Represents the argument of the `.map()` function, as a closure or as a path +/// in case η-reduction is used. +enum MapFunc<'hir> { + Closure(&'hir Closure<'hir>), + Path(&'hir Expr<'hir>), +} + +impl<'hir> TryFrom<&'hir Expr<'hir>> for MapFunc<'hir> { + type Error = (); + + fn try_from(expr: &'hir Expr<'hir>) -> Result { + match expr.kind { + ExprKind::Closure(closure) => Ok(Self::Closure(closure)), + ExprKind::Path(_) => Ok(Self::Path(expr)), + _ => Err(()), + } + } +} + +impl<'hir> MapFunc<'hir> { + /// Build a suggestion suitable for use in a `.map()`-like function. η-expansion will be applied + /// as needed. + fn sugg(self, cx: &LateContext<'hir>, invert: bool, app: &mut Applicability) -> String { + match self { + Self::Closure(closure) => { + let body = Sugg::hir_with_applicability(cx, cx.tcx.hir_body(closure.body).value, "..", app); format!( - "{}{before_map_snippet}{}{after_map_snippet}", - if op == BinOpKind::Eq { "" } else { "!" }, - if is_option { "is_some_and" } else { "is_ok_and" }, + "{} {}", + snippet_with_applicability(cx, closure.fn_decl_span, "|..|", app), + if invert { !body } else { body } ) }, - Applicability::MachineApplicable, - ); + Self::Path(expr) => { + let path = snippet_with_applicability(cx, expr.span, "_", app); + if invert { + format!("|x| !{path}(x)") + } else { + path.to_string() + } + }, + } } } +fn emit_lint<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + op: Op, + flavor: Flavor, + in_some_or_ok: bool, + map_func: MapFunc<'tcx>, + recv: &Expr<'_>, +) { + let mut app = Applicability::MachineApplicable; + let recv = snippet_with_applicability(cx, recv.span, "_", &mut app); + + let (invert_expr, method, invert_body) = match (flavor, op) { + (Flavor::Option, Op::Eq) => (false, "is_some_and", !in_some_or_ok), + (Flavor::Option, Op::Ne) => (false, "is_none_or", in_some_or_ok), + (Flavor::Result, Op::Eq) => (false, "is_ok_and", !in_some_or_ok), + (Flavor::Result, Op::Ne) => (true, "is_ok_and", !in_some_or_ok), + }; + span_lint_and_sugg( + cx, + MANUAL_IS_VARIANT_AND, + span, + format!("called `.map() {op} {pos}()`", pos = flavor.positive(),), + "use", + format!( + "{inversion}{recv}.{method}({body})", + inversion = if invert_expr { "!" } else { "" }, + body = map_func.sugg(cx, invert_body, &mut app), + ), + app, + ); +} + pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::Binary(op, left, right) = parent_expr.kind - && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) && op.span.eq_ctxt(expr.span) + && let Ok(op) = Op::try_from(op.node) { // Check `left` and `right` expression in any order, and for `Option` and `Result` for (expr1, expr2) in [(left, right), (right, left)] { - for item in [sym::Option, sym::Result] { - if let ExprKind::Call(call, ..) = expr1.kind + for flavor in [Flavor::Option, Flavor::Result] { + if let ExprKind::Call(call, [arg]) = expr1.kind + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Bool(bool_cst) = lit.node && let ExprKind::Path(QPath::Resolved(_, path)) = call.kind && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res && let ty = cx.typeck_results().expr_ty(expr1) && let ty::Adt(adt, args) = ty.kind() - && cx.tcx.is_diagnostic_item(item, adt.did()) + && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did()) && args.type_at(0).is_bool() - && let ExprKind::MethodCall(_, recv, _, span) = expr2.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item) + && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol()) + && let Ok(map_func) = MapFunc::try_from(map_expr) { - return emit_lint(cx, op.node, parent_expr, span, item == sym::Option); + return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv); } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index 2139466ce7466..6ce7dd3d4d0a8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -136,7 +136,7 @@ pub(super) fn check<'tcx>( fun_span: Option, ) -> bool { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [ + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ (sym::BTreeEntry, false, &[sym::or_insert], "with"), (sym::HashMapEntry, false, &[sym::or_insert], "with"), ( @@ -146,7 +146,9 @@ pub(super) fn check<'tcx>( "else", ), (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Option, true, &[sym::and], "then"), (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), + (sym::Result, true, &[sym::and], "then"), ]; if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs index df8544f92203e..54f38a322b8d9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -1,5 +1,5 @@ use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArg, Ty}; use rustc_span::sym; @@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; -use clippy_utils::{get_parent_expr, peel_blocks}; +use clippy_utils::{peel_blocks, potential_return_of_enclosing_body}; use super::RETURN_AND_THEN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'_>, ) { - if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() { + if !potential_return_of_enclosing_body(cx, expr) { return; } @@ -55,15 +55,28 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - // If suggestion is going to get inserted as part of a `return` expression, it must be blockified. - let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { - let base_indent = indent_of(cx, parent_expr.span); + // If suggestion is going to get inserted as part of a `return` expression or as a match expression + // arm, it must be blockified. + let (parent_span_for_indent, opening_paren, closing_paren) = match cx.tcx.parent_hir_node(expr.hir_id) { + Node::Expr(parent_expr) if matches!(parent_expr.kind, hir::ExprKind::Break(..)) => { + (Some(parent_expr.span), "(", ")") + }, + Node::Expr(parent_expr) => (Some(parent_expr.span), "", ""), + Node::Arm(match_arm) => (Some(match_arm.span), "", ""), + _ => (None, "", ""), + }; + let sugg = if let Some(span) = parent_span_for_indent { + let base_indent = indent_of(cx, span); let inner_indent = base_indent.map(|i| i + 4); format!( "{}\n{}\n{}", - reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent), + reindent_multiline( + &format!("{opening_paren}{{\nlet {arg_snip} = {recv_snip}?;"), + true, + inner_indent + ), reindent_multiline(inner, false, inner_indent), - reindent_multiline("}", false, base_indent), + reindent_multiline(&format!("}}{closing_paren}"), false, base_indent), ) } else { format!( diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs index de729fb343a34..e378cbd6ae0ad 100644 --- a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -4,6 +4,7 @@ use rustc_ast::BorrowKind; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Node, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::Adjust; use rustc_span::sym; use super::SWAP_WITH_TEMPORARY; @@ -11,12 +12,12 @@ use super::SWAP_WITH_TEMPORARY; const MSG_TEMPORARY: &str = "this expression returns a temporary value"; const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value"; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'_>, args: &'tcx [Expr<'_>]) { if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind && let Some(func_def_id) = func_path.res.opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id) { - match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) { + match (ArgKind::new(cx, &args[0]), ArgKind::new(cx, &args[1])) { (ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => { emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp); }, @@ -28,10 +29,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args } enum ArgKind<'tcx> { - // Mutable reference to a place, coming from a macro - RefMutToPlaceAsMacro(&'tcx Expr<'tcx>), - // Place behind a mutable reference - RefMutToPlace(&'tcx Expr<'tcx>), + // Mutable reference to a place, coming from a macro, and number of dereferences to use + RefMutToPlaceAsMacro(&'tcx Expr<'tcx>, usize), + // Place behind a mutable reference, and number of dereferences to use + RefMutToPlace(&'tcx Expr<'tcx>, usize), // Temporary value behind a mutable reference RefMutToTemp(&'tcx Expr<'tcx>), // Any other case @@ -39,13 +40,29 @@ enum ArgKind<'tcx> { } impl<'tcx> ArgKind<'tcx> { - fn new(arg: &'tcx Expr<'tcx>) -> Self { - if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind { - if target.is_syntactic_place_expr() { + /// Build a new `ArgKind` from `arg`. There must be no false positive when returning a + /// `ArgKind::RefMutToTemp` variant, as this may cause a spurious lint to be emitted. + fn new(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Self { + if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind + && let adjustments = cx.typeck_results().expr_adjustments(arg) + && adjustments + .first() + .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None))) + && adjustments + .last() + .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) + { + let extra_derefs = adjustments[1..adjustments.len() - 1] + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + // If a deref is used, `arg` might be a place expression. For example, a mutex guard + // would dereference into the mutex content which is probably not temporary. + if target.is_syntactic_place_expr() || extra_derefs > 0 { if arg.span.from_expansion() { - ArgKind::RefMutToPlaceAsMacro(arg) + ArgKind::RefMutToPlaceAsMacro(arg, extra_derefs) } else { - ArgKind::RefMutToPlace(target) + ArgKind::RefMutToPlace(target, extra_derefs) } } else { ArgKind::RefMutToTemp(target) @@ -106,10 +123,15 @@ fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>, let mut applicability = Applicability::MachineApplicable; let ctxt = expr.span.ctxt(); let assign_target = match target { - ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => { - Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref() - }, - ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + ArgKind::Expr(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref(), + ArgKind::RefMutToPlaceAsMacro(arg, derefs) => (0..*derefs).fold( + Sugg::hir_with_context(cx, arg, ctxt, "_", &mut applicability).deref(), + |sugg, _| sugg.deref(), + ), + ArgKind::RefMutToPlace(target, derefs) => (0..*derefs).fold( + Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + |sugg, _| sugg.deref(), + ), ArgKind::RefMutToTemp(_) => unreachable!(), }; let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability); 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 b90748dd1585f..4a9007c607c88 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 @@ -62,7 +62,8 @@ pub(super) fn check<'a>( let ext_def_span = def.span.until(map.span); - let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind + let (sugg, method, applicability) = if cx.typeck_results().expr_adjustments(recv).is_empty() + && 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() && let ExprKind::Binary(op, l, r) = closure_body_value.kind diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 3ed4b1c2ea947..b3aa1a7286a9c 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -199,11 +199,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { let mut applicability = Applicability::MachineApplicable; let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability); - let sugg = if a == b { + let mut sugg = if a == b { format!("{cond}; {lhs} = {a:?};") } else { format!("{lhs} = {};", if a { cond } else { !cond }) }; + + if is_else_clause(cx.tcx, e) { + sugg = format!("{{ {sugg} }}"); + } + span_lint_and_sugg( cx, NEEDLESS_BOOL_ASSIGN, diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index 442280f999821..946114e104195 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -1,13 +1,13 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::get_parent_expr; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -33,6 +33,19 @@ declare_clippy_lint! { declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); +fn is_in_parens_with_postfix(cx: &LateContext<'_>, mul_expr: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, mul_expr) { + let mult_snippet = snippet(cx, mul_expr.span, ""); + if has_enclosing_paren(&mult_snippet) + && let ExprKind::MethodCall(_, _, _, _) = parent.kind + { + return true; + } + } + + false +} + impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, left, right) = e.kind @@ -40,15 +53,15 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { { match (&left.kind, &right.kind) { (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e, lit, left), _ => {}, } } } } -fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { +fn check_mul(cx: &LateContext<'_>, mul_expr: &Expr<'_>, lit: &Expr<'_>, exp: &Expr<'_>) { const F16_ONE: u16 = 1.0_f16.to_bits(); const F128_ONE: u128 = 1.0_f128.to_bits(); if let ExprKind::Lit(l) = lit.kind @@ -63,8 +76,19 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { && cx.typeck_results().expr_ty(exp).is_numeric() { let mut applicability = Applicability::MachineApplicable; - let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { + let (snip, from_macro) = snippet_with_context(cx, exp.span, mul_expr.span.ctxt(), "..", &mut applicability); + + let needs_parens_for_postfix = is_in_parens_with_postfix(cx, mul_expr); + + let suggestion = if needs_parens_for_postfix { + // Special case: when the multiplication is in parentheses followed by a method call + // we need to preserve the grouping but negate the inner expression. + // Consider this expression: `((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0)` + // We need to end up with: `(-(a.delta - 0.5).abs()).total_cmp(&1.0)` + // Otherwise, without the parentheses we would try to negate an Ordering: + // `-(a.delta - 0.5).abs().total_cmp(&1.0)` + format!("(-{snip})") + } else if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") @@ -72,7 +96,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { span_lint_and_sugg( cx, NEG_MULTIPLY, - span, + mul_expr.span, "this multiplication by -1 can be written more succinctly", "consider using", suggestion, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 02c48166131ef..72e6503e7e495 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::has_drop; +use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{ in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, }; @@ -340,11 +340,13 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option Some(v.iter().collect()), ExprKind::Repeat(inner, _) - | ExprKind::Cast(inner, _) | ExprKind::Type(inner, _) | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => { + reduce_expression(cx, inner).or_else(|| Some(vec![inner])) + }, ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs index 0faa7b9e64665..21e1ab0f4f27d 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_enclosing_block; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -61,12 +61,13 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of both operands", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.multipart_suggestion( "use the values directly", - vec![(left.span, lsnip), (right.span, rsnip)], - Applicability::MachineApplicable, + vec![(left.span, lsnip.to_string()), (right.span, rsnip.to_string())], + applicability, ); }, ); @@ -80,13 +81,9 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of left operand", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); - diag.span_suggestion( - left.span, - "use the left value directly", - lsnip, - Applicability::MachineApplicable, - ); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); + diag.span_suggestion(left.span, "use the left value directly", lsnip, applicability); }, ); } else if !lcpy @@ -99,7 +96,8 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of right operand", |diag| { - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( right.span, "use the right value directly", @@ -131,7 +129,8 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of left operand", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( left.span, "use the left value directly", @@ -158,7 +157,8 @@ pub(crate) fn check<'tcx>( && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) { span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| { - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( right.span, "use the right value directly", 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 84597269a58fa..1c23fe998ad8b 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -5,9 +5,7 @@ use hir::Param; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; -use rustc_hir::{ - ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit, -}; +use rustc_hir::{ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, intravisit as hir_visit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; @@ -198,15 +196,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { hint = hint.asyncify(); } - let is_in_fn_call_arg = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) { - matches!(expr.kind, ExprKind::Call(_, _)) - } else { - false - }; - - // avoid clippy::double_parens - if !is_in_fn_call_arg { - hint = hint.maybe_paren(); + // If the closure body is a block with a single expression, suggest just the inner expression, + // not the block. Example: `(|| { Some(true) })()` should suggest + // `Some(true)` + if let ExprKind::Block(block, _) = body.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + { + hint = Sugg::hir_with_context(cx, expr, full_expr.span.ctxt(), "..", &mut applicability) + .maybe_paren(); } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 442b3250d865f..cf70e883bd098 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; use rustc_attr_data_structures::{StabilityLevel, StableSince}; use rustc_errors::Applicability; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Block, Body, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; @@ -88,49 +88,52 @@ declare_clippy_lint! { } pub struct StdReexports { - lint_point: (Span, Option), + lint_points: Option<(Span, Vec)>, msrv: Msrv, } impl StdReexports { pub fn new(conf: &'static Conf) -> Self { Self { - lint_point: Default::default(), + lint_points: Option::default(), msrv: conf.msrv, } } - fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option)) { - if span.source_equal(self.lint_point.0) { - return; + fn lint_if_finish(&mut self, cx: &LateContext<'_>, krate: Span, lint_point: LintPoint) { + match &mut self.lint_points { + Some((prev_krate, prev_lints)) if prev_krate.overlaps(krate) => { + prev_lints.push(lint_point); + }, + _ => emit_lints(cx, self.lint_points.replace((krate, vec![lint_point]))), } - - if !self.lint_point.0.is_dummy() { - emit_lints(cx, &self.lint_point); - } - - self.lint_point = (span, item); } } impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); -type LintPoint = (&'static Lint, &'static str, &'static str); +#[derive(Debug)] +enum LintPoint { + Available(Span, &'static Lint, &'static str, &'static str), + Conflict, +} impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { - if let Res::Def(_, def_id) = path.res + if let Res::Def(def_kind, def_id) = path.res && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id, self.msrv) && !path.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, &first_segment.ident) + && !matches!(def_kind, DefKind::Macro(_)) + && let Some(last_segment) = path.segments.last() { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; }, }, @@ -138,44 +141,84 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { (ALLOC_INSTEAD_OF_CORE, "alloc", "core") } else { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; } }, - _ => return, + _ => { + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); + return; + }, }; - self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with)))); + self.lint_if_finish( + cx, + first_segment.ident.span, + LintPoint::Available(last_segment.ident.span, lint, used_mod, replace_with), + ); } } fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } } -fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option)) { - let Some((lint, used_mod, replace_with)) = item else { +fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec)>) { + let Some((krate_span, lint_points)) = lint_points else { return; }; - span_lint_and_sugg( - cx, - lint, - *span, - format!("used import from `{used_mod}` instead of `{replace_with}`"), - format!("consider importing the item from `{replace_with}`"), - (*replace_with).to_string(), - Applicability::MachineApplicable, - ); + let mut lint: Option<(&'static Lint, &'static str, &'static str)> = None; + let mut has_conflict = false; + for lint_point in &lint_points { + match lint_point { + LintPoint::Available(_, l, used_mod, replace_with) + if lint.is_none_or(|(prev_l, ..)| l.name == prev_l.name) => + { + lint = Some((l, used_mod, replace_with)); + }, + _ => { + has_conflict = true; + break; + }, + } + } + + if !has_conflict && let Some((lint, used_mod, replace_with)) = lint { + span_lint_and_sugg( + cx, + lint, + krate_span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + format!("consider importing the item from `{replace_with}`"), + (*replace_with).to_string(), + Applicability::MachineApplicable, + ); + return; + } + + for lint_point in lint_points { + let LintPoint::Available(span, lint, used_mod, replace_with) = lint_point else { + continue; + }; + span_lint_and_help( + cx, + lint, + span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + None, + format!("consider importing the item from `{replace_with}`"), + ); + } } /// Returns the first named segment of a [`Path`]. diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 6cc4b589a7207..45c0d459d904c 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -143,7 +143,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if let Some(tail) = block.expr && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) && !tail.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { span_lint_and_then( @@ -167,7 +168,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { span_lint_and_then( @@ -540,7 +542,12 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { +fn stmt_has_safety_comment( + cx: &LateContext<'_>, + span: Span, + hir_id: HirId, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { match span_from_macro_expansion_has_safety_comment(cx, span) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, @@ -555,6 +562,13 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H _ => return HasSafetyComment::Maybe, }; + // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib + // } + let mut span = span; + if accept_comment_above_attributes { + span = include_attrs_in_span(cx, hir_id, span); + } + let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) diff --git a/src/tools/clippy/clippy_lints_internal/src/symbols.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs index 7b5d58824c38f..74712097e7166 100644 --- a/src/tools/clippy/clippy_lints_internal/src/symbols.rs +++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs @@ -65,7 +65,7 @@ pub struct Symbols { impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); impl Symbols { - fn lit_suggestion(&self, lit: &Lit) -> Option<(Span, String)> { + fn lit_suggestion(&self, lit: Lit) -> Option<(Span, String)> { if let LitKind::Str(name, _) = lit.node { let sugg = if let Some((prefix, name)) = self.symbol_map.get(&name.as_u32()) { format!("{prefix}::{name}") diff --git a/src/tools/clippy/clippy_test_deps/Cargo.lock b/src/tools/clippy/clippy_test_deps/Cargo.lock new file mode 100644 index 0000000000000..a591dae3a1a62 --- /dev/null +++ b/src/tools/clippy/clippy_test_deps/Cargo.lock @@ -0,0 +1,505 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "clippy_test_deps" +version = "0.1.0" +dependencies = [ + "futures", + "if_chain", + "itertools", + "parking_lot", + "quote", + "regex", + "serde", + "syn", + "tokio", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/src/tools/clippy/clippy_test_deps/Cargo.toml b/src/tools/clippy/clippy_test_deps/Cargo.toml new file mode 100644 index 0000000000000..a23ffcaf2f978 --- /dev/null +++ b/src/tools/clippy/clippy_test_deps/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "clippy_test_deps" +version = "0.1.0" +edition = "2021" + +# Add dependencies here to make them available in ui tests. + +[dependencies] +regex = "1.5.5" +serde = { version = "1.0.145", features = ["derive"] } +if_chain = "1.0" +quote = "1.0.25" +syn = { version = "2.0", features = ["full"] } +futures = "0.3" +parking_lot = "0.12" +tokio = { version = "1", features = ["io-util"] } +itertools = "0.12" + +# Make sure we are not part of the rustc workspace. +[workspace] diff --git a/src/tools/clippy/clippy_test_deps/src/main.rs b/src/tools/clippy/clippy_test_deps/src/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/clippy/clippy_test_deps/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 649748d1534bb..645b644d9f4e8 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-06-26 +nightly-2025-07-10 ``` diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 4c7a589e185b9..34472eaab93cc 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,5 +1,8 @@ +use crate::source::SpanRangeExt; +use crate::{sym, tokenize_with_text}; use rustc_ast::attr; use rustc_ast::attr::AttributeExt; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::Applicability; use rustc_lexer::TokenKind; use rustc_lint::LateContext; @@ -7,10 +10,6 @@ use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; use rustc_span::{Span, Symbol}; use std::str::FromStr; -use rustc_attr_data_structures::find_attr; -use crate::source::SpanRangeExt; -use crate::{sym, tokenize_with_text}; -use rustc_attr_data_structures::AttributeKind; /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { @@ -168,7 +167,8 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { adt.is_variant_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..)) || adt.variants().iter().any(|variant_def| { - variant_def.is_field_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..)) + variant_def.is_field_list_non_exhaustive() + || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..)) }) || adt .all_fields() diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index ba0376e4d4066..94b7055ad20e4 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -304,9 +304,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => { - Constant::Binary(s.as_byte_str().to_vec()) - } + LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(s.as_byte_str().to_vec()), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n.get()), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { @@ -568,9 +566,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } else { match &lit.node { LitKind::Str(is, _) => Some(is.is_empty()), - LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => { - Some(s.as_byte_str().is_empty()) - } + LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.as_byte_str().is_empty()), _ => None, } } diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 0ca494f16e31d..6f19ce80cf6eb 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -2,6 +2,7 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; use crate::tokenize_with_text; +use rustc_ast::ast; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::MatchSource::TryDesugar; @@ -9,8 +10,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind, - Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, - TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, + Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -1004,8 +1005,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); } }, - ExprKind::Match(e, arms, s) => { - self.hash_expr(e); + ExprKind::Match(scrutinee, arms, _) => { + self.hash_expr(scrutinee); for arm in *arms { self.hash_pat(arm.pat); @@ -1014,8 +1015,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } self.hash_expr(arm.body); } - - s.hash(&mut self.s); }, ExprKind::MethodCall(path, receiver, args, _fn_span) => { self.hash_name(path.ident.name); @@ -1058,8 +1057,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Use(expr, _) => { self.hash_expr(expr); }, - ExprKind::Unary(lop, le) => { - std::mem::discriminant(lop).hash(&mut self.s); + ExprKind::Unary(l_op, le) => { + std::mem::discriminant(l_op).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { @@ -1394,3 +1393,70 @@ fn eq_span_tokens( } f(cx, left.into_range(), right.into_range(), pred) } + +/// Returns true if the expression contains ambiguous literals (unsuffixed float or int literals) +/// that could be interpreted as either f32/f64 or i32/i64 depending on context. +pub fn has_ambiguous_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(ref qpath) => { + if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) + && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) + && local.ty.is_none() + && let Some(init) = local.init + { + return has_ambiguous_literal_in_expr(cx, init); + } + false + }, + ExprKind::Lit(lit) => matches!( + lit.node, + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) | ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) + ), + + ExprKind::Array(exprs) | ExprKind::Tup(exprs) => exprs.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Binary(_, lhs, rhs) => { + has_ambiguous_literal_in_expr(cx, lhs) || has_ambiguous_literal_in_expr(cx, rhs) + }, + + ExprKind::Unary(_, e) + | ExprKind::Cast(e, _) + | ExprKind::Type(e, _) + | ExprKind::DropTemps(e) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Field(e, _) + | ExprKind::Index(e, _, _) + | ExprKind::Yield(e, _) => has_ambiguous_literal_in_expr(cx, e), + + ExprKind::MethodCall(_, receiver, args, _) | ExprKind::Call(receiver, args) => { + has_ambiguous_literal_in_expr(cx, receiver) || args.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Closure(Closure { body, .. }) => { + let body = cx.tcx.hir_body(*body); + let closure_expr = crate::peel_blocks(body.value); + has_ambiguous_literal_in_expr(cx, closure_expr) + }, + + ExprKind::Block(blk, _) => blk.expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::If(cond, then_expr, else_expr) => { + has_ambiguous_literal_in_expr(cx, cond) + || has_ambiguous_literal_in_expr(cx, then_expr) + || else_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Match(scrutinee, arms, _) => { + has_ambiguous_literal_in_expr(cx, scrutinee) + || arms.iter().any(|arm| has_ambiguous_literal_in_expr(cx, arm.body)) + }, + + ExprKind::Loop(body, ..) => body.expr.is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Ret(opt_expr) | ExprKind::Break(_, opt_expr) => { + opt_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + _ => false, + } +} diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c01f0ffaac9a0..2e03743e621d4 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -77,7 +77,8 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, + HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr, + hash_stmt, is_bool, over, }; use core::mem; @@ -3497,3 +3498,64 @@ pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> false } } + +/// Checks if `expr` may be directly used as the return value of its enclosing body. +/// The following cases are covered: +/// - `expr` as the last expression of the body, or of a block that can be used as the return value +/// - `return expr` +/// - then or else part of a `if` in return position +/// - arm body of a `match` in a return position +/// - `break expr` or `break 'label expr` if the loop or block being exited is used as a return +/// value +/// +/// Contrary to [`TyCtxt::hir_get_fn_id_for_return_block()`], if `expr` is part of a +/// larger expression, for example a field expression of a `struct`, it will not be +/// considered as matching the condition and will return `false`. +/// +/// Also, even if `expr` is assigned to a variable which is later returned, this function +/// will still return `false` because `expr` is not used *directly* as the return value +/// as it goes through the intermediate variable. +pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let enclosing_body_owner = cx + .tcx + .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id)); + let mut prev_id = expr.hir_id; + let mut skip_until_id = None; + for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) { + if hir_id == enclosing_body_owner { + return true; + } + if let Some(id) = skip_until_id { + prev_id = hir_id; + if id == hir_id { + skip_until_id = None; + } + continue; + } + match node { + Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {}, + Node::Arm(arm) if arm.body.hir_id == prev_id => {}, + Node::Expr(expr) => match expr.kind { + ExprKind::Ret(_) => return true, + ExprKind::If(_, then, opt_else) + if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {}, + ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {}, + ExprKind::Block(block, _) if block.hir_id == prev_id => {}, + ExprKind::Break( + Destination { + target_id: Ok(target_id), + .. + }, + _, + ) => skip_until_id = Some(target_id), + _ => break, + }, + _ => break, + } + prev_id = hir_id; + } + + // `expr` is used as part of "something" and is not returned directly from its + // enclosing body. + false +} diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 7a0bef1a9bbbf..24ed4c3a8beca 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -74,7 +74,7 @@ msrv_aliases! { 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } - 1,24,0 { IS_ASCII_DIGIT } + 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 615a0910dfde3..ba5cbc738361f 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::msrvs::Msrv; +use crate::qualify_min_const_fn::is_stable_const_fn; use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; -use crate::qualify_min_const_fn::is_stable_const_fn; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -355,7 +355,7 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> ExprKind::Binary(_, lhs, rhs) if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (), + ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), ExprKind::Index(base, _, _) if matches!( @@ -390,7 +390,8 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> | ExprKind::Repeat(..) | ExprKind::Struct(..) | ExprKind::Tup(_) - | ExprKind::Type(..) => (), + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => (), _ => { return ControlFlow::Break(()); @@ -678,10 +679,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( helper(typeck, true, else_expr, f)?; } }, - ExprKind::Type(e, _) => { - helper(typeck, consume, e, f)?; - }, - ExprKind::UnsafeBinderCast(_, e, _) => { + ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => { helper(typeck, consume, e, f)?; }, diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index 83c3d7aba021a..3b2ebf0c28ac2 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -68,6 +68,9 @@ pub(crate) enum Commands { /// This will limit the number of warnings that will be printed for each lint #[clap(long)] truncate: bool, + /// Write the diff summary to a JSON file if there are any changes + #[clap(long, value_name = "PATH")] + write_summary: Option, }, /// Create a lintcheck crates TOML file containing the top N popular crates Popular { diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs index 8ea0a41ed368a..808997ff02204 100644 --- a/src/tools/clippy/lintcheck/src/json.rs +++ b/src/tools/clippy/lintcheck/src/json.rs @@ -4,8 +4,8 @@ //! loading warnings from JSON files, and generating human-readable diffs //! between different linting runs. -use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::{fmt, fs}; use itertools::{EitherOrBoth, Itertools}; use serde::{Deserialize, Serialize}; @@ -17,7 +17,6 @@ const DEFAULT_LIMIT_PER_LINT: usize = 300; /// Target for total warnings to display across all lints when truncating output. const TRUNCATION_TOTAL_TARGET: usize = 1000; -/// Representation of a single Clippy warning for JSON serialization. #[derive(Debug, Deserialize, Serialize)] struct LintJson { /// The lint name e.g. `clippy::bytes_nth` @@ -29,7 +28,6 @@ struct LintJson { } impl LintJson { - /// Returns a tuple of name and `file_line` for sorting and comparison. fn key(&self) -> impl Ord + '_ { (self.name.as_str(), self.file_line.as_str()) } @@ -40,6 +38,57 @@ impl LintJson { } } +#[derive(Debug, Serialize)] +struct SummaryRow { + name: String, + added: usize, + removed: usize, + changed: usize, +} + +#[derive(Debug, Serialize)] +struct Summary(Vec); + +impl fmt::Display for Summary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str( + "\ +| Lint | Added | Removed | Changed | +| ---- | ----: | ------: | ------: | +", + )?; + + for SummaryRow { + name, + added, + changed, + removed, + } in &self.0 + { + let html_id = to_html_id(name); + writeln!(f, "| [`{name}`](#{html_id}) | {added} | {changed} | {removed} |")?; + } + + Ok(()) + } +} + +impl Summary { + fn new(lints: &[LintWarnings]) -> Self { + Summary( + lints + .iter() + .map(|lint| SummaryRow { + name: lint.name.clone(), + added: lint.added.len(), + removed: lint.removed.len(), + changed: lint.changed.len(), + }) + .collect(), + ) + } +} + /// Creates the log file output for [`crate::config::OutputFormat::Json`] pub(crate) fn output(clippy_warnings: Vec) -> String { let mut lints: Vec = clippy_warnings @@ -74,7 +123,7 @@ fn load_warnings(path: &Path) -> Vec { /// /// Compares warnings from `old_path` and `new_path`, then displays a summary table /// and detailed information about added, removed, and changed warnings. -pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { +pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool, write_summary: Option) { let old_warnings = load_warnings(old_path); let new_warnings = load_warnings(new_path); @@ -108,13 +157,16 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { } } - print_summary_table(&lint_warnings); - println!(); - if lint_warnings.is_empty() { return; } + let summary = Summary::new(&lint_warnings); + if let Some(path) = write_summary { + let json = serde_json::to_string(&summary).unwrap(); + fs::write(path, json).unwrap(); + } + let truncate_after = if truncate { // Max 15 ensures that we at least have five messages per lint DEFAULT_LIMIT_PER_LINT @@ -126,6 +178,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { usize::MAX }; + println!("{summary}"); for lint in lint_warnings { print_lint_warnings(&lint, truncate_after); } @@ -140,13 +193,11 @@ struct LintWarnings { changed: Vec<(LintJson, LintJson)>, } -/// Prints a formatted report for a single lint type with its warnings. fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { let name = &lint.name; let html_id = to_html_id(name); - // The additional anchor is added for non GH viewers that don't prefix ID's - println!(r#"## `{name}` "#); + println!(r#"

{name}

"#); println!(); print!( @@ -162,22 +213,6 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { print_changed_diff(&lint.changed, truncate_after / 3); } -/// Prints a summary table of all lints with counts of added, removed, and changed warnings. -fn print_summary_table(lints: &[LintWarnings]) { - println!("| Lint | Added | Removed | Changed |"); - println!("| ------------------------------------------ | ------: | ------: | ------: |"); - - for lint in lints { - println!( - "| {:<62} | {:>7} | {:>7} | {:>7} |", - format!("[`{}`](#user-content-{})", lint.name, to_html_id(&lint.name)), - lint.added.len(), - lint.removed.len(), - lint.changed.len() - ); - } -} - /// Prints a section of warnings with a header and formatted code blocks. fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { if warnings.is_empty() { @@ -248,17 +283,16 @@ fn truncate(list: &[T], truncate_after: usize) -> &[T] { } } -/// Prints a level 3 heading with an appropriate HTML ID for linking. fn print_h3(lint: &str, title: &str) { let html_id = to_html_id(lint); - // We have to use HTML here to be able to manually add an id. - println!(r#"### {title} "#); + // We have to use HTML here to be able to manually add an id, GitHub doesn't add them automatically + println!(r#"

{title}

"#); } -/// GitHub's markdown parsers doesn't like IDs with `::` and `_`. This simplifies -/// the lint name for the HTML ID. +/// Creates a custom ID allowed by GitHub, they must start with `user-content-` and cannot contain +/// `::`/`_` fn to_html_id(lint_name: &str) -> String { - lint_name.replace("clippy::", "").replace('_', "-") + lint_name.replace("clippy::", "user-content-").replace('_', "-") } /// This generates the `x added` string for the start of the job summery. @@ -270,9 +304,6 @@ fn count_string(lint: &str, label: &str, count: usize) -> String { format!("0 {label}") } else { let html_id = to_html_id(lint); - // GitHub's job summaries don't add HTML ids to headings. That's why we - // manually have to add them. GitHub prefixes these manual ids with - // `user-content-` and that's how we end up with these awesome links :D - format!("[{count} {label}](#user-content-{html_id}-{label})") + format!("[{count} {label}](#{html_id}-{label})") } } diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index eb390eecbcca8..3a60cfa79f410 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -303,7 +303,12 @@ fn main() { let config = LintcheckConfig::new(); match config.subcommand { - Some(Commands::Diff { old, new, truncate }) => json::diff(&old, &new, truncate), + Some(Commands::Diff { + old, + new, + truncate, + write_summary, + }) => json::diff(&old, &new, truncate, write_summary), Some(Commands::Popular { output, number }) => popular_crates::fetch(output, number).unwrap(), None => lintcheck(config), } diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 124756a360095..f46e079db3f10 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-06-26" +channel = "nightly-2025-07-10" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index cefe654fef68a..aa7863347113e 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -16,8 +16,10 @@ use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; +use ui_test::dependencies::DependencyBuilder; use ui_test::spanned::Spanned; -use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; +use ui_test::status_emitter::StatusEmitter; +use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; @@ -27,46 +29,26 @@ use std::path::{Path, PathBuf}; use std::sync::mpsc::{Sender, channel}; use std::{fs, iter, thread}; -// Test dependencies may need an `extern crate` here to ensure that they show up -// in the depinfo file (otherwise cargo thinks they are unused) -extern crate futures; -extern crate if_chain; -extern crate itertools; -extern crate parking_lot; -extern crate quote; -extern crate syn; -extern crate tokio; - mod test_utils; -/// All crates used in UI tests are listed here -static TEST_DEPENDENCIES: &[&str] = &[ - "clippy_config", - "clippy_lints", - "clippy_utils", - "futures", - "if_chain", - "itertools", - "parking_lot", - "quote", - "regex", - "serde_derive", - "serde", - "syn", - "tokio", -]; - -/// Produces a string with an `--extern` flag for all UI test crate -/// dependencies. +/// All crates used in internal UI tests are listed here. +/// We directly re-use these crates from their normal clippy builds, so we don't have them +/// in `clippy_test_devs`. That saves a lot of time but also means they don't work in a stage 1 +/// test in rustc bootstrap. +static INTERNAL_TEST_DEPENDENCIES: &[&str] = &["clippy_config", "clippy_lints", "clippy_utils"]; + +/// Produces a string with an `--extern` flag for all `INTERNAL_TEST_DEPENDENCIES`. /// /// The dependency files are located by parsing the depinfo file for this test /// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test /// dependencies must be added to Cargo.toml at the project root. Test /// dependencies that are not *directly* used by this test module require an /// `extern crate` declaration. -fn extern_flags() -> Vec { +fn internal_extern_flags() -> Vec { + let current_exe_path = env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); let current_exe_depinfo = { - let mut path = env::current_exe().unwrap(); + let mut path = current_exe_path.clone(); path.set_extension("d"); fs::read_to_string(path).unwrap() }; @@ -88,7 +70,7 @@ fn extern_flags() -> Vec { Some((name, path_str)) }; if let Some((name, path)) = parse_name_path() - && TEST_DEPENDENCIES.contains(&name) + && INTERNAL_TEST_DEPENDENCIES.contains(&name) { // A dependency may be listed twice if it is available in sysroot, // and the sysroot dependencies are listed first. As of the writing, @@ -96,7 +78,7 @@ fn extern_flags() -> Vec { crates.insert(name, path); } } - let not_found: Vec<&str> = TEST_DEPENDENCIES + let not_found: Vec<&str> = INTERNAL_TEST_DEPENDENCIES .iter() .copied() .filter(|n| !crates.contains_key(n)) @@ -111,6 +93,7 @@ fn extern_flags() -> Vec { crates .into_iter() .map(|(name, path)| format!("--extern={name}={path}")) + .chain([format!("-Ldependency={}", deps_path.display())]) .collect() } @@ -119,7 +102,6 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); struct TestContext { args: Args, - extern_flags: Vec, diagnostic_collector: Option, collector_thread: Option>, } @@ -134,7 +116,6 @@ impl TestContext { .unzip(); Self { args, - extern_flags: extern_flags(), diagnostic_collector, collector_thread, } @@ -158,6 +139,15 @@ impl TestContext { }; let defaults = config.comment_defaults.base(); defaults.set_custom("edition", Edition("2024".into())); + defaults.set_custom( + "dependencies", + DependencyBuilder { + program: CommandBuilder::cargo(), + crate_manifest_path: Path::new("clippy_test_deps").join("Cargo.toml"), + build_std: None, + bless_lockfile: self.args.bless, + }, + ); defaults.exit_status = None.into(); if mandatory_annotations { defaults.require_annotations = Some(Spanned::dummy(true)).into(); @@ -182,12 +172,10 @@ impl TestContext { "-Zui-testing", "-Zdeduplicate-diagnostics=no", "-Dwarnings", - &format!("-Ldependency={}", deps_path.display()), ] .map(OsString::from), ); - config.program.args.extend(self.extern_flags.iter().map(OsString::from)); // Prevent rustc from creating `rustc-ice-*` files the console output is enough. config.program.envs.push(("RUSTC_ICE".into(), Some("0".into()))); @@ -217,7 +205,7 @@ fn run_ui(cx: &TestContext) { vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -227,13 +215,17 @@ fn run_internal_tests(cx: &TestContext) { return; } let mut config = cx.base_config("ui-internal", true); + config + .program + .args + .extend(internal_extern_flags().iter().map(OsString::from)); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -257,7 +249,7 @@ fn run_ui_toml(cx: &TestContext) { .envs .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into()))); }, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -304,7 +296,7 @@ fn run_ui_cargo(cx: &TestContext) { .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)) }, |_config, _file_contents| {}, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 20cdff5fcd12b..cebfc48a884f6 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -450,5 +450,13 @@ help: consider removing the safety comment LL | // SAFETY: unnecessary_safety_comment triggers here | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 52 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12 + | +LL | return unsafe { h() }; + | ^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 53 previous errors diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index 91a02bc3d7c65..a2d7c1b6c7967 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -723,4 +723,15 @@ fn issue_13039() { _ = unsafe { foo() } } +fn rfl_issue15034() -> i32 { + unsafe fn h() -> i32 { + 1i32 + } + // This shouldn't lint with accept-comment-above-attributes! Thus fixing a false positive! + // SAFETY: My safety comment! + #[allow(clippy::unnecessary_cast)] + return unsafe { h() }; + //~[disabled]^ ERROR: unsafe block missing a safety comment +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed index 3ba2eea59f0b0..3f6e5245b8788 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -47,3 +47,9 @@ fn implicit_cast() { // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs index 8cdd0512da5f7..20f4f40e00197 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -47,3 +47,9 @@ fn implicit_cast() { // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +} diff --git a/src/tools/clippy/tests/ui/cast_size.32bit.stderr b/src/tools/clippy/tests/ui/cast_size.32bit.stderr index cb1620e36a267..5811cb3607ba0 100644 --- a/src/tools/clippy/tests/ui/cast_size.32bit.stderr +++ b/src/tools/clippy/tests/ui/cast_size.32bit.stderr @@ -177,6 +177,14 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: casting `usize` to `u16` may truncate the value + --> tests/ui/cast_size.rs:71:20 + | +LL | const N: u16 = M as u16; + | ^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... + error: literal out of range for `usize` --> tests/ui/cast_size.rs:63:5 | @@ -186,5 +194,5 @@ LL | 9_999_999_999_999_999usize as f64; = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295` = note: `#[deny(overflowing_literals)]` on by default -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/cast_size.64bit.stderr b/src/tools/clippy/tests/ui/cast_size.64bit.stderr index b6000a52abb34..ba1419583aeb5 100644 --- a/src/tools/clippy/tests/ui/cast_size.64bit.stderr +++ b/src/tools/clippy/tests/ui/cast_size.64bit.stderr @@ -177,5 +177,13 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: casting `usize` to `u16` may truncate the value + --> tests/ui/cast_size.rs:71:20 + | +LL | const N: u16 = M as u16; + | ^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index e5bef2a99d59e..ecc5866941918 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -65,3 +65,9 @@ fn main() { //~[32bit]^^ ERROR: literal out of range for `usize` // 999_999_999_999_999_999_999_999_999_999u128 as f128; } + +fn issue15163() { + const M: usize = 100; + const N: u16 = M as u16; + //~^ cast_possible_truncation +} diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed index ae9d3ef9656f1..b5b3f15b4de4b 100644 --- a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed +++ b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&*x); @@ -15,12 +15,23 @@ fn main() { let _: &dyn Any = &*x; //~^ coerce_container_to_any + let _: &dyn Any = &*x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut *x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.rs b/src/tools/clippy/tests/ui/coerce_container_to_any.rs index 9948bd48e0d8d..4d6527bb5525d 100644 --- a/src/tools/clippy/tests/ui/coerce_container_to_any.rs +++ b/src/tools/clippy/tests/ui/coerce_container_to_any.rs @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&x); @@ -15,12 +15,23 @@ fn main() { let _: &dyn Any = &x; //~^ coerce_container_to_any + let _: &dyn Any = &mut x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr index 00ab77e0ce0fc..26389c9186ebc 100644 --- a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr +++ b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr @@ -19,5 +19,17 @@ error: coercing `&std::boxed::Box` to `&dyn Any` LL | let _: &dyn Any = &x; | ^^ help: consider dereferencing: `&*x` -error: aborting due to 3 previous errors +error: coercing `&mut std::boxed::Box` to `&dyn Any` + --> tests/ui/coerce_container_to_any.rs:18:23 + | +LL | let _: &dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&*x` + +error: coercing `&mut std::boxed::Box` to `&mut dyn Any` + --> tests/ui/coerce_container_to_any.rs:21:27 + | +LL | let _: &mut dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&mut *x` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/disallowed_script_idents.rs b/src/tools/clippy/tests/ui/disallowed_script_idents.rs index 08fd1d9669ee9..dae380045ae42 100644 --- a/src/tools/clippy/tests/ui/disallowed_script_idents.rs +++ b/src/tools/clippy/tests/ui/disallowed_script_idents.rs @@ -15,3 +15,17 @@ fn main() { let カウンタ = 10; //~^ disallowed_script_idents } + +fn issue15116() { + const ÄÖÜ: u8 = 0; + const _ÄÖÜ: u8 = 0; + const Ä_ÖÜ: u8 = 0; + const ÄÖ_Ü: u8 = 0; + const ÄÖÜ_: u8 = 0; + let äöüß = 1; + let _äöüß = 1; + let ä_öüß = 1; + let äö_üß = 1; + let äöü_ß = 1; + let äöüß_ = 1; +} diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed index 065f4486e390a..5c57c58fbc0b2 100644 --- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed @@ -72,8 +72,6 @@ pub struct NotEmptyTight; /// ## Heading /// -/// - [x][] - Done -//~^ ERROR: link reference defined in list item -/// - [ ][] - Not Done -//~^ ERROR: link reference defined in list item +/// - [x] - Done +/// - [ ] - Not Done pub struct GithubCheckboxes; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs index c7eab50c8b386..06b6ba49e192e 100644 --- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs @@ -73,7 +73,5 @@ pub struct NotEmptyTight; /// ## Heading /// /// - [x] - Done -//~^ ERROR: link reference defined in list item /// - [ ] - Not Done -//~^ ERROR: link reference defined in list item pub struct GithubCheckboxes; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr index 5a815dabf4d8f..27314c7e968dd 100644 --- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr @@ -144,29 +144,5 @@ help: for an intra-doc link, add `[]` between the label and the colon LL | /// - [link][]: def "title" | ++ -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:75:7 - | -LL | /// - [x] - Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [x][] - Done - | ++ - -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:77:7 - | -LL | /// - [ ] - Not Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [ ][] - Not Done - | ++ - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs b/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs new file mode 100644 index 0000000000000..a550e5609658d --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs @@ -0,0 +1,13 @@ +//@check-pass + +#![warn(clippy::empty_loop)] +#![feature(intrinsics)] +#![feature(rustc_attrs)] + +// From issue #15200 +#[rustc_intrinsic] +#[rustc_nounwind] +/// # Safety +pub const unsafe fn simd_insert(x: T, idx: u32, val: U) -> T; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs new file mode 100644 index 0000000000000..9f83ed3253369 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs @@ -0,0 +1,17 @@ +//@compile-flags: --test +#![warn(clippy::exit)] + +fn not_main() { + if true { + std::process::exit(4); + //~^ exit + } +} + +fn main() { + if true { + std::process::exit(2); + }; + not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr b/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr new file mode 100644 index 0000000000000..6e33c39f0d7b9 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr @@ -0,0 +1,11 @@ +error: usage of `process::exit` + --> tests/ui/exit1_compile_flag_test.rs:6:9 + | +LL | std::process::exit(4); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::exit)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs new file mode 100644 index 0000000000000..0b994ebc56c3e --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs @@ -0,0 +1,15 @@ +//@compile-flags: --test +#![warn(clippy::exit)] + +fn also_not_main() { + std::process::exit(3); + //~^ exit +} + +fn main() { + if true { + std::process::exit(2); + }; + also_not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr b/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr new file mode 100644 index 0000000000000..51eb26e9c2a46 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr @@ -0,0 +1,11 @@ +error: usage of `process::exit` + --> tests/ui/exit2_compile_flag_test.rs:5:5 + | +LL | std::process::exit(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::exit)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs new file mode 100644 index 0000000000000..f8131ead2da30 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs @@ -0,0 +1,11 @@ +//@ check-pass +//@compile-flags: --test + +#![warn(clippy::exit)] + +fn main() { + if true { + std::process::exit(2); + }; + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit4.rs b/src/tools/clippy/tests/ui/exit4.rs new file mode 100644 index 0000000000000..821a26fd78b05 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit4.rs @@ -0,0 +1,8 @@ +//@ check-pass +//@compile-flags: --test + +#![warn(clippy::exit)] + +fn main() { + std::process::exit(0) +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed index 83aeddb2a1f4a..884bae0043204 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed @@ -69,3 +69,47 @@ fn _issue11831() { let _ = a + b * c; } + +fn _issue14897() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + + let _ = 2.0f64.mul_add(4.0, x); + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y.mul_add(2.0, 0.5); + //~^ suboptimal_flops + let _ = 1.0f64.mul_add(2.0, 0.5); + //~^ suboptimal_flops +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs index 039ee8d053fc9..9ceb2ec96062f 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.rs +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs @@ -69,3 +69,47 @@ fn _issue11831() { let _ = a + b * c; } + +fn _issue14897() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + + let _ = 0.5 + 2.0 * x; + //~^ suboptimal_flops + let _ = 2.0 * x + 0.5; + //~^ suboptimal_flops + + let _ = x + 2.0 * 4.0; + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y * 2.0 + 0.5; + //~^ suboptimal_flops + let _ = 1.0 * 2.0 + 0.5; + //~^ suboptimal_flops +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr index 6482127bcc00b..dad65ddf2ec39 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr @@ -79,5 +79,35 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: aborting due to 13 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:102:13 + | +LL | let _ = 0.5 + 2.0 * x; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:104:13 + | +LL | let _ = 2.0 * x + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:107:13 + | +LL | let _ = x + 2.0 * 4.0; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:111:13 + | +LL | let _ = y * 2.0 + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:113:13 + | +LL | let _ = 1.0 * 2.0 + 0.5; + | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed index 6425f32c09c42..65a9cfa6e64c2 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed @@ -61,7 +61,7 @@ fn option_methods() { let _ = Some(2).is_some_and(|x| x % 2 == 0); //~^ manual_is_variant_and - let _ = Some(2).is_none_or(|x| x % 2 == 0); + let _ = Some(2).is_none_or(|x| x % 2 != 0); //~^ manual_is_variant_and let _ = Some(2).is_some_and(|x| x % 2 == 0); //~^ manual_is_variant_and @@ -116,3 +116,113 @@ fn main() { option_methods(); result_methods(); } + +fn issue15202() { + let xs = [None, Some(b'_'), Some(b'1')]; + for x in xs { + let a1 = x.is_none_or(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_none_or(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_some_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_some_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + let xs = [Err("foo"), Ok(b'_'), Ok(b'1')]; + for x in xs { + let a1 = !x.is_ok_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = !x.is_ok_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_ok_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_ok_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } +} + +mod with_func { + fn iad(b: u8) -> bool { + b.is_ascii_digit() + } + + fn check_option(b: Option) { + let a1 = b.is_some_and(iad); + //~^ manual_is_variant_and + let a2 = b.is_some_and(iad); + assert_eq!(a1, a2); + + let a1 = b.is_some_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_some_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.is_none_or(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_none_or(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.is_none_or(iad); + //~^ manual_is_variant_and + let a2 = b.is_none_or(iad); + assert_eq!(a1, a2); + } + + fn check_result(b: Result) { + let a1 = b.is_ok_and(iad); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.is_ok_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = !b.is_ok_and(iad); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = !b.is_ok_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + } +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.rs b/src/tools/clippy/tests/ui/manual_is_variant_and.rs index e069e97a04ddc..85b45d654a7d0 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.rs +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.rs @@ -125,3 +125,113 @@ fn main() { option_methods(); result_methods(); } + +fn issue15202() { + let xs = [None, Some(b'_'), Some(b'1')]; + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + let xs = [Err("foo"), Ok(b'_'), Ok(b'1')]; + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } +} + +mod with_func { + fn iad(b: u8) -> bool { + b.is_ascii_digit() + } + + fn check_option(b: Option) { + let a1 = b.map(iad) == Some(true); + //~^ manual_is_variant_and + let a2 = b.is_some_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) == Some(false); + //~^ manual_is_variant_and + let a2 = b.is_some_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Some(true); + //~^ manual_is_variant_and + let a2 = b.is_none_or(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Some(false); + //~^ manual_is_variant_and + let a2 = b.is_none_or(iad); + assert_eq!(a1, a2); + } + + fn check_result(b: Result) { + let a1 = b.map(iad) == Ok(true); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) == Ok(false); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Ok(true); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Ok(false); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + } +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr index f770319a2681e..da36b5a07d210 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr @@ -54,7 +54,7 @@ error: called `.map() != Some()` --> tests/ui/manual_is_variant_and.rs:70:13 | LL | let _ = Some(2).map(|x| x % 2 == 0) != Some(true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 != 0)` error: called `.map() == Some()` --> tests/ui/manual_is_variant_and.rs:72:13 @@ -126,5 +126,101 @@ error: called `map().unwrap_or_default()` on a `Result` value LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)` -error: aborting due to 15 previous errors +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:132:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| !b.is_ascii_digit())` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:139:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:146:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:153:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| !b.is_ascii_digit())` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:161:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| b.is_ascii_digit())` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:168:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| !b.is_ascii_digit())` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:175:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| b.is_ascii_digit())` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:182:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| !b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:195:18 + | +LL | let a1 = b.map(iad) == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(iad)` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:200:18 + | +LL | let a1 = b.map(iad) == Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(|x| !iad(x))` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:205:18 + | +LL | let a1 = b.map(iad) != Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(|x| !iad(x))` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:210:18 + | +LL | let a1 = b.map(iad) != Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(iad)` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:217:18 + | +LL | let a1 = b.map(iad) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(iad)` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:222:18 + | +LL | let a1 = b.map(iad) == Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(|x| !iad(x))` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:227:18 + | +LL | let a1 = b.map(iad) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(iad)` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:232:18 + | +LL | let a1 = b.map(iad) != Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.fixed b/src/tools/clippy/tests/ui/manual_let_else_match.fixed index 588ba5edd8f15..15f604aec2928 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.fixed +++ b/src/tools/clippy/tests/ui/manual_let_else_match.fixed @@ -137,3 +137,48 @@ fn not_fire() { fn issue11579() { let Some(msg) = Some("hi") else { unreachable!("can't happen") }; } + +#[derive(Clone, Copy)] +struct Issue9939 { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") }; + let issue = Some(Issue9939 { avalanche: true }); + let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") }; + + // with shadowing + let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") }; +} + +#[derive(Clone, Copy)] +struct Issue9939b { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") }; + assert!(erosion); + + // with shadowing + let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") }; + assert!(erosion); +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs index 6416753bac107..44a044b142bd8 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.rs +++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs @@ -177,3 +177,76 @@ fn issue11579() { _ => unreachable!("can't happen"), }; } + +#[derive(Clone, Copy)] +struct Issue9939 { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let tornado = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche }) => avalanche, + _ => unreachable!("can't happen"), + }; + let issue = Some(Issue9939 { avalanche: true }); + let acid_rain = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche: tornado }) => tornado, + _ => unreachable!("can't happen"), + }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _y = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; + + // with shadowing + let _x = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; +} + +#[derive(Clone, Copy)] +struct Issue9939b { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let (issue, drought, flood) = match issue { + //~^ manual_let_else + flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let (_y, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); + + // with shadowing + let (_x, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr index 393562c629bac..ed6117ebffb7d 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr @@ -101,5 +101,75 @@ LL | | _ => unreachable!("can't happen"), LL | | }; | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };` -error: aborting due to 10 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:188:5 + | +LL | / let tornado = match issue { +LL | | +LL | | Some(Issue9939 { avalanche }) => avalanche, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:194:5 + | +LL | / let acid_rain = match issue { +LL | | +LL | | Some(Issue9939 { avalanche: tornado }) => tornado, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:203:5 + | +LL | / let _y = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:210:5 + | +LL | / let _x = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:228:5 + | +LL | / let (issue, drought, flood) = match issue { +LL | | +LL | | flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:238:5 + | +LL | / let (_y, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:246:5 + | +LL | / let (_x, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") };` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index ffdae8504f72e..d016e099e303b 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -250,3 +250,31 @@ pub fn issue_12760() { } } } + +/// This needs documenting +pub fn unwrap_expect_etc_in_const() { + let a = const { std::num::NonZeroUsize::new(1).unwrap() }; + // This should still pass the lint even if it is guaranteed to panic at compile-time + let b = const { std::num::NonZeroUsize::new(0).unwrap() }; +} + +/// This needs documenting +pub const fn unwrap_expect_etc_in_const_fn_fails() { + //~^ missing_panics_doc + let a = std::num::NonZeroUsize::new(1).unwrap(); +} + +/// This needs documenting +pub const fn assert_in_const_fn_fails() { + //~^ missing_panics_doc + let x = 0; + if x == 0 { + panic!(); + } +} + +/// This needs documenting +pub const fn in_const_fn(n: usize) { + //~^ missing_panics_doc + assert!(N > n); +} diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr index 7f0acf8de9b77..85a0091442734 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr @@ -180,5 +180,41 @@ note: first possible panic found here LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:262:1 + | +LL | pub const fn unwrap_expect_etc_in_const_fn_fails() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:264:13 + | +LL | let a = std::num::NonZeroUsize::new(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:268:1 + | +LL | pub const fn assert_in_const_fn_fails() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:272:9 + | +LL | panic!(); + | ^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:277:1 + | +LL | pub const fn in_const_fn(n: usize) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:279:5 + | +LL | assert!(N > n); + | ^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.fixed b/src/tools/clippy/tests/ui/needless_bool_assign.fixed index e0c717ecda21b..d6fab4c51b538 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.fixed +++ b/src/tools/clippy/tests/ui/needless_bool_assign.fixed @@ -33,3 +33,12 @@ fn main() { b = true; } } + +fn issue15063(x: bool, y: bool) { + let mut z = false; + + if x && y { + todo!() + } else { z = x || y; } + //~^^^^^ needless_bool_assign +} diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.rs b/src/tools/clippy/tests/ui/needless_bool_assign.rs index 3e4fecefa7852..c504f61f4dd1d 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.rs +++ b/src/tools/clippy/tests/ui/needless_bool_assign.rs @@ -45,3 +45,16 @@ fn main() { b = true; } } + +fn issue15063(x: bool, y: bool) { + let mut z = false; + + if x && y { + todo!() + } else if x || y { + z = true; + } else { + z = false; + } + //~^^^^^ needless_bool_assign +} diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr index f33a4bc0c592c..1d09b8b25a091 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr +++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr @@ -51,5 +51,16 @@ LL | | } = note: `-D clippy::if-same-then-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` -error: aborting due to 4 previous errors +error: this if-then-else expression assigns a bool literal + --> tests/ui/needless_bool_assign.rs:54:12 + | +LL | } else if x || y { + | ____________^ +LL | | z = true; +LL | | } else { +LL | | z = false; +LL | | } + | |_____^ help: you can reduce it to: `{ z = x || y; }` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/neg_multiply.fixed b/src/tools/clippy/tests/ui/neg_multiply.fixed index ff6e08300e298..32d466e88fc8b 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.fixed +++ b/src/tools/clippy/tests/ui/neg_multiply.fixed @@ -82,3 +82,15 @@ fn float() { -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64, +} + +fn nested() { + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs index b0f4e85c78e5d..241a72c6d990d 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.rs +++ b/src/tools/clippy/tests/ui/neg_multiply.rs @@ -82,3 +82,15 @@ fn float() { -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64, +} + +fn nested() { + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; + let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr index 2ef7e32ce05e1..f4fb6d3ce5414 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.stderr +++ b/src/tools/clippy/tests/ui/neg_multiply.stderr @@ -97,5 +97,11 @@ error: this multiplication by -1 can be written more succinctly LL | (3.0_f32 as f64) * -1.0; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` -error: aborting due to 16 previous errors +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:93:13 + | +LL | let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-(a.delta - 0.5).abs())` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/op_ref.fixed b/src/tools/clippy/tests/ui/op_ref.fixed index f412190b9fd9e..4bf4b91888c8c 100644 --- a/src/tools/clippy/tests/ui/op_ref.fixed +++ b/src/tools/clippy/tests/ui/op_ref.fixed @@ -110,3 +110,37 @@ mod issue_2597 { &array[idx] < val } } + +#[allow(clippy::needless_if)] +fn issue15063() { + use std::ops::BitAnd; + + macro_rules! mac { + ($e:expr) => { + $e.clone() + }; + } + + let x = 1; + if x == mac!(1) {} + //~^ op_ref + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & mac!(y); + //~^ op_ref +} diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs index a4bbd86c7e95b..9a192661aafcd 100644 --- a/src/tools/clippy/tests/ui/op_ref.rs +++ b/src/tools/clippy/tests/ui/op_ref.rs @@ -110,3 +110,37 @@ mod issue_2597 { &array[idx] < val } } + +#[allow(clippy::needless_if)] +fn issue15063() { + use std::ops::BitAnd; + + macro_rules! mac { + ($e:expr) => { + $e.clone() + }; + } + + let x = 1; + if &x == &mac!(1) {} + //~^ op_ref + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & &mac!(y); + //~^ op_ref +} diff --git a/src/tools/clippy/tests/ui/op_ref.stderr b/src/tools/clippy/tests/ui/op_ref.stderr index 51c2963a9eed2..8a58b154c8109 100644 --- a/src/tools/clippy/tests/ui/op_ref.stderr +++ b/src/tools/clippy/tests/ui/op_ref.stderr @@ -36,5 +36,25 @@ LL | let _ = two + &three; | | | help: use the right value directly: `three` -error: aborting due to 4 previous errors +error: needlessly taken reference of both operands + --> tests/ui/op_ref.rs:125:8 + | +LL | if &x == &mac!(1) {} + | ^^^^^^^^^^^^^^ + | +help: use the values directly + | +LL - if &x == &mac!(1) {} +LL + if x == mac!(1) {} + | + +error: taken reference of right operand + --> tests/ui/op_ref.rs:144:13 + | +LL | let z = x & &mac!(y); + | ^^^^-------- + | | + | help: use the right value directly: `mac!(y)` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 34f3e04684193..bcd2602edb6ad 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -439,4 +439,24 @@ fn test_option_get_or_insert() { //~^ or_fun_call } +fn test_option_and() { + // assume that this is slow call + fn g() -> Option { + Some(99) + } + let mut x = Some(42_u8); + let _ = x.and_then(|_| g()); + //~^ or_fun_call +} + +fn test_result_and() { + // assume that this is slow call + fn g() -> Result { + Ok(99) + } + let mut x: Result = Ok(42); + let _ = x.and_then(|_| g()); + //~^ or_fun_call +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index dc57bd6060ac0..8d1202ebf914a 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -439,4 +439,24 @@ fn test_option_get_or_insert() { //~^ or_fun_call } +fn test_option_and() { + // assume that this is slow call + fn g() -> Option { + Some(99) + } + let mut x = Some(42_u8); + let _ = x.and(g()); + //~^ or_fun_call +} + +fn test_result_and() { + // assume that this is slow call + fn g() -> Result { + Ok(99) + } + let mut x: Result = Ok(42); + let _ = x.and(g()); + //~^ or_fun_call +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 0f159fe8bff4b..585ee2d0e19d1 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -264,5 +264,17 @@ error: function call inside of `get_or_insert` LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` -error: aborting due to 41 previous errors +error: function call inside of `and` + --> tests/ui/or_fun_call.rs:448:15 + | +LL | let _ = x.and(g()); + | ^^^^^^^^ help: try: `and_then(|_| g())` + +error: function call inside of `and` + --> tests/ui/or_fun_call.rs:458:15 + | +LL | let _ = x.and(g()); + | ^^^^^^^^ help: try: `and_then(|_| g())` + +error: aborting due to 43 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed index 099c118e64e35..9f6643e8d52e8 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed @@ -60,7 +60,7 @@ fn issue9956() { //~^ redundant_closure_call // immediately calling only one closure, so we can't remove the other ones - let a = (|| || 123); + let a = || || 123; //~^ redundant_closure_call dbg!(a()()); @@ -144,3 +144,15 @@ fn issue_12358() { // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + Some(true) == Some(true); + //~^ redundant_closure_call + Some(true) == Some(true); + //~^ redundant_closure_call + Some(if 1 > 2 {1} else {2}) == Some(2); + //~^ redundant_closure_call + Some( 1 > 2 ) == Some(true); + //~^ redundant_closure_call +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs index da5dd7ef263b6..34f228786786b 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs @@ -144,3 +144,15 @@ fn issue_12358() { // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + (|| { Some(true) })() == Some(true); + //~^ redundant_closure_call + (|| Some(true))() == Some(true); + //~^ redundant_closure_call + (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + //~^ redundant_closure_call + (|| { Some( 1 > 2 ) })() == Some(true); + //~^ redundant_closure_call +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr index 2c35aafbe3109..a5591cf7813b7 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr @@ -95,7 +95,7 @@ error: try not to call a closure in the expression where it is declared --> tests/ui/redundant_closure_call_fixable.rs:63:13 | LL | let a = (|| || || 123)(); - | ^^^^^^^^^^^^^^^^ help: try doing something like: `(|| || 123)` + | ^^^^^^^^^^^^^^^^ help: try doing something like: `|| || 123` error: try not to call a closure in the expression where it is declared --> tests/ui/redundant_closure_call_fixable.rs:68:13 @@ -145,5 +145,29 @@ error: try not to call a closure in the expression where it is declared LL | std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `13_i32 + 36_i32` -error: aborting due to 17 previous errors +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:150:5 + | +LL | (|| { Some(true) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:152:5 + | +LL | (|| Some(true))() == Some(true); + | ^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:154:5 + | +LL | (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(if 1 > 2 {1} else {2})` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:156:5 + | +LL | (|| { Some( 1 > 2 ) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some( 1 > 2 )` + +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/return_and_then.fixed b/src/tools/clippy/tests/ui/return_and_then.fixed index 8d9481d159512..8ee259b97f3d4 100644 --- a/src/tools/clippy/tests/ui/return_and_then.fixed +++ b/src/tools/clippy/tests/ui/return_and_then.fixed @@ -99,6 +99,92 @@ fn main() { }; None } + + #[expect(clippy::diverging_sub_expression)] + fn with_return_in_expression() -> Option { + _ = ( + return { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + }, + //~^ return_and_then + 10, + ); + } + + fn inside_if(a: bool, i: Option) -> Option { + if a { + let i = i?; + if i > 3 { Some(i) } else { None } + //~^ return_and_then + } else { + Some(42) + } + } + + fn inside_match(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let i = i?; + if i > 3 { Some(i) } else { None } + }, + //~^ return_and_then + 3 | 4 => Some(42), + _ => None, + } + } + + fn inside_match_and_block_and_if(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let a = a * 3; + if a.is_multiple_of(2) { + let i = i?; + if i > 3 { Some(i) } else { None } + //~^ return_and_then + } else { + Some(10) + } + }, + 3 | 4 => Some(42), + _ => None, + } + } + + #[expect(clippy::never_loop)] + fn with_break(i: Option) -> Option { + match i { + Some(1) => loop { + break ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + }, + Some(2) => 'foo: loop { + loop { + break 'foo ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + } + }, + Some(3) => 'bar: { + break 'bar ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + }, + Some(4) => 'baz: loop { + _ = loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + }; + }, + _ => None, + } + } } fn gen_option(n: i32) -> Option { @@ -124,3 +210,48 @@ mod issue14781 { Ok(()) } } + +mod issue15111 { + #[derive(Debug)] + struct EvenOdd { + even: Option, + odd: Option, + } + + impl EvenOdd { + fn new(i: Option) -> Self { + Self { + even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }), + odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }), + } + } + } + + fn with_if_let(i: Option) -> u32 { + if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) { + x + } else { + std::hint::black_box(0) + } + } + + fn main() { + let _ = EvenOdd::new(Some(2)); + } +} + +mod issue14927 { + use std::path::Path; + struct A { + pub func: fn(check: bool, a: &Path, b: Option<&Path>), + } + const MY_A: A = A { + func: |check, a, b| { + if check { + let _ = (); + } else if let Some(parent) = b.and_then(|p| p.parent()) { + let _ = (); + } + }, + }; +} diff --git a/src/tools/clippy/tests/ui/return_and_then.rs b/src/tools/clippy/tests/ui/return_and_then.rs index beada921a9187..dcb344f142bb6 100644 --- a/src/tools/clippy/tests/ui/return_and_then.rs +++ b/src/tools/clippy/tests/ui/return_and_then.rs @@ -90,6 +90,75 @@ fn main() { }; None } + + #[expect(clippy::diverging_sub_expression)] + fn with_return_in_expression() -> Option { + _ = ( + return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }), + //~^ return_and_then + 10, + ); + } + + fn inside_if(a: bool, i: Option) -> Option { + if a { + i.and_then(|i| if i > 3 { Some(i) } else { None }) + //~^ return_and_then + } else { + Some(42) + } + } + + fn inside_match(a: u32, i: Option) -> Option { + match a { + 1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }), + //~^ return_and_then + 3 | 4 => Some(42), + _ => None, + } + } + + fn inside_match_and_block_and_if(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let a = a * 3; + if a.is_multiple_of(2) { + i.and_then(|i| if i > 3 { Some(i) } else { None }) + //~^ return_and_then + } else { + Some(10) + } + }, + 3 | 4 => Some(42), + _ => None, + } + } + + #[expect(clippy::never_loop)] + fn with_break(i: Option) -> Option { + match i { + Some(1) => loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + }, + Some(2) => 'foo: loop { + loop { + break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + } + }, + Some(3) => 'bar: { + break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + }, + Some(4) => 'baz: loop { + _ = loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + }; + }, + _ => None, + } + } } fn gen_option(n: i32) -> Option { @@ -115,3 +184,48 @@ mod issue14781 { Ok(()) } } + +mod issue15111 { + #[derive(Debug)] + struct EvenOdd { + even: Option, + odd: Option, + } + + impl EvenOdd { + fn new(i: Option) -> Self { + Self { + even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }), + odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }), + } + } + } + + fn with_if_let(i: Option) -> u32 { + if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) { + x + } else { + std::hint::black_box(0) + } + } + + fn main() { + let _ = EvenOdd::new(Some(2)); + } +} + +mod issue14927 { + use std::path::Path; + struct A { + pub func: fn(check: bool, a: &Path, b: Option<&Path>), + } + const MY_A: A = A { + func: |check, a, b| { + if check { + let _ = (); + } else if let Some(parent) = b.and_then(|p| p.parent()) { + let _ = (); + } + }, + }; +} diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr index 5feca88286057..33867ea818aa7 100644 --- a/src/tools/clippy/tests/ui/return_and_then.stderr +++ b/src/tools/clippy/tests/ui/return_and_then.stderr @@ -146,5 +146,99 @@ LL + if x.len() > 2 { Some(3) } else { None } LL ~ }; | -error: aborting due to 10 previous errors +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:97:20 + | +LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ return { +LL + let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }, + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:105:13 + | +LL | i.and_then(|i| if i > 3 { Some(i) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let i = i?; +LL + if i > 3 { Some(i) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:114:22 + | +LL | 1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ 1 | 2 => { +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }, + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:126:21 + | +LL | i.and_then(|i| if i > 3 { Some(i) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let i = i?; +LL + if i > 3 { Some(i) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:141:23 + | +LL | break i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:146:32 + | +LL | break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break 'foo ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:151:28 + | +LL | break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break 'bar ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index 1820ade422ff5..603ab0accb0b6 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -8,7 +8,6 @@ extern crate alloc; #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use core::hash::Hasher; @@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: core::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index 32c4933098161..b6d4abad9f8fe 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -8,7 +8,6 @@ extern crate alloc; #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use std::hash::Hasher; @@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: std::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index 45d60d235ceb3..a5f8fbbe37cb8 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:14:9 + --> tests/ui/std_instead_of_core.rs:13:9 | LL | use std::hash::Hasher; | ^^^ help: consider importing the item from `core`: `core` @@ -8,61 +8,61 @@ LL | use std::hash::Hasher; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:17:11 + --> tests/ui/std_instead_of_core.rs:16:11 | LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:23:9 + --> tests/ui/std_instead_of_core.rs:22:9 | LL | use std::fmt::{Debug, Result}; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:28:9 + --> tests/ui/std_instead_of_core.rs:27:9 | LL | use std::{ | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:35:15 + --> tests/ui/std_instead_of_core.rs:34:15 | LL | let ptr = std::ptr::null::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:37:21 + --> tests/ui/std_instead_of_core.rs:36:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:41:16 + --> tests/ui/std_instead_of_core.rs:40:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:43:27 + --> tests/ui/std_instead_of_core.rs:42:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:48:9 + --> tests/ui/std_instead_of_core.rs:47:9 | LL | use std::error::Error; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:52:9 + --> tests/ui/std_instead_of_core.rs:51:9 | LL | use std::iter::Iterator; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:59:9 + --> tests/ui/std_instead_of_core.rs:58:9 | LL | use std::vec; | ^^^ help: consider importing the item from `alloc`: `alloc` @@ -71,13 +71,13 @@ LL | use std::vec; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:61:9 + --> tests/ui/std_instead_of_core.rs:60:9 | LL | use std::vec::Vec; | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> tests/ui/std_instead_of_core.rs:67:9 + --> tests/ui/std_instead_of_core.rs:66:9 | LL | use alloc::slice::from_ref; | ^^^^^ help: consider importing the item from `core`: `core` @@ -86,13 +86,13 @@ LL | use alloc::slice::from_ref; = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:82:9 + --> tests/ui/std_instead_of_core.rs:81:9 | LL | std::intrinsics::copy(a, b, 1); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:91:17 + --> tests/ui/std_instead_of_core.rs:90:17 | LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core` diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs new file mode 100644 index 0000000000000..957f472a45448 --- /dev/null +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs @@ -0,0 +1,18 @@ +//@no-rustfix + +#![warn(clippy::std_instead_of_core)] +#![warn(clippy::std_instead_of_alloc)] +#![allow(unused_imports)] + +#[rustfmt::skip] +fn issue14982() { + use std::{collections::HashMap, hash::Hash}; + //~^ std_instead_of_core +} + +#[rustfmt::skip] +fn issue15143() { + use std::{error::Error, vec::Vec, fs::File}; + //~^ std_instead_of_core + //~| std_instead_of_alloc +} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr new file mode 100644 index 0000000000000..0cdec56c99274 --- /dev/null +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr @@ -0,0 +1,30 @@ +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:9:43 + | +LL | use std::{collections::HashMap, hash::Hash}; + | ^^^^ + | + = help: consider importing the item from `core` + = note: `-D clippy::std-instead-of-core` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:15:22 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^^^ + | + = help: consider importing the item from `core` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:15:34 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^ + | + = help: consider importing the item from `alloc` + = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.fixed b/src/tools/clippy/tests/ui/swap_with_temporary.fixed index 4007d998ba068..4b4b0d4aebd2b 100644 --- a/src/tools/clippy/tests/ui/swap_with_temporary.fixed +++ b/src/tools/clippy/tests/ui/swap_with_temporary.fixed @@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { swap(&mut s.t, v.get_mut(0).unwrap()); swap(w.unwrap(), &mut s.t); } + +fn issue15166() { + use std::sync::Mutex; + + struct A { + thing: Mutex>, + } + + impl A { + fn a(&self) { + let mut new_vec = vec![42]; + // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries + swap(&mut new_vec, &mut self.thing.lock().unwrap()); + for v in new_vec { + // Do something with v + } + // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix + *self.thing.lock().unwrap() = vec![42]; + //~^ ERROR: swapping with a temporary value is inefficient + } + } +} + +fn multiple_deref() { + let mut v1 = &mut &mut &mut vec![42]; + ***v1 = vec![]; + //~^ ERROR: swapping with a temporary value is inefficient + + struct Wrapper(T); + impl std::ops::Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + use std::sync::Mutex; + let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42]))); + ***v1.lock().unwrap() = vec![]; + //~^ ERROR: swapping with a temporary value is inefficient +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.rs b/src/tools/clippy/tests/ui/swap_with_temporary.rs index d403c086c0f4f..8e35e6144d99a 100644 --- a/src/tools/clippy/tests/ui/swap_with_temporary.rs +++ b/src/tools/clippy/tests/ui/swap_with_temporary.rs @@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { swap(&mut s.t, v.get_mut(0).unwrap()); swap(w.unwrap(), &mut s.t); } + +fn issue15166() { + use std::sync::Mutex; + + struct A { + thing: Mutex>, + } + + impl A { + fn a(&self) { + let mut new_vec = vec![42]; + // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries + swap(&mut new_vec, &mut self.thing.lock().unwrap()); + for v in new_vec { + // Do something with v + } + // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix + swap(&mut vec![42], &mut self.thing.lock().unwrap()); + //~^ ERROR: swapping with a temporary value is inefficient + } + } +} + +fn multiple_deref() { + let mut v1 = &mut &mut &mut vec![42]; + swap(&mut ***v1, &mut vec![]); + //~^ ERROR: swapping with a temporary value is inefficient + + struct Wrapper(T); + impl std::ops::Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + use std::sync::Mutex; + let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42]))); + swap(&mut vec![], &mut v1.lock().unwrap()); + //~^ ERROR: swapping with a temporary value is inefficient +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.stderr b/src/tools/clippy/tests/ui/swap_with_temporary.stderr index 59355771a9648..5ca4fccd37a2e 100644 --- a/src/tools/clippy/tests/ui/swap_with_temporary.stderr +++ b/src/tools/clippy/tests/ui/swap_with_temporary.stderr @@ -96,5 +96,41 @@ note: this expression returns a temporary value LL | swap(mac!(refmut y), &mut func()); | ^^^^^^ -error: aborting due to 8 previous errors +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:92:13 + | +LL | swap(&mut vec![42], &mut self.thing.lock().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*self.thing.lock().unwrap() = vec![42]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:92:23 + | +LL | swap(&mut vec![42], &mut self.thing.lock().unwrap()); + | ^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:100:5 + | +LL | swap(&mut ***v1, &mut vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1 = vec![]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:100:27 + | +LL | swap(&mut ***v1, &mut vec![]); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:118:5 + | +LL | swap(&mut vec![], &mut v1.lock().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1.lock().unwrap() = vec![]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:118:15 + | +LL | swap(&mut vec![], &mut v1.lock().unwrap()); + | ^^^^^^ + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs index 2e67fb65efcd9..3bae23f198495 100644 --- a/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs +++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs @@ -4,6 +4,7 @@ // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@normalize-stderr-test: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@normalize-stderr-test: "src/tools/clippy/" -> "" #![warn(clippy::let_and_return, clippy::unnecessary_cast)] @@ -12,7 +13,7 @@ fn main() { let a = 3u32; let b = a as u32; //~^ unnecessary_cast - + // Check the provenance of a lint sent through `TyCtxt::node_span_lint()` let c = { let d = 42; diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr index 9d6538112bf05..d5533877b4515 100644 --- a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr +++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr @@ -4,7 +4,7 @@ error: casting to the same type is unnecessary (`u32` -> `u32`) LL | let b = a as u32; | ^^^^^^^^ help: try: `a` | - = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/casts/unnecessary_cast.rs:LL:CC = note: `-D clippy::unnecessary-cast` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` @@ -16,7 +16,7 @@ LL | let d = 42; LL | d | ^ | - = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/returns.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC = note: `-D clippy::let-and-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` help: return the expression directly diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index 3c7243972845f..3109c4af8e287 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -130,3 +130,13 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { //~^ unnecessary_map_or x && y } + +fn issue15180() { + let s = std::sync::Mutex::new(Some("foo")); + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); + //~^ unnecessary_map_or + + let s = &&&&Some("foo"); + _ = s.is_some_and(|s| s == "foo"); + //~^ unnecessary_map_or +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index e734a27badabd..52a55f9fc9e49 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -134,3 +134,13 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { //~^ unnecessary_map_or x && y } + +fn issue15180() { + let s = std::sync::Mutex::new(Some("foo")); + _ = s.lock().unwrap().map_or(false, |s| s == "foo"); + //~^ unnecessary_map_or + + let s = &&&&Some("foo"); + _ = s.map_or(false, |s| s == "foo"); + //~^ unnecessary_map_or +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index 0f9466a6a6b30..99e17e8b34ba2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -326,5 +326,29 @@ LL - let y = b.map_or(true, |b| b == *s); LL + let y = b.is_none_or(|b| b == *s); | -error: aborting due to 26 previous errors +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:140:9 + | +LL | _ = s.lock().unwrap().map_or(false, |s| s == "foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - _ = s.lock().unwrap().map_or(false, |s| s == "foo"); +LL + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:144:9 + | +LL | _ = s.map_or(false, |s| s == "foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - _ = s.map_or(false, |s| s == "foo"); +LL + _ = s.is_some_and(|s| s == "foo"); + | + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed index 645b56fe95e74..ac9fa4de20a60 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -144,3 +144,16 @@ const fn foo() { assert!([42, 55].len() > get_usize()); //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box Option>; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs index 97e90269c5c0c..a3e6c6288ada5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.rs +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -150,3 +150,16 @@ const fn foo() { [42, 55][get_usize()]; //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box Option>; +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.fixed b/src/tools/clippy/tests/ui/zero_ptr.fixed index f2375d57f3a28..f9d9d2db17697 100644 --- a/src/tools/clippy/tests/ui/zero_ptr.fixed +++ b/src/tools/clippy/tests/ui/zero_ptr.fixed @@ -16,3 +16,11 @@ fn main() { let z = 0; let _ = z as *const usize; // this is currently not caught } + +const fn in_const_context() { + #[clippy::msrv = "1.23"] + let _: *const usize = 0 as *const _; + #[clippy::msrv = "1.24"] + let _: *const usize = std::ptr::null(); + //~^ zero_ptr +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.rs b/src/tools/clippy/tests/ui/zero_ptr.rs index ee01e426a43b4..41455fee5b5ff 100644 --- a/src/tools/clippy/tests/ui/zero_ptr.rs +++ b/src/tools/clippy/tests/ui/zero_ptr.rs @@ -16,3 +16,11 @@ fn main() { let z = 0; let _ = z as *const usize; // this is currently not caught } + +const fn in_const_context() { + #[clippy::msrv = "1.23"] + let _: *const usize = 0 as *const _; + #[clippy::msrv = "1.24"] + let _: *const usize = 0 as *const _; + //~^ zero_ptr +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.stderr b/src/tools/clippy/tests/ui/zero_ptr.stderr index 8dc781f36258c..81269de6c607e 100644 --- a/src/tools/clippy/tests/ui/zero_ptr.stderr +++ b/src/tools/clippy/tests/ui/zero_ptr.stderr @@ -31,5 +31,11 @@ error: `0 as *mut _` detected LL | foo(0 as *const _, 0 as *mut _); | ^^^^^^^^^^^ help: try: `std::ptr::null_mut()` -error: aborting due to 5 previous errors +error: `0 as *const _` detected + --> tests/ui/zero_ptr.rs:24:27 + | +LL | let _: *const usize = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `std::ptr::null()` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 4f370758c0064..805baf2af6dd0 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -15,6 +15,8 @@ allow-unauthenticated = [ [close] +[transfer] + [issue-links] [mentions."clippy_lints/src/doc"] @@ -43,12 +45,15 @@ reviewed_label = "S-waiting-on-author" [autolabel."S-waiting-on-review"] new_pr = true +[concern] +# These labels are set when there are unresolved concerns, removed otherwise +labels = ["S-waiting-on-concerns"] + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", "Manishearth", - "blyxyas", ] [assign.owners] diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index 865b9523c39e7..6f380ec8feef3 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -149,49 +149,45 @@

Clippy Lints

{# #}
{# #} {# #} {# #}
{# #}
{{Self::markdown(lint.docs)}}
{# #}
{# Applicability #} -
{# #} - Applicability: {# #} +
{# #} + Applicability: {#+ #} {{ lint.applicability_str() }} {# #} (?) {# #}
{# Clippy version #} -
{# #} - {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {# #} +
{# #} + {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {#+ #} {{lint.version}} {# #}
{# Open related issues #} -
{# #} +
{# #} Related Issues {# #}
{# Jump to source #} {% if let Some(id_location) = lint.id_location %} -
{# #} +
{# #} View Source {# #}
{% endif %} diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 285aa34e70186..ee13f1c0cd875 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -554,10 +554,10 @@ function addListeners() { return; } - if (event.target.classList.contains("lint-anchor")) { - lintAnchor(event); - } else if (event.target.classList.contains("copy-to-clipboard")) { + if (event.target.classList.contains("copy-to-clipboard")) { copyToClipboard(event); + } else if (event.target.classList.contains("anchor")) { + lintAnchor(event); } }); diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css index 3cc7a919c23ab..022ea87520004 100644 --- a/src/tools/clippy/util/gh-pages/style.css +++ b/src/tools/clippy/util/gh-pages/style.css @@ -50,11 +50,25 @@ div.panel div.panel-body button.open { .panel-heading { cursor: pointer; } -.panel-title { display: flex; flex-wrap: wrap;} -.panel-title .label { display: inline-block; } +.lint-title { + cursor: pointer; + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + display: flex; + flex-wrap: wrap; + background: var(--theme-hover); + color: var(--fg); + border: 1px solid var(--theme-popup-border); + padding: 10px 15px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + gap: 4px; +} + +.lint-title .label { display: inline-block; } .panel-title-name { flex: 1; min-width: 400px;} -.panel-title-name span { vertical-align: bottom; } .panel .panel-title-name .anchor { display: none; } .panel:hover .panel-title-name .anchor { display: inline;} @@ -147,7 +161,7 @@ div.panel div.panel-body button.open { display: flex; flex-flow: column; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-top: 1px solid var(--theme-popup-border); } } @@ -156,12 +170,12 @@ div.panel div.panel-body button.open { display: flex; flex-flow: row; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-left: 1px solid var(--theme-popup-border); } } -.lint-additional-info-item { +.lint-additional-info-container > div { display: inline-flex; min-width: 200px; flex-grow: 1; diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index fa833b51fa31b..35555723f5d6d 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -34,4 +34,5 @@ windows-sys = { version = "0.59", features = [ "Wdk_Storage_FileSystem", ] } +# Make sure we are not part of the rustc workspace. [workspace]