diff options
| author | Ben Kimock <kimockb@gmail.com> | 2024-11-09 06:57:05 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-09 06:57:05 +0000 |
| commit | c272bb4ec0a09744bef47fbe15ac6cded53d0216 (patch) | |
| tree | 0c85c9634f6ff5ec409a9589eb9636d8a1d435b9 /src/tools | |
| parent | d7aceeea71d4d32c7db671d39a448aa051bc90f6 (diff) | |
| parent | 15d883e5b73ce6758926d8e3ef4a307f9ac6092f (diff) | |
| download | rust-c272bb4ec0a09744bef47fbe15ac6cded53d0216.tar.gz rust-c272bb4ec0a09744bef47fbe15ac6cded53d0216.zip | |
Merge pull request #4019 from rust-lang/rustup-2024-11-09
Automatic Rustup
Diffstat (limited to 'src/tools')
347 files changed, 7228 insertions, 1940 deletions
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 925cbfe09a4..39d9158a1ff 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -157,7 +157,6 @@ static TARGETS: &[&str] = &[ "thumbv8m.main-none-eabihf", "wasm32-unknown-emscripten", "wasm32-unknown-unknown", - "wasm32-wasi", "wasm32-wasip1", "wasm32-wasip1-threads", "wasm32-wasip2", diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index cf0a8bde202..bcb3193ad67 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -1,17 +1,8 @@ name: Clippy Dev Test on: - push: - branches: - - auto - - try + merge_group: pull_request: - # Only run on paths, that get checked by the clippy_dev tool - paths: - - 'CHANGELOG.md' - - 'README.md' - - '**.stderr' - - '**.rs' env: RUST_BACKTRACE: 1 @@ -47,28 +38,21 @@ jobs: cargo check git reset --hard HEAD - # These jobs doesn't actually test anything, but they're only used to tell - # bors the build completed, as there is no practical way to detect when a - # workflow is successful listening to webhooks only. - # - # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! - - end-success: - name: bors dev test finished - if: github.event.pusher.name == 'bors' && success() - runs-on: ubuntu-latest - needs: [clippy_dev] - - steps: - - name: Mark the job as successful - run: exit 0 - - end-failure: - name: bors dev test finished - if: github.event.pusher.name == 'bors' && (failure() || cancelled()) + conclusion_dev: + needs: [ clippy_dev ] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} runs-on: ubuntu-latest - needs: [clippy_dev] - steps: - - name: Mark the job as a failure - run: exit 1 + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index 026771e6fcf..49622048050 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -1,10 +1,7 @@ -name: Clippy Test (bors) +name: Clippy Test (merge queue) on: - push: - branches: - - auto - - try + merge_group: env: RUST_BACKTRACE: 1 @@ -13,11 +10,6 @@ env: CARGO_INCREMENTAL: 0 RUSTFLAGS: -D warnings -concurrency: - # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. - group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" - cancel-in-progress: true - defaults: run: shell: bash @@ -218,28 +210,21 @@ jobs: env: INTEGRATION: ${{ matrix.integration }} - # These jobs doesn't actually test anything, but they're only used to tell - # bors the build completed, as there is no practical way to detect when a - # workflow is successful listening to webhooks only. - # - # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! - - end-success: - name: bors test finished - if: github.event.pusher.name == 'bors' && success() + conclusion: + needs: [ changelog, base, metadata_collection, integration_build, integration ] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} runs-on: ubuntu-latest - needs: [changelog, base, metadata_collection, integration_build, integration] - - steps: - - name: Mark the job as successful - run: exit 0 - - end-failure: - name: bors test finished - if: github.event.pusher.name == 'bors' && (failure() || cancelled()) - runs-on: ubuntu-latest - needs: [changelog, base, metadata_collection, integration_build, integration] - steps: - - name: Mark the job as a failure - run: exit 1 + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index 0a0538490cc..2e5b5bd41df 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -1,24 +1,7 @@ name: Clippy Test on: - push: - # Ignore bors branches, since they are covered by `clippy_bors.yml` - branches-ignore: - - auto - - try - # Don't run Clippy tests, when only text files were modified - paths-ignore: - - 'COPYRIGHT' - - 'LICENSE-*' - - '**.md' - - '**.txt' pull_request: - # Don't run Clippy tests, when only text files were modified - paths-ignore: - - 'COPYRIGHT' - - 'LICENSE-*' - - '**.md' - - '**.txt' env: RUST_BACKTRACE: 1 @@ -35,7 +18,7 @@ concurrency: jobs: base: - # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml + # NOTE: If you modify this job, make sure you copy the changes to clippy_mq.yml runs-on: ubuntu-latest steps: @@ -73,3 +56,24 @@ jobs: run: .github/driver.sh env: OS: ${{ runner.os }} + + # We need to have the "conclusion" job also on PR CI, to make it possible + # to add PRs to a merge queue. + conclusion: + needs: [ base ] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 94f494b65c4..32dc251c836 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -52,7 +52,7 @@ jobs: run: cargo generate-lockfile - name: Cache - uses: Swatinem/rust-cache@v2.7.0 + uses: Swatinem/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/master' }} diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index a1b011dc32d..0d402fe7064 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -1,13 +1,8 @@ name: Remark on: - push: - branches: - - auto - - try + merge_group: pull_request: - paths: - - '**.md' jobs: remark: @@ -45,28 +40,21 @@ jobs: - name: Build mdbook run: mdbook build book - # These jobs doesn't actually test anything, but they're only used to tell - # bors the build completed, as there is no practical way to detect when a - # workflow is successful listening to webhooks only. - # - # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! - - end-success: - name: bors remark test finished - if: github.event.pusher.name == 'bors' && success() - runs-on: ubuntu-latest - needs: [remark] - - steps: - - name: Mark the job as successful - run: exit 0 - - end-failure: - name: bors remark test finished - if: github.event.pusher.name == 'bors' && (failure() || cancelled()) + conclusion_remark: + needs: [ remark ] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} runs-on: ubuntu-latest - needs: [remark] - steps: - - name: Mark the job as a failure - run: exit 1 + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 4bdbc91db93..161fa630ed4 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5331,6 +5331,7 @@ Released 2018-09-13 [`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant +[`arbitrary_source_item_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering [`arc_with_non_send_sync`]: https://rust-lang.github.io/rust-clippy/master/index.html#arc_with_non_send_sync [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions @@ -5689,6 +5690,7 @@ Released 2018-09-13 [`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default [`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names +[`map_all_any_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_all_any_identity [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry @@ -5696,6 +5698,7 @@ Released 2018-09-13 [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten [`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or +[`map_with_unused_argument_over_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro @@ -5761,6 +5764,7 @@ Released 2018-09-13 [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount [`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type +[`needless_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_as_bytes [`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign @@ -6205,12 +6209,14 @@ Released 2018-09-13 [`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items +[`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline [`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline [`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold +[`source-item-ordering`]: https://doc.rust-lang.org/clippy/lint_configuration.html#source-item-ordering [`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold [`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces [`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold @@ -6218,6 +6224,7 @@ Released 2018-09-13 [`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack [`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold [`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold +[`trait-assoc-item-kinds-order`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trait-assoc-item-kinds-order [`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit [`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold [`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index b1a59238c82..1f6c918fc6c 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -21,7 +21,6 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Rust Analyzer](#rust-analyzer) - [How Clippy works](#how-clippy-works) - [Issue and PR triage](#issue-and-pr-triage) - - [Bors and Homu](#bors-and-homu) - [Contributions](#contributions) - [License](#license) @@ -213,16 +212,6 @@ We have prioritization labels and a sync-blocker label, which are described belo Or rather: before the sync this should be addressed, e.g. by removing a lint again, so it doesn't hit beta/stable. -## Bors and Homu - -We use a bot powered by [Homu][homu] to help automate testing and landing of pull -requests in Clippy. The bot's username is @bors. - -You can find the Clippy bors queue [here][homu_queue]. - -If you have @bors permissions, you can find an overview of the available -commands [here][homu_instructions]. - [triage]: https://forge.rust-lang.org/release/triage-procedure.html [l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash [l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug @@ -230,9 +219,6 @@ commands [here][homu_instructions]. [p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium [p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high [l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker -[homu]: https://github.com/rust-lang/homu -[homu_instructions]: https://bors.rust-lang.org/ -[homu_queue]: https://bors.rust-lang.org/queue/clippy ## Contributions @@ -244,7 +230,7 @@ All PRs should include a `changelog` entry with a short comment explaining the c "what do you believe is important from an outsider's perspective?" Often, PRs are only related to a single property of a lint, and then it's good to mention that one. Otherwise, it's better to include too much detail than too little. -Clippy's [changelog] is created from these comments. Every release, someone gets all commits from bors with a +Clippy's [changelog] is created from these comments. Every release, someone gets all merge commits with a `changelog: XYZ` entry and combines them into the changelog. This is a manual process. Examples: diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 1f7784fc489..50a2afbfc07 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -39,7 +39,7 @@ toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" -pulldown-cmark = "0.11" +pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } rinja = { version = "0.3", default-features = false, features = ["config"] } # UI test dependencies diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index ec76a6dfb08..cb3a22d4288 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -1,11 +1,10 @@ # Clippy -[/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto) [](#license) A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md index 7bdfb97c3ac..5d2c3972b06 100644 --- a/src/tools/clippy/book/src/README.md +++ b/src/tools/clippy/book/src/README.md @@ -1,12 +1,11 @@ # Clippy -[/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto) [](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/src/tools/clippy/book/src/development/README.md b/src/tools/clippy/book/src/development/README.md index 8f09f66f595..b33cdc00ead 100644 --- a/src/tools/clippy/book/src/development/README.md +++ b/src/tools/clippy/book/src/development/README.md @@ -53,7 +53,6 @@ book](../lints.md). > - IDE setup > - High level overview on how Clippy works > - Triage procedure -> - Bors and Homu [ast]: https://rustc-dev-guide.rust-lang.org/syntax-intro.html [hir]: https://rustc-dev-guide.rust-lang.org/hir.html diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 09171d86a20..77910917963 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { // Check our expr is calling a method if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind // Check the name of this method is `some_method` - && path.ident.name == sym!(some_method) + && path.ident.name.as_str() == "some_method" // Optionally, check the type of the self argument. // - See "Checking for a specific type" { @@ -167,7 +167,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // Check if item is a method/function if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - && impl_item.ident.name == sym!(some_method) + && impl_item.ident.name.as_str() == "some_method" // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` diff --git a/src/tools/clippy/book/src/development/method_checking.md b/src/tools/clippy/book/src/development/method_checking.md index 56d1be37519..9c5d4b516db 100644 --- a/src/tools/clippy/book/src/development/method_checking.md +++ b/src/tools/clippy/book/src/development/method_checking.md @@ -3,8 +3,8 @@ In some scenarios we might want to check for methods when developing a lint. There are two kinds of questions that we might be curious about: -- Invocation: Does an expression call a specific method? -- Definition: Does an `impl` define a method? +- Invocation: Does an expression call a specific method? +- Definition: Does an `impl` define a method? ## Checking if an `expr` is calling a specific method @@ -23,7 +23,7 @@ impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { // Check our expr is calling a method with pattern matching if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind // Check if the name of this method is `our_fancy_method` - && path.ident.name == sym!(our_fancy_method) + && path.ident.name.as_str() == "our_fancy_method" // We can check the type of the self argument whenever necessary. // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) @@ -41,10 +41,6 @@ information on the pattern matching. As mentioned in [Define Lints](defining_lints.md#lint-types), the `methods` lint type is full of pattern matching with `MethodCall` in case the reader wishes to explore more. -Additionally, we use the [`clippy_utils::sym!`][sym] macro to conveniently -convert an input `our_fancy_method` into a `Symbol` and compare that symbol to -the [`Ident`]'s name in the [`PathSegment`] in the [`MethodCall`]. - ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other @@ -71,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // Check if item is a method/function if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `our_fancy_method` - && impl_item.ident.name == sym!(our_fancy_method) + && impl_item.ident.name.as_str() == "our_fancy_method" // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go even further and even check if its return type is `String` @@ -85,9 +81,6 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { [`check_impl_item`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_impl_item [`ExprKind`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/enum.ExprKind.html -[`Ident`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/struct.Ident.html [`ImplItem`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir/hir/struct.ImplItem.html [`LateLintPass`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html [`MethodCall`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/enum.ExprKind.html#variant.MethodCall -[`PathSegment`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/struct.PathSegment.html -[sym]: https://doc.rust-lang.org/stable/nightly-rustc/clippy_utils/macro.sym.html diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 43b551ae216..670b5cbef82 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -456,7 +456,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** @@ -666,6 +666,16 @@ crate. For example, `pub(crate)` items. * [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) +## `module-item-order-groupings` +The named groupings of different source item kinds within modules. + +**Default Value:** `[["modules", ["extern_crate", "mod", "foreign_mod"]], ["use", ["use"]], ["macros", ["macro"]], ["global_asm", ["global_asm"]], ["UPPER_SNAKE_CASE", ["static", "const"]], ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]]]` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` @@ -710,6 +720,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold) * [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) * [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) +* [`map_with_unused_argument_over_ranges`](https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges) * [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) * [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) * [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) @@ -783,6 +794,16 @@ The maximum number of single char bindings a scope may have * [`many_single_char_names`](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) +## `source-item-ordering` +Which kind of elements should be ordered internally, possible values being `enum`, `impl`, `module`, `struct`, `trait`. + +**Default Value:** `["enum", "impl", "module", "struct", "trait"]` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `stack-size-threshold` The maximum allowed stack size for functions in bytes @@ -862,6 +883,16 @@ The maximum number of lines a function or method can have * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) +## `trait-assoc-item-kinds-order` +The order of associated items in traits. + +**Default Value:** `["const", "type", "fn"]` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `trivial-copy-size-limit` The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. By default there is no limit diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 4757c0b1339..600d5b6e2c8 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,6 +1,10 @@ use crate::ClippyConfiguration; use crate::msrvs::Msrv; -use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; +use crate::types::{ + DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, + SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, + SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, +}; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -17,8 +21,9 @@ use std::{cmp, env, fmt, fs, io}; #[rustfmt::skip] const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", + "MHz", "GHz", "THz", "AccessKit", - "CoreFoundation", "CoreGraphics", "CoreText", + "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", @@ -46,6 +51,29 @@ const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"]; const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] = &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]; +const DEFAULT_MODULE_ITEM_ORDERING_GROUPS: &[(&str, &[SourceItemOrderingModuleItemKind])] = { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingModuleItemKind::*; + &[ + ("modules", &[ExternCrate, Mod, ForeignMod]), + ("use", &[Use]), + ("macros", &[Macro]), + ("global_asm", &[GlobalAsm]), + ("UPPER_SNAKE_CASE", &[Static, Const]), + ("PascalCase", &[TyAlias, Enum, Struct, Union, Trait, TraitAlias, Impl]), + ("lower_snake_case", &[Fn]), + ] +}; +const DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER: &[SourceItemOrderingTraitAssocItemKind] = { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingTraitAssocItemKind::*; + &[Const, Type, Fn] +}; +const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingCategory::*; + &[Enum, Impl, Module, Struct, Trait] +}; /// Conf with parse errors #[derive(Default)] @@ -102,7 +130,9 @@ pub fn sanitize_explanation(raw_docs: &str) -> String { // Remove tags and hidden code: let mut explanation = String::with_capacity(128); let mut in_code = false; - for line in raw_docs.lines().map(str::trim) { + for line in raw_docs.lines() { + let line = line.strip_prefix(' ').unwrap_or(line); + if let Some(lang) = line.strip_prefix("```") { let tag = lang.split_once(',').map_or(lang, |(left, _)| left); if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") { @@ -530,6 +560,9 @@ define_Conf! { /// crate. For example, `pub(crate)` items. #[lints(missing_docs_in_private_items)] missing_docs_in_crate_items: bool = false, + /// The named groupings of different source item kinds within modules. + #[lints(arbitrary_source_item_ordering)] + module_item_order_groupings: SourceItemOrderingModuleItemGroupings = DEFAULT_MODULE_ITEM_ORDERING_GROUPS.into(), /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = "current version"] #[lints( @@ -570,6 +603,7 @@ define_Conf! { manual_try_fold, map_clone, map_unwrap_or, + map_with_unused_argument_over_ranges, match_like_matches_macro, mem_replace_with_default, missing_const_for_fn, @@ -608,6 +642,9 @@ define_Conf! { /// The maximum number of single char bindings a scope may have #[lints(many_single_char_names)] single_char_binding_names_threshold: u64 = 4, + /// Which kind of elements should be ordered internally, possible values being `enum`, `impl`, `module`, `struct`, `trait`. + #[lints(arbitrary_source_item_ordering)] + source_item_ordering: SourceItemOrdering = DEFAULT_SOURCE_ITEM_ORDERING.into(), /// The maximum allowed stack size for functions in bytes #[lints(large_stack_frames)] stack_size_threshold: u64 = 512_000, @@ -637,6 +674,9 @@ define_Conf! { /// The maximum number of lines a function or method can have #[lints(too_many_lines)] too_many_lines_threshold: u64 = 100, + /// The order of associated items in traits. + #[lints(arbitrary_source_item_ordering)] + trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(), /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by /// reference. By default there is no limit #[default_text = "target_pointer_width * 2"] diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index 42651521f8d..1c3f32c2514 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -20,6 +20,7 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_session; extern crate rustc_span; +extern crate smallvec; mod conf; mod metadata; diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index 2f4da4cba3d..764ca8fb50a 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -3,6 +3,7 @@ use rustc_attr::parse_version; use rustc_session::{RustcVersion, Session}; use rustc_span::{Symbol, sym}; use serde::Deserialize; +use smallvec::{SmallVec, smallvec}; use std::fmt; macro_rules! msrv_aliases { @@ -18,7 +19,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } - 1,82,0 { IS_NONE_OR } + 1,82,0 { IS_NONE_OR, REPEAT_N } 1,81,0 { LINT_REASONS_STABILIZATION } 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } @@ -54,7 +55,7 @@ msrv_aliases! { 1,33,0 { UNDERSCORE_IMPORTS } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } - 1,28,0 { FROM_BOOL } + 1,28,0 { FROM_BOOL, REPEAT_WITH } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } 1,24,0 { IS_ASCII_DIGIT } @@ -67,7 +68,7 @@ msrv_aliases! { /// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]` #[derive(Debug, Clone)] pub struct Msrv { - stack: Vec<RustcVersion>, + stack: SmallVec<[RustcVersion; 2]>, } impl fmt::Display for Msrv { @@ -87,14 +88,14 @@ impl<'de> Deserialize<'de> for Msrv { { let v = String::deserialize(deserializer)?; parse_version(Symbol::intern(&v)) - .map(|v| Msrv { stack: vec![v] }) + .map(|v| Msrv { stack: smallvec![v] }) .ok_or_else(|| serde::de::Error::custom("not a valid Rust version")) } } impl Msrv { pub fn empty() -> Msrv { - Msrv { stack: Vec::new() } + Msrv { stack: SmallVec::new() } } pub fn read_cargo(&mut self, sess: &Session) { @@ -103,7 +104,7 @@ impl Msrv { .and_then(|v| parse_version(Symbol::intern(&v))); match (self.current(), cargo_msrv) { - (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv], + (None, Some(cargo_msrv)) => self.stack = smallvec![cargo_msrv], (Some(clippy_msrv), Some(cargo_msrv)) => { if clippy_msrv != cargo_msrv { sess.dcx().warn(format!( diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index bab63911182..fe576424148 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -1,5 +1,6 @@ use serde::de::{self, Deserializer, Visitor}; use serde::{Deserialize, Serialize, ser}; +use std::collections::HashMap; use std::fmt; #[derive(Debug, Deserialize)] @@ -102,6 +103,306 @@ impl<'de> Deserialize<'de> for MacroMatcher { } } +/// Represents the item categories that can be ordered by the source ordering lint. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SourceItemOrderingCategory { + Enum, + Impl, + Module, + Struct, + Trait, +} + +/// Represents which item categories are enabled for ordering. +/// +/// The [`Deserialize`] implementation checks that there are no duplicates in +/// the user configuration. +pub struct SourceItemOrdering(Vec<SourceItemOrderingCategory>); + +impl SourceItemOrdering { + pub fn contains(&self, category: &SourceItemOrderingCategory) -> bool { + self.0.contains(category) + } +} + +impl<T> From<T> for SourceItemOrdering +where + T: Into<Vec<SourceItemOrderingCategory>>, +{ + fn from(value: T) -> Self { + Self(value.into()) + } +} + +impl core::fmt::Debug for SourceItemOrdering { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl<'de> Deserialize<'de> for SourceItemOrdering { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let items = Vec::<SourceItemOrderingCategory>::deserialize(deserializer)?; + let mut items_set = std::collections::HashSet::new(); + + for item in &items { + if items_set.contains(item) { + return Err(de::Error::custom(format!( + "The category \"{item:?}\" was enabled more than once in the source ordering configuration." + ))); + } + items_set.insert(item); + } + + Ok(Self(items)) + } +} + +impl Serialize for SourceItemOrdering { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + self.0.serialize(serializer) + } +} + +/// Represents the items that can occur within a module. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SourceItemOrderingModuleItemKind { + ExternCrate, + Mod, + ForeignMod, + Use, + Macro, + GlobalAsm, + Static, + Const, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl, + Fn, +} + +impl SourceItemOrderingModuleItemKind { + pub fn all_variants() -> Vec<Self> { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingModuleItemKind::*; + vec![ + ExternCrate, + Mod, + ForeignMod, + Use, + Macro, + GlobalAsm, + Static, + Const, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl, + Fn, + ] + } +} + +/// Represents the configured ordering of items within a module. +/// +/// The [`Deserialize`] implementation checks that no item kinds have been +/// omitted and that there are no duplicates in the user configuration. +#[derive(Clone)] +pub struct SourceItemOrderingModuleItemGroupings { + groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)>, + lut: HashMap<SourceItemOrderingModuleItemKind, usize>, +} + +impl SourceItemOrderingModuleItemGroupings { + fn build_lut( + groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)], + ) -> HashMap<SourceItemOrderingModuleItemKind, usize> { + let mut lut = HashMap::new(); + for (group_index, (_, items)) in groups.iter().enumerate() { + for item in items { + lut.insert(item.clone(), group_index); + } + } + lut + } + + pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<usize> { + self.lut.get(item).copied() + } +} + +impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrderingModuleItemGroupings { + fn from(value: &[(&str, &[SourceItemOrderingModuleItemKind])]) -> Self { + let groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)> = + value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect(); + let lut = Self::build_lut(&groups); + Self { groups, lut } + } +} + +impl core::fmt::Debug for SourceItemOrderingModuleItemGroupings { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.groups.fmt(f) + } +} + +impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let groups = Vec::<(String, Vec<SourceItemOrderingModuleItemKind>)>::deserialize(deserializer)?; + let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum(); + let lut = Self::build_lut(&groups); + + let mut expected_items = SourceItemOrderingModuleItemKind::all_variants(); + for item in lut.keys() { + expected_items.retain(|i| i != item); + } + + let all_items = SourceItemOrderingModuleItemKind::all_variants(); + if expected_items.is_empty() && items_total == all_items.len() { + let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else { + return Err(de::Error::custom("Error in internal LUT.")); + }; + let Some((_, use_group_items)) = groups.get(*use_group_index) else { + return Err(de::Error::custom("Error in internal LUT.")); + }; + if use_group_items.len() > 1 { + return Err(de::Error::custom( + "The group containing the \"use\" item kind may not contain any other item kinds. \ + The \"use\" items will (generally) be sorted by rustfmt already. \ + Therefore it makes no sense to implement linting rules that may conflict with rustfmt.", + )); + } + + Ok(Self { groups, lut }) + } else if items_total != all_items.len() { + Err(de::Error::custom(format!( + "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \ + The module item kinds are: {all_items:?}" + ))) + } else { + Err(de::Error::custom(format!( + "Not all module item kinds were part of the configured source ordering rule. \ + All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ + The module item kinds are: {all_items:?}" + ))) + } + } +} + +impl Serialize for SourceItemOrderingModuleItemGroupings { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + self.groups.serialize(serializer) + } +} + +/// Represents all kinds of trait associated items. +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SourceItemOrderingTraitAssocItemKind { + Const, + Fn, + Type, +} + +impl SourceItemOrderingTraitAssocItemKind { + pub fn all_variants() -> Vec<Self> { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingTraitAssocItemKind::*; + vec![Const, Fn, Type] + } +} + +/// Represents the order in which associated trait items should be ordered. +/// +/// The reason to wrap a `Vec` in a newtype is to be able to implement +/// [`Deserialize`]. Implementing `Deserialize` allows for implementing checks +/// on configuration completeness at the time of loading the clippy config, +/// letting the user know if there's any issues with the config (e.g. not +/// listing all item kinds that should be sorted). +#[derive(Clone)] +pub struct SourceItemOrderingTraitAssocItemKinds(Vec<SourceItemOrderingTraitAssocItemKind>); + +impl SourceItemOrderingTraitAssocItemKinds { + pub fn index_of(&self, item: &SourceItemOrderingTraitAssocItemKind) -> Option<usize> { + self.0.iter().position(|i| i == item) + } +} + +impl<T> From<T> for SourceItemOrderingTraitAssocItemKinds +where + T: Into<Vec<SourceItemOrderingTraitAssocItemKind>>, +{ + fn from(value: T) -> Self { + Self(value.into()) + } +} + +impl core::fmt::Debug for SourceItemOrderingTraitAssocItemKinds { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let items = Vec::<SourceItemOrderingTraitAssocItemKind>::deserialize(deserializer)?; + + let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants(); + for item in &items { + expected_items.retain(|i| i != item); + } + + let all_items = SourceItemOrderingTraitAssocItemKind::all_variants(); + if expected_items.is_empty() && items.len() == all_items.len() { + Ok(Self(items)) + } else if items.len() != all_items.len() { + Err(de::Error::custom(format!( + "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \ + The trait associated item kinds are: {all_items:?}", + ))) + } else { + Err(de::Error::custom(format!( + "Not all trait associated item kinds were part of the configured source ordering rule. \ + All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ + The trait associated item kinds are: {all_items:?}" + ))) + } + } +} + +impl Serialize for SourceItemOrderingTraitAssocItemKinds { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + self.0.serialize(serializer) + } +} + // these impls are never actually called but are used by the various config options that default to // empty lists macro_rules! unimplemented_serialize { diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index d367fefec61..a2d1236629f 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -20,8 +20,14 @@ pub fn run(port: u16, lint: Option<String>) -> ! { loop { let index_time = mtime("util/gh-pages/index.html"); + let times = [ + "clippy_lints/src", + "util/gh-pages/index_template.html", + "tests/compile-test.rs", + ] + .map(mtime); - if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") { + if times.iter().any(|&time| index_time < time) { Command::new(env::var("CARGO").unwrap_or("cargo".into())) .arg("collect-metadata") .spawn() diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index d6ed36d52f4..795456ad3c5 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -762,13 +762,19 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { Literal{..}(desc) ); - if let Some(LintDeclSearchResult { - token_kind: TokenKind::CloseBrace, - range, - .. - }) = iter.next() - { - lints.push(Lint::new(name, group, desc, module, start..range.end)); + if let Some(end) = iter.find_map(|t| { + if let LintDeclSearchResult { + token_kind: TokenKind::CloseBrace, + range, + .. + } = t + { + Some(range.end) + } else { + None + } + }) { + lints.push(Lint::new(name, group, desc, module, start..end)); } } } 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 new file mode 100644 index 00000000000..8719f61a890 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -0,0 +1,531 @@ +use clippy_config::Conf; +use clippy_config::types::{ + SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, + SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, +}; +use clippy_utils::diagnostics::span_lint_and_note; +use rustc_hir::{ + AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, UseKind, + Variant, VariantData, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// + /// Confirms that items are sorted in source files as per configuration. + /// + /// ### Why restrict this? + /// + /// Keeping a consistent ordering throughout the codebase helps with working + /// as a team, and possibly improves maintainability of the codebase. The + /// idea is that by defining a consistent and enforceable rule for how + /// source files are structured, less time will be wasted during reviews on + /// a topic that is (under most circumstances) not relevant to the logic + /// implemented in the code. Sometimes this will be referred to as + /// "bikeshedding". + /// + /// ### Default Ordering and Configuration + /// + /// As there is no generally applicable rule, and each project may have + /// different requirements, the lint can be configured with high + /// granularity. The configuration is split into two stages: + /// + /// 1. Which item kinds that should have an internal order enforced. + /// 2. Individual ordering rules per item kind. + /// + /// The item kinds that can be linted are: + /// - Module (with customized groupings, alphabetical within) + /// - Trait (with customized order of associated items, alphabetical within) + /// - Enum, Impl, Struct (purely alphabetical) + /// + /// #### Module Item Order + /// + /// Due to the large variation of items within modules, the ordering can be + /// configured on a very granular level. Item kinds can be grouped together + /// arbitrarily, items within groups will be ordered alphabetically. The + /// following table shows the default groupings: + /// + /// | Group | Item Kinds | + /// |--------------------|----------------------| + /// | `modules` | "mod", "foreign_mod" | + /// | `use` | "use" | + /// | `macros` | "macro" | + /// | `global_asm` | "global_asm" | + /// | `UPPER_SNAKE_CASE` | "static", "const" | + /// | `PascalCase` | "ty_alias", "opaque_ty", "enum", "struct", "union", "trait", "trait_alias", "impl" | + /// | `lower_snake_case` | "fn" | + /// + /// All item kinds must be accounted for to create an enforceable linting + /// rule set. + /// + /// ### Known Problems + /// + /// #### Performance Impact + /// + /// Keep in mind, that ordering source code alphabetically can lead to + /// reduced performance in cases where the most commonly used enum variant + /// isn't the first entry anymore, and similar optimizations that can reduce + /// branch misses, cache locality and such. Either don't use this lint if + /// that's relevant, or disable the lint in modules or items specifically + /// where it matters. Other solutions can be to use profile guided + /// optimization (PGO), post-link optimization (e.g. using BOLT for LLVM), + /// or other advanced optimization methods. A good starting point to dig + /// into optimization is [cargo-pgo][cargo-pgo]. + /// + /// #### Lints on a Contains basis + /// + /// The lint can be disabled only on a "contains" basis, but not per element + /// within a "container", e.g. the lint works per-module, per-struct, + /// per-enum, etc. but not for "don't order this particular enum variant". + /// + /// #### Module documentation + /// + /// Module level rustdoc comments are not part of the resulting syntax tree + /// and as such cannot be linted from within `check_mod`. Instead, the + /// `rustdoc::missing_documentation` lint may be used. + /// + /// #### Module Tests + /// + /// This lint does not implement detection of module tests (or other feature + /// dependent elements for that matter). To lint the location of mod tests, + /// the lint `items_after_test_module` can be used instead. + /// + /// ### Example + /// + /// ```no_run + /// trait TraitUnordered { + /// const A: bool; + /// const C: bool; + /// const B: bool; + /// + /// type SomeType; + /// + /// fn a(); + /// fn c(); + /// fn b(); + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// trait TraitOrdered { + /// const A: bool; + /// const B: bool; + /// const C: bool; + /// + /// type SomeType; + /// + /// fn a(); + /// fn b(); + /// fn c(); + /// } + /// ``` + /// + /// [cargo-pgo]: https://github.com/Kobzol/cargo-pgo/blob/main/README.md + /// + #[clippy::version = "1.82.0"] + pub ARBITRARY_SOURCE_ITEM_ORDERING, + restriction, + "arbitrary source item ordering" +} + +impl_lint_pass!(ArbitrarySourceItemOrdering => [ARBITRARY_SOURCE_ITEM_ORDERING]); + +#[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] // Bools are cached feature flags. +pub struct ArbitrarySourceItemOrdering { + assoc_types_order: SourceItemOrderingTraitAssocItemKinds, + enable_ordering_for_enum: bool, + enable_ordering_for_impl: bool, + enable_ordering_for_module: bool, + enable_ordering_for_struct: bool, + enable_ordering_for_trait: bool, + module_item_order_groupings: SourceItemOrderingModuleItemGroupings, +} + +impl ArbitrarySourceItemOrdering { + pub fn new(conf: &'static Conf) -> Self { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingCategory::*; + Self { + assoc_types_order: conf.trait_assoc_item_kinds_order.clone(), + enable_ordering_for_enum: conf.source_item_ordering.contains(&Enum), + enable_ordering_for_impl: conf.source_item_ordering.contains(&Impl), + enable_ordering_for_module: conf.source_item_ordering.contains(&Module), + enable_ordering_for_struct: conf.source_item_ordering.contains(&Struct), + enable_ordering_for_trait: conf.source_item_ordering.contains(&Trait), + module_item_order_groupings: conf.module_item_order_groupings.clone(), + } + } + + /// Produces a linting warning for incorrectly ordered impl items. + fn lint_impl_item<T: LintContext>(&self, cx: &T, item: &ImplItemRef, before_item: &ImplItemRef) { + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + item.span, + format!( + "incorrect ordering of impl items (defined order: {:?})", + self.assoc_types_order + ), + Some(before_item.span), + format!("should be placed before `{}`", before_item.ident.as_str(),), + ); + } + + /// Produces a linting warning for incorrectly ordered item members. + fn lint_member_name<T: LintContext>( + cx: &T, + ident: &rustc_span::symbol::Ident, + before_ident: &rustc_span::symbol::Ident, + ) { + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + ident.span, + "incorrect ordering of items (must be alphabetically ordered)", + Some(before_ident.span), + format!("should be placed before `{}`", before_ident.as_str(),), + ); + } + + fn lint_member_item<T: LintContext>(cx: &T, item: &Item<'_>, before_item: &Item<'_>) { + let span = if item.ident.as_str().is_empty() { + &item.span + } else { + &item.ident.span + }; + + let (before_span, note) = if before_item.ident.as_str().is_empty() { + ( + &before_item.span, + "should be placed before the following item".to_owned(), + ) + } else { + ( + &before_item.ident.span, + format!("should be placed before `{}`", before_item.ident.as_str(),), + ) + }; + + // This catches false positives where generated code gets linted. + if span == before_span { + return; + } + + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + *span, + "incorrect ordering of items (must be alphabetically ordered)", + Some(*before_span), + note, + ); + } + + /// Produces a linting warning for incorrectly ordered trait items. + fn lint_trait_item<T: LintContext>(&self, cx: &T, item: &TraitItemRef, before_item: &TraitItemRef) { + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + item.span, + format!( + "incorrect ordering of trait items (defined order: {:?})", + self.assoc_types_order + ), + Some(before_item.span), + format!("should be placed before `{}`", before_item.ident.as_str(),), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + match &item.kind { + ItemKind::Enum(enum_def, _generics) if self.enable_ordering_for_enum => { + let mut cur_v: Option<&Variant<'_>> = None; + for variant in enum_def.variants { + if in_external_macro(cx.sess(), variant.span) { + continue; + } + + if let Some(cur_v) = cur_v { + if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span { + Self::lint_member_name(cx, &variant.ident, &cur_v.ident); + } + } + cur_v = Some(variant); + } + }, + ItemKind::Struct(VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => { + let mut cur_f: Option<&FieldDef<'_>> = None; + for field in *fields { + if in_external_macro(cx.sess(), field.span) { + continue; + } + + if let Some(cur_f) = cur_f { + if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span { + Self::lint_member_name(cx, &field.ident, &cur_f.ident); + } + } + cur_f = Some(field); + } + }, + ItemKind::Trait(is_auto, _safety, _generics, _generic_bounds, item_ref) + if self.enable_ordering_for_trait && *is_auto == IsAuto::No => + { + let mut cur_t: Option<&TraitItemRef> = None; + + for item in *item_ref { + if in_external_macro(cx.sess(), item.span) { + continue; + } + + if let Some(cur_t) = cur_t { + let cur_t_kind = convert_assoc_item_kind(cur_t.kind); + let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind); + let item_kind = convert_assoc_item_kind(item.kind); + let item_kind_index = self.assoc_types_order.index_of(&item_kind); + + if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() { + Self::lint_member_name(cx, &item.ident, &cur_t.ident); + } else if cur_t_kind_index > item_kind_index { + self.lint_trait_item(cx, item, cur_t); + } + } + cur_t = Some(item); + } + }, + ItemKind::Impl(trait_impl) if self.enable_ordering_for_impl => { + let mut cur_t: Option<&ImplItemRef> = None; + + for item in trait_impl.items { + if in_external_macro(cx.sess(), item.span) { + continue; + } + + if let Some(cur_t) = cur_t { + let cur_t_kind = convert_assoc_item_kind(cur_t.kind); + let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind); + let item_kind = convert_assoc_item_kind(item.kind); + let item_kind_index = self.assoc_types_order.index_of(&item_kind); + + if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() { + Self::lint_member_name(cx, &item.ident, &cur_t.ident); + } else if cur_t_kind_index > item_kind_index { + self.lint_impl_item(cx, item, cur_t); + } + } + cur_t = Some(item); + } + }, + _ => {}, // Catch-all for `ItemKinds` that don't have fields. + } + } + + fn check_mod(&mut self, cx: &LateContext<'tcx>, module: &'tcx Mod<'tcx>, _: HirId) { + struct CurItem<'a> { + item: &'a Item<'a>, + order: usize, + name: String, + } + let mut cur_t: Option<CurItem<'_>> = None; + + if !self.enable_ordering_for_module { + return; + } + + let items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id)); + + // Iterates over the items within a module. + // + // As of 2023-05-09, the Rust compiler will hold the entries in the same + // order as they appear in the source code, which is convenient for us, + // as no sorting by source map/line of code has to be applied. + // + for item in items { + if in_external_macro(cx.sess(), item.span) { + continue; + } + + // The following exceptions (skipping with `continue;`) may not be + // complete, edge cases have not been explored further than what + // appears in the existing code base. + if item.ident.name == rustc_span::symbol::kw::Empty { + if let ItemKind::Impl(_) = item.kind { + // Sorting trait impls for unnamed types makes no sense. + if get_item_name(item).is_empty() { + continue; + } + } else if let ItemKind::ForeignMod { .. } = item.kind { + continue; + } else if let ItemKind::GlobalAsm(_) = item.kind { + continue; + } else if let ItemKind::Use(path, use_kind) = item.kind { + if path.segments.is_empty() { + // Use statements that contain braces get caught here. + // They will still be linted internally. + continue; + } else if path.segments.len() >= 2 + && (path.segments[0].ident.name == rustc_span::sym::std + || path.segments[0].ident.name == rustc_span::sym::core) + && path.segments[1].ident.name == rustc_span::sym::prelude + { + // Filters the autogenerated prelude use statement. + // e.g. `use std::prelude::rustc_2021` + } else if use_kind == UseKind::Glob { + // Filters glob kinds of uses. + // e.g. `use std::sync::*` + } else { + // This can be used for debugging. + // println!("Unknown autogenerated use statement: {:?}", item); + } + continue; + } + } + + if item.ident.name.as_str().starts_with('_') { + // Filters out unnamed macro-like impls for various derives, + // e.g. serde::Serialize or num_derive::FromPrimitive. + continue; + } + + if item.ident.name == rustc_span::sym::std && item.span.is_dummy() { + if let ItemKind::ExternCrate(None) = item.kind { + // Filters the auto-included Rust standard library. + continue; + } + println!("Unknown item: {item:?}"); + } + + let item_kind = convert_module_item_kind(&item.kind); + let module_level_order = self + .module_item_order_groupings + .module_level_order_of(&item_kind) + .unwrap_or_default(); + + if let Some(cur_t) = cur_t.as_ref() { + use std::cmp::Ordering; // Better legibility. + match module_level_order.cmp(&cur_t.order) { + Ordering::Less => { + Self::lint_member_item(cx, item, cur_t.item); + }, + Ordering::Equal if item_kind == SourceItemOrderingModuleItemKind::Use => { + // Skip ordering use statements, as these should be ordered by rustfmt. + }, + Ordering::Equal if cur_t.name > get_item_name(item) => { + Self::lint_member_item(cx, item, cur_t.item); + }, + Ordering::Equal | Ordering::Greater => { + // Nothing to do in this case, they're already in the right order. + }, + } + } + + // Makes a note of the current item for comparison with the next. + cur_t = Some(CurItem { + order: module_level_order, + item, + name: get_item_name(item), + }); + } + } +} + +/// Converts a [`rustc_hir::AssocItemKind`] to a +/// [`SourceItemOrderingTraitAssocItemKind`]. +/// +/// This is implemented here because `rustc_hir` is not a dependency of +/// `clippy_config`. +fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssocItemKind { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingTraitAssocItemKind::*; + match value { + AssocItemKind::Const { .. } => Const, + AssocItemKind::Type { .. } => Type, + AssocItemKind::Fn { .. } => Fn, + } +} + +/// Converts a [`rustc_hir::ItemKind`] to a +/// [`SourceItemOrderingModuleItemKind`]. +/// +/// This is implemented here because `rustc_hir` is not a dependency of +/// `clippy_config`. +fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleItemKind { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingModuleItemKind::*; + match value { + ItemKind::ExternCrate(..) => ExternCrate, + ItemKind::Use(..) => Use, + ItemKind::Static(..) => Static, + ItemKind::Const(..) => Const, + ItemKind::Fn(..) => Fn, + ItemKind::Macro(..) => Macro, + ItemKind::Mod(..) => Mod, + ItemKind::ForeignMod { .. } => ForeignMod, + ItemKind::GlobalAsm(..) => GlobalAsm, + ItemKind::TyAlias(..) => TyAlias, + ItemKind::Enum(..) => Enum, + ItemKind::Struct(..) => Struct, + ItemKind::Union(..) => Union, + ItemKind::Trait(..) => Trait, + ItemKind::TraitAlias(..) => TraitAlias, + ItemKind::Impl(..) => Impl, + } +} + +/// Gets the item name for sorting purposes, which in the general case is +/// `item.ident.name`. +/// +/// For trait impls, the name used for sorting will be the written path of +/// `item.self_ty` plus the written path of `item.of_trait`, joined with +/// exclamation marks. Exclamation marks are used because they are the first +/// printable ASCII character. +/// +/// Trait impls generated using a derive-macro will have their path rewritten, +/// such that for example `Default` is `$crate::default::Default`, and +/// `std::clone::Clone` is `$crate::clone::Clone`. This behaviour is described +/// further in the [Rust Reference, Paths Chapter][rust_ref]. +/// +/// [rust_ref]: https://doc.rust-lang.org/reference/paths.html#crate-1 +fn get_item_name(item: &Item<'_>) -> String { + match item.kind { + ItemKind::Impl(im) => { + if let TyKind::Path(path) = im.self_ty.kind { + match path { + QPath::Resolved(_, path) => { + let segs = path.segments.iter(); + let mut segs: Vec<String> = segs.map(|s| s.ident.name.as_str().to_owned()).collect(); + + if let Some(of_trait) = im.of_trait { + let mut trait_segs: Vec<String> = of_trait + .path + .segments + .iter() + .map(|s| s.ident.name.as_str().to_owned()) + .collect(); + segs.append(&mut trait_segs); + } + + segs.push(String::new()); + segs.join("!!") + }, + QPath::TypeRelative(_, _path_seg) => { + // This case doesn't exist in the clippy tests codebase. + String::new() + }, + QPath::LangItem(_, _) => String::new(), + } + } else { + // Impls for anything that isn't a named type can be skipped. + String::new() + } + }, + _ => item.ident.name.as_str().to_owned(), + } +} diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs index a5a7b9f74a6..1879391ec29 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs @@ -3,11 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; // Separate each crate's features. -pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) { +pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) { if !in_external_macro(cx.sess(), attr.span) && let AttrStyle::Outer = attr.style && let Some(ident) = attr.ident() diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 5d4e864b9b0..788377fe83c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -2,12 +2,12 @@ use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{MetaItemInner, MetaItemKind}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::sym; use rustc_span::symbol::Symbol; -pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) { +pub(super) fn check<'cx>(cx: &EarlyContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) { // Check if the reason is present if let Some(item) = items.last().and_then(MetaItemInner::meta_item) && let MetaItemKind::NameValue(_) = &item.kind diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 0baf889faa0..fecf3166406 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -2,11 +2,11 @@ use super::BLANKET_CLIPPY_RESTRICTION_LINTS; use super::utils::extract_clippy_lint; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use rustc_ast::MetaItemInner; -use rustc_lint::{LateContext, Level, LintContext}; +use rustc_lint::{EarlyContext, Level, LintContext}; use rustc_span::symbol::Symbol; use rustc_span::{DUMMY_SP, sym}; -pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner]) { +pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { if let Some(lint_name) = extract_clippy_lint(lint) { if lint_name.as_str() == "restriction" && name != sym::allow { @@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner]) } } -pub(super) fn check_command_line(cx: &LateContext<'_>) { +pub(super) fn check_command_line(cx: &EarlyContext<'_>) { for (name, level) in &cx.sess().opts.lint_opts { if name == "clippy::restriction" && *level > Level::Allow { span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs index 1898c145c76..d3153ec6613 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs @@ -1,11 +1,11 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; use rustc_ast::{LitKind, MetaItemLit}; -use rustc_lint::LateContext; +use rustc_lint::EarlyContext; use rustc_span::Span; use semver::Version; -pub(super) fn check(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) { +pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind { if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() { return; diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 55f8e1072db..2ddbc7a6a76 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -2,12 +2,12 @@ use super::DUPLICATED_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::{Attribute, MetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_lint::LateContext; +use rustc_lint::EarlyContext; use rustc_span::{Span, sym}; use std::collections::hash_map::Entry; fn emit_if_duplicated( - cx: &LateContext<'_>, + cx: &EarlyContext<'_>, attr: &MetaItem, attr_paths: &mut FxHashMap<String, Span>, complete_path: String, @@ -26,7 +26,7 @@ fn emit_if_duplicated( } fn check_duplicated_attr( - cx: &LateContext<'_>, + cx: &EarlyContext<'_>, attr: &MetaItem, attr_paths: &mut FxHashMap<String, Span>, parent: &mut Vec<String>, @@ -65,7 +65,7 @@ fn check_duplicated_attr( } } -pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { +pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attr_paths = FxHashMap::default(); for attr in attrs { diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 5d2ea36b366..32c28c09c36 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::{AttrKind, AttrStyle, Attribute}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_span::source_map::SourceMap; use rustc_span::{SourceFile, Span, Symbol}; @@ -32,7 +32,7 @@ impl From<&AttrKind> for SimpleAttrKind { } } -pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) { +pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) { let mut inner_attr_kind: FxHashSet<SimpleAttrKind> = FxHashSet::default(); let mut outer_attr_kind: FxHashSet<SimpleAttrKind> = FxHashSet::default(); @@ -64,7 +64,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) } } -fn lint_mixed_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) { +fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion()); let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) { first.span.with_hi(last.span.hi()) diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 1a34ca99fc2..c29c9f08cf7 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -14,8 +14,8 @@ mod utils; use clippy_config::Conf; use clippy_config::msrvs::{self, Msrv}; -use rustc_ast::{Attribute, MetaItemInner, MetaItemKind}; -use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; +use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind}; +use rustc_hir::{ImplItem, Item, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -414,15 +414,7 @@ pub struct Attributes { } impl_lint_pass!(Attributes => [ - ALLOW_ATTRIBUTES, - ALLOW_ATTRIBUTES_WITHOUT_REASON, INLINE_ALWAYS, - DEPRECATED_SEMVER, - USELESS_ATTRIBUTE, - BLANKET_CLIPPY_RESTRICTION_LINTS, - SHOULD_PANIC_WITHOUT_EXPECT, - MIXED_ATTRIBUTES_STYLE, - DUPLICATED_ATTRIBUTES, ]); impl Attributes { @@ -434,53 +426,11 @@ impl Attributes { } impl<'tcx> LateLintPass<'tcx> for Attributes { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - blanket_clippy_restriction_lints::check_command_line(cx); - duplicated_attributes::check(cx, cx.tcx.hir().krate_attrs()); - } - - fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { - if let Some(items) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - if is_lint_level(ident.name, attr.id) { - blanket_clippy_restriction_lints::check(cx, ident.name, items); - } - if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { - allow_attributes::check(cx, attr); - } - if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) - { - allow_attributes_without_reason::check(cx, ident.name, items, attr); - } - if items.is_empty() || !attr.has_name(sym::deprecated) { - return; - } - for item in items { - if let MetaItemInner::MetaItem(mi) = &item - && let MetaItemKind::NameValue(lit) = &mi.kind - && mi.has_name(sym::since) - { - deprecated_semver::check(cx, item.span(), lit); - } - } - } - } - if attr.has_name(sym::should_panic) { - should_panic_without_expect::check(cx, attr); - } - } - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); if is_relevant_item(cx, item) { inline_always::check(cx, item.span, item.ident.name, attrs); } - match item.kind { - ItemKind::ExternCrate(..) | ItemKind::Use(..) => useless_attribute::check(cx, item, attrs), - _ => {}, - } - mixed_attributes_style::check(cx, item.span, attrs); - duplicated_attributes::check(cx, attrs); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { @@ -526,3 +476,77 @@ impl EarlyLintPass for EarlyAttributes { extract_msrv_attr!(EarlyContext); } + +pub struct PostExpansionEarlyAttributes { + msrv: Msrv, +} + +impl PostExpansionEarlyAttributes { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(PostExpansionEarlyAttributes => [ + ALLOW_ATTRIBUTES, + ALLOW_ATTRIBUTES_WITHOUT_REASON, + DEPRECATED_SEMVER, + USELESS_ATTRIBUTE, + BLANKET_CLIPPY_RESTRICTION_LINTS, + SHOULD_PANIC_WITHOUT_EXPECT, + MIXED_ATTRIBUTES_STYLE, + DUPLICATED_ATTRIBUTES, +]); + +impl EarlyLintPass for PostExpansionEarlyAttributes { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { + blanket_clippy_restriction_lints::check_command_line(cx); + duplicated_attributes::check(cx, &krate.attrs); + } + + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + if let Some(items) = &attr.meta_item_list() { + if let Some(ident) = attr.ident() { + if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes::check(cx, attr); + } + if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) + { + allow_attributes_without_reason::check(cx, ident.name, items, attr); + } + if is_lint_level(ident.name, attr.id) { + blanket_clippy_restriction_lints::check(cx, ident.name, items); + } + if items.is_empty() || !attr.has_name(sym::deprecated) { + return; + } + for item in items { + if let MetaItemInner::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) + { + deprecated_semver::check(cx, item.span(), lit); + } + } + } + } + + if attr.has_name(sym::should_panic) { + should_panic_without_expect::check(cx, attr); + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) { + match item.kind { + ast::ItemKind::ExternCrate(..) | ast::ItemKind::Use(..) => useless_attribute::check(cx, item, &item.attrs), + _ => {}, + } + + mixed_attributes_style::check(cx, item.span, &item.attrs); + duplicated_attributes::check(cx, &item.attrs); + } + + extract_msrv_attr!(EarlyContext); +} diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs index 2d45cbbf621..fadd5272880 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -4,12 +4,12 @@ use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind}; use rustc_errors::Applicability; -use rustc_lint::LateContext; +use rustc_lint::EarlyContext; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, attr: &Attribute) { +pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { if let AttrKind::Normal(normal_attr) = &attr.kind { - if let AttrArgs::Eq(_, AttrArgsEq::Hir(_)) = &normal_attr.item.args { + if let AttrArgs::Eq(_, AttrArgsEq::Ast(_)) = &normal_attr.item.args { // `#[should_panic = ".."]` found, good return; } diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 72e6ce59d59..dc5a6857089 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -1,15 +1,14 @@ +use super::USELESS_ATTRIBUTE; use super::utils::{extract_clippy_lint, is_lint_level, is_word}; -use super::{Attribute, USELESS_ATTRIBUTE}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, first_line_of_span}; -use rustc_ast::MetaItemInner; +use rustc_ast::{Attribute, Item, ItemKind, MetaItemInner}; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) { +pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use)); for attr in attrs { diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 9952d0af99e..6948cf560a5 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -15,7 +15,7 @@ declare_clippy_lint! { /// `MutexGuard`. /// /// ### Why is this bad? - /// The Mutex types found in [`std::sync`][https://doc.rust-lang.org/stable/std/sync/] and + /// The Mutex types found in [`std::sync`](https://doc.rust-lang.org/stable/std/sync/) and /// [`parking_lot`](https://docs.rs/parking_lot/latest/parking_lot/) are /// not designed to operate in an async context across await points. /// diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 26888f7e3a0..26a20bc99a0 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -203,6 +203,21 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) { && let Some(suggestion) = simplify_not(cx, msrv, inner) && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow { + use clippy_utils::sugg::{Sugg, has_enclosing_paren}; + let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) { + match sug { + Sugg::BinOp(..) => true, + Sugg::MaybeParen(sug) if !has_enclosing_paren(&sug) => true, + _ => false, + } + } else { + false + }; + let suggestion = if maybe_par { + format!("({suggestion})") + } else { + suggestion + }; span_lint_and_sugg( cx, NONMINIMAL_BOOL, diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index cba8224b84c..f2551a05b1a 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -4,7 +4,7 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, UnOp}; +use rustc_hir::{BorrowKind, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; @@ -49,7 +49,7 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { - if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, addrof_target) = e.kind && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) && !e.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index b11b967f8bc..205357afd84 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind { - if method_path.ident.name == sym!(cast) + if method_path.ident.name.as_str() == "cast" && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 811d33c8bde..abd80abffe6 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -268,8 +268,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx if !snippet .split("->") .skip(1) - .map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) - .any(|a| a) + .any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) { return ControlFlow::Break(()); } diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 2e7f91a842e..f76e399517c 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -232,7 +232,7 @@ fn get_types_from_cast<'a>( // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind // to_type::max_value(), from_type - && let TyKind::Path(ref from_type_path) = &from_type.kind + && let TyKind::Path(from_type_path) = &from_type.kind && let Some(from_sym) = int_ty_to_sym(from_type_path) { Some((limit, from_sym)) @@ -245,7 +245,7 @@ fn get_types_from_cast<'a>( if let ExprKind::Call(from_func, [limit]) = &expr.kind // `from_type::from, to_type::max_value()` // `from_type::from` - && let ExprKind::Path(ref path) = &from_func.kind + && let ExprKind::Path(path) = &from_func.kind && let Some(from_sym) = get_implementing_type(path, INTS, "from") { Some((limit, from_sym)) diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 477435236a5..383fae7992b 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery, - "functions that should be split up into multiple functions" + "functions that should be split up into multiple functions", @eval_always = true } diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index d90a22bb62a..c4afdc757d8 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -540,24 +540,22 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .iter() .filter(|&&(_, name)| !name.as_str().starts_with('_')) .any(|&(_, name)| { - let mut walker = ContainsName { - name, - result: false, - cx, - }; + let mut walker = ContainsName { name, cx }; // Scan block - block + let mut res = block .stmts .iter() .filter(|stmt| !ignore_span.overlaps(stmt.span)) - .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); + .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); if let Some(expr) = block.expr { - intravisit::walk_expr(&mut walker, expr); + if res.is_continue() { + res = intravisit::walk_expr(&mut walker, expr); + } } - walker.result + res.is_break() }) }) } diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs index 50fd76a3a47..4ecf3e41611 100644 --- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs @@ -37,7 +37,7 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]); impl<'tcx> LateLintPass<'tcx> for CopyIterator { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), .. }) = item.kind && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs index 2fe37a64db6..589b99518a0 100644 --- a/src/tools/clippy/clippy_lints/src/ctfe.rs +++ b/src/tools/clippy/clippy_lints/src/ctfe.rs @@ -1,29 +1,15 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; -use rustc_lint::Level::Deny; -use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::Span; -/// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes). -/// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran. -pub static CLIPPY_CTFE: &Lint = &Lint { - name: &"clippy::CLIPPY_CTFE", - default_level: Deny, - desc: "Ensure CTFE is being made", - edition_lint_opts: None, - report_in_external_macro: true, - future_incompatible: None, - is_externally_loaded: true, - crate_level_only: false, - eval_always: true, - ..Lint::default_fields_for_macro() -}; - -// No static CLIPPY_CTFE_INFO because we want this lint to be invisible - -declare_lint_pass! { ClippyCtfe => [CLIPPY_CTFE] } +declare_lint_pass! { + /// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes). + /// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran. + ClippyCtfe => [] +} impl<'tcx> LateLintPass<'tcx> for ClippyCtfe { fn check_fn( diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs index a785a9d377c..4d908af4084 100644 --- a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs +++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs @@ -4,7 +4,7 @@ macro_rules! declare_clippy_lint { (@ $(#[doc = $lit:literal])* pub $lint_name:ident, - $category:ident, + $level:ident, $lintcategory:expr, $desc:literal, $version_expr:expr, @@ -15,7 +15,7 @@ macro_rules! declare_clippy_lint { $(#[doc = $lit])* #[clippy::version = $version_lit] pub clippy::$lint_name, - $category, + $level, $desc, report_in_external_macro:true $(, @eval_always = $eval_always)? @@ -35,12 +35,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, restriction, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Restriction, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; ( @@ -49,12 +50,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, style, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Style, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; ( @@ -63,12 +65,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, correctness, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Deny, crate::LintCategory::Correctness, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; @@ -78,12 +81,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, perf, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Perf, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; ( @@ -92,12 +96,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, complexity, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Complexity, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; ( @@ -106,12 +111,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, suspicious, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; ( @@ -120,12 +126,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, nursery, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Nursery, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; ( @@ -134,12 +141,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, pedantic, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; ( @@ -148,12 +156,13 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, cargo, $desc:literal - $(@eval_always = $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Cargo, $desc, - Some($version), $version $(, $eval_always)? + Some($version), $version + $(, $eval_always)? } }; diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 3c4e75df8ab..edb52851e0c 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -28,12 +28,15 @@ pub static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, #[cfg(feature = "internal")] + crate::utils::internal_lints::slow_symbol_comparisons::SLOW_SYMBOL_COMPARISONS_INFO, + #[cfg(feature = "internal")] crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO, crate::absolute_paths::ABSOLUTE_PATHS_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, + crate::arbitrary_source_item_ordering::ARBITRARY_SOURCE_ITEM_ORDERING_INFO, crate::arc_with_non_send_sync::ARC_WITH_NON_SEND_SYNC_INFO, crate::as_conversions::AS_CONVERSIONS_INFO, crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO, @@ -414,14 +417,17 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::MANUAL_SPLIT_ONCE_INFO, crate::methods::MANUAL_STR_REPEAT_INFO, crate::methods::MANUAL_TRY_FOLD_INFO, + crate::methods::MAP_ALL_ANY_IDENTITY_INFO, crate::methods::MAP_CLONE_INFO, crate::methods::MAP_COLLECT_RESULT_UNIT_INFO, crate::methods::MAP_ERR_IGNORE_INFO, crate::methods::MAP_FLATTEN_INFO, crate::methods::MAP_IDENTITY_INFO, crate::methods::MAP_UNWRAP_OR_INFO, + crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES_INFO, crate::methods::MUT_MUTEX_LOCK_INFO, crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_AS_BYTES_INFO, crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO, crate::methods::NEEDLESS_COLLECT_INFO, crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 2920bbb4c81..2b6bfafe695 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -187,7 +187,7 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items: [child], self_ty, .. diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index e8e21edd494..e569c4dc786 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; @@ -202,7 +204,7 @@ declare_lint_pass!(Derive => [ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), .. }) = item.kind { @@ -371,9 +373,8 @@ fn check_unsafe_derive_deserialize<'tcx>( ty: Ty<'tcx>, ) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { - let mut visitor = UnsafeVisitor { cx, has_unsafe: false }; - walk_item(&mut visitor, item); - visitor.has_unsafe + let mut visitor = UnsafeVisitor { cx }; + walk_item(&mut visitor, item).is_break() } if let Some(trait_def_id) = trait_ref.trait_def_id() @@ -406,38 +407,37 @@ fn check_unsafe_derive_deserialize<'tcx>( struct UnsafeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - has_unsafe: bool, } impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::All; - fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: LocalDefId) { - if self.has_unsafe { - return; - } - + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body_id: BodyId, + _: Span, + id: LocalDefId, + ) -> Self::Result { if let Some(header) = kind.header() && header.safety == Safety::Unsafe { - self.has_unsafe = true; + ControlFlow::Break(()) + } else { + walk_fn(self, kind, decl, body_id, id) } - - walk_fn(self, kind, decl, body_id, id); } - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.has_unsafe { - return; - } - + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { if let ExprKind::Block(block, _) = expr.kind { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { - self.has_unsafe = true; + return ControlFlow::Break(()); } } - walk_expr(self, expr); + walk_expr(self, expr) } fn nested_visit_map(&mut self) -> Self::Map { diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index b51d343132b..bdd49bf8aa7 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -2,7 +2,6 @@ use clippy_config::Conf; use clippy_utils::create_disallowed_map; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; -use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Diag; use rustc_hir::def_id::DefIdMap; @@ -14,6 +13,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::{ExpnId, MacroKind, Span}; +use crate::utils::attr_collector::AttrStorage; + declare_clippy_lint! { /// ### What it does /// Denies the configured macros in clippy.toml @@ -64,14 +65,19 @@ pub struct DisallowedMacros { // Track the most recently seen node that can have a `derive` attribute. // Needed to use the correct lint level. derive_src: Option<OwnerId>, + + // When a macro is disallowed in an early pass, it's stored + // and emitted during the late pass. This happens for attributes. + earlies: AttrStorage, } impl DisallowedMacros { - pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self { Self { disallowed: create_disallowed_map(tcx, &conf.disallowed_macros), seen: FxHashSet::default(), derive_src: None, + earlies, } } @@ -114,6 +120,15 @@ impl DisallowedMacros { impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); impl LateLintPass<'_> for DisallowedMacros { + fn check_crate(&mut self, cx: &LateContext<'_>) { + // once we check a crate in the late pass we can emit the early pass lints + if let Some(attr_spans) = self.earlies.clone().0.get() { + for span in attr_spans { + self.check(cx, *span, None); + } + } + } + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { self.check(cx, expr.span, None); // `$t + $t` can have the context of $t, check also the span of the binary operator @@ -164,8 +179,4 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { self.check(cx, path.span, None); } - - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { - self.check(cx, attr.span, self.derive_src); - } } 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 f79264e6b04..53c24a3faf1 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -80,7 +80,7 @@ impl EarlyLintPass for DisallowedScriptIdents { let mut symbols: Vec<_> = symbols.iter().collect(); symbols.sort_unstable_by_key(|k| k.1); - for (symbol, &span) in &symbols { + for &(symbol, &span) in &symbols { // Note: `symbol.as_str()` is an expensive operation, thus should not be called // more than once for a single symbol. let symbol_str = symbol.as_str(); diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 89c6a4e08dc..df7c37a192a 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -427,11 +427,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks if the first line in the documentation of items listed in module page is too long. + /// Checks if the first paragraph in the documentation of items listed in the module page is too long. /// /// ### Why is this bad? - /// Documentation will show the first paragraph of the doscstring in the summary page of a - /// module, so having a nice, short summary in the first paragraph is part of writing good docs. + /// Documentation will show the first paragraph of the docstring in the summary page of a + /// module. Having a nice, short summary in the first paragraph is part of writing good docs. /// /// ### Example /// ```no_run @@ -453,7 +453,7 @@ declare_clippy_lint! { #[clippy::version = "1.82.0"] pub TOO_LONG_FIRST_DOC_PARAGRAPH, nursery, - "ensure that the first line of a documentation paragraph isn't too long" + "ensure the first documentation paragraph is short" } declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs index b66dd2108fc..10a84b1b2ff 100644 --- a/src/tools/clippy/clippy_lints/src/empty_drop.rs +++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs @@ -36,7 +36,7 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); impl LateLintPass<'_> for EmptyDrop { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items: [child], .. }) = item.kind diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 6ca599ed361..de10b7bf533 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, - TypeckResults, + self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -204,11 +203,16 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - match cx.tcx.infer_ctxt().build(cx.typing_mode()).err_ctxt().type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { + match cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { // Mutable closure is used after current expr; we cannot consume it. Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 4e4434ec7d1..0550c22761a 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { // match call to write_fmt && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) && let ExprKind::Call(write_recv_path, []) = write_recv.kind - && write_fun.ident.name == sym!(write_fmt) + && write_fun.ident.name.as_str() == "write_fmt" && let Some(def_id) = path_def_id(cx, write_recv_path) { // match calls to std::io::stdout() / std::io::stderr () diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index d5a2e06863d..14da0b515a5 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use clippy_config::Conf; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; @@ -115,25 +117,26 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { } /// Finds the occurrences of `Self` and `self` +/// +/// Returns `ControlFlow::break` if any of the `self`/`Self` usages were from an expansion, or the +/// body contained a binding already named `val`. struct SelfFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// Occurrences of `Self` upper: Vec<Span>, /// Occurrences of `self` lower: Vec<Span>, - /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding - /// already named `val` - invalid: bool, } impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) -> Self::Result { for segment in path.segments { match segment.ident.name { kw::SelfLower => self.lower.push(segment.ident.span), @@ -141,17 +144,19 @@ impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> { _ => continue, } - self.invalid |= segment.ident.span.from_expansion(); + if segment.ident.span.from_expansion() { + return ControlFlow::Break(()); + } } - if !self.invalid { - walk_path(self, path); - } + walk_path(self, path) } - fn visit_name(&mut self, name: Symbol) { + fn visit_name(&mut self, name: Symbol) -> Self::Result { if name == sym::val { - self.invalid = true; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } } @@ -209,11 +214,9 @@ fn convert_to_from( cx, upper: Vec::new(), lower: Vec::new(), - invalid: false, }; - finder.visit_expr(body.value); - if finder.invalid { + if finder.visit_expr(body.value).is_break() { return None; } diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs index d62d008d480..c8828c93615 100644 --- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs @@ -41,7 +41,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind - && seg.ident.name == sym!(from_raw) + && seg.ident.name.as_str() == "from_raw" && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() && let ty::RawPtr(ty, _) = arg_kind diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 91d73c2a9c9..be3d0f7ad63 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -441,7 +441,7 @@ declare_clippy_lint! { /// fn bar(&self) -> Option<&String> { None } /// # } /// ``` - #[clippy::version = "1.82.0"] + #[clippy::version = "1.83.0"] pub REF_OPTION, pedantic, "function signature uses `&Option<T>` instead of `Option<&T>`" diff --git a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs index 373ecd74cb3..aba0fbcb9fe 100644 --- a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs +++ b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs @@ -15,9 +15,9 @@ use rustc_span::{Span, sym}; fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() && is_type_diagnostic_item(cx, *opt_ty, sym::Option) - && let ty::Adt(_, opt_gen) = opt_ty.kind() - && let [gen] = opt_gen.as_slice() - && let GenericArgKind::Type(gen_ty) = gen.unpack() + && let ty::Adt(_, opt_gen_args) = opt_ty.kind() + && let [gen_arg] = opt_gen_args.as_slice() + && let GenericArgKind::Type(gen_ty) = gen_arg.unpack() && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 4c5375730b8..c370f206c8f 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -340,7 +340,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { if method.ident.name == sym::new { self.suggestions.insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { + } else if method.ident.name.as_str() == "with_capacity" { self.suggestions.insert( e.span, format!( @@ -352,7 +352,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { if method.ident.name == sym::new { self.suggestions.insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { + } else if method.ident.name.as_str() == "with_capacity" { self.suggestions.insert( e.span, format!( diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index 71c317552ee..48874d6064b 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -157,7 +157,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name == sym!(flat_map) && args.len() == 1 { + if method.ident.name.as_str() == "flat_map" && args.len() == 1 { if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { let body = cx.tcx.hir().body(body); return is_infinite(cx, body.value); @@ -224,7 +224,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { return MaybeInfinite.and(is_infinite(cx, receiver)); } } - if method.ident.name == sym!(last) && args.is_empty() { + if method.ident.name.as_str() == "last" && args.is_empty() { let not_double_ended = cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) @@ -234,7 +234,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { if not_double_ended { return is_infinite(cx, receiver); } - } else if method.ident.name == sym!(collect) { + } else if method.ident.name.as_str() == "collect" { let ty = cx.typeck_results().expr_ty(expr); if matches!( get_type_diagnostic_name(cx, ty), diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 1e6404190d3..314d0dfa26c 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -142,7 +142,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter { ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some() }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { - if item.ident.name == sym!(IntoIter) { + if item.ident.name.as_str() == "IntoIter" { Some(cx.tcx.hir().impl_item(item.id).expect_type().span) } else { None @@ -247,8 +247,8 @@ impl {self_ty_without_ref} {{ let sugg = format!( " impl IntoIterator for {self_ty_snippet} {{ - type IntoIter = {ret_ty}; type Item = {iter_ty}; + type IntoIter = {ret_ty}; fn into_iter(self) -> Self::IntoIter {{ self.iter() }} diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index 5d2b521b250..aa8b4d88d2f 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, ConstKind}; +use rustc_middle::ty::{self, ParamEnv}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && !item.span.from_expansion() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Array(element_type, cst) = ty.kind() - && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() + && let Ok((_, ty::ValTree::Leaf(element_count))) = cst.eval_valtree(cx.tcx, ParamEnv::empty(), item.span) && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index f2f841dcec3..ab3d19f89c3 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -1,11 +1,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; -use rustc_ast::LitKind; +use clippy_utils::source::snippet_opt; +use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -51,6 +52,24 @@ impl LargeIncludeFile { impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); +impl LargeIncludeFile { + fn emit_lint(&self, cx: &LateContext<'_>, span: Span) { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + LARGE_INCLUDE_FILE, + span, + "attempted to include a large file", + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, + ); + } +} + impl LateLintPass<'_> for LargeIncludeFile { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Lit(lit) = &expr.kind @@ -66,19 +85,33 @@ impl LateLintPass<'_> for LargeIncludeFile { && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - LARGE_INCLUDE_FILE, - expr.span.source_callsite(), - "attempted to include a large file", - |diag| { - diag.note(format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - )); - }, - ); + self.emit_lint(cx, expr.span.source_callsite()); + } + } + + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { + if !attr.span.from_expansion() + // Currently, rustc limits the usage of macro at the top-level of attributes, + // so we don't need to recurse into each level. + && let AttrKind::Normal(ref normal) = attr.kind + && let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args + && !attr.span.contains(meta.span) + // Since the `include_str` is already expanded at this point, we can only take the + // whole attribute snippet and then modify for our suggestion. + && let Some(snippet) = snippet_opt(cx, attr.span) + // We cannot remove this because a `#[doc = include_str!("...")]` attribute can + // occupy several lines. + && let Some(start) = snippet.find('[') + && let Some(end) = snippet.rfind(']') + && let snippet = &snippet[start + 1..end] + // We check that the expansion actually comes from `include_str!` and not just from + // another macro. + && let Some(sub_snippet) = snippet.trim().strip_prefix("doc") + && let Some(sub_snippet) = sub_snippet.trim().strip_prefix("=") + && let sub_snippet = sub_snippet.trim() + && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!")) + { + self.emit_lint(cx, attr.span); } } } diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 47c65ee6d0b..b7887ef76a5 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -629,7 +629,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }), - ty::Alias(ty::Projection, ref proj) => has_is_empty_impl(cx, proj.def_id), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), ty::Array(..) | ty::Slice(..) | ty::Str => true, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 14110539709..c9064df25ac 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -73,6 +73,7 @@ pub mod deprecated_lints; mod absolute_paths; mod almost_complete_range; mod approx_const; +mod arbitrary_source_item_ordering; mod arc_with_non_send_sync; mod as_conversions; mod asm_syntax; @@ -400,6 +401,7 @@ use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; +use utils::attr_collector::{AttrCollector, AttrStorage}; /// Register all pre expansion lints /// @@ -412,6 +414,8 @@ use rustc_lint::{Lint, LintId}; pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); + + store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); } #[derive(Default)] @@ -462,6 +466,7 @@ pub(crate) enum LintCategory { #[cfg(feature = "internal")] Internal, } + #[allow(clippy::enum_glob_use)] use LintCategory::*; @@ -583,6 +588,10 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { )) }); + let attr_storage = AttrStorage::default(); + let attrs = attr_storage.clone(); + store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone()))); + // all the internal lints #[cfg(feature = "internal")] { @@ -604,6 +613,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| { Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new()) }); + store.register_late_pass(|_| Box::new(utils::internal_lints::slow_symbol_comparisons::SlowSymbolComparisons)); } store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe)); @@ -793,7 +803,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); - store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf))); + let attrs = attr_storage.clone(); + store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()))); store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); @@ -951,5 +962,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); + store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 119e410b91c..ee561ea85ed 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -29,11 +29,7 @@ pub(super) fn check( if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { return; } - } else if count - .try_to_target_usize(cx.tcx) - .map_or(true, |x| x > 32) - && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) - { + } else if count.try_to_target_usize(cx.tcx).map_or(true, |x| x > 32) && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) { return; } } diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index e25c03db534..9f543b79bac 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed}; use hir::intravisit::{Visitor, walk_expr}; -use hir::{Expr, ExprKind, FnRetTy, FnSig, Node}; +use hir::{Expr, ExprKind, FnRetTy, FnSig, Node, TyKind}; use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_span::sym; use super::INFINITE_LOOP; @@ -25,13 +26,7 @@ pub(super) fn check<'tcx>( return; }; // Or, its parent function is already returning `Never` - if matches!( - parent_fn_ret, - FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Never, - .. - }) - ) { + if is_never_return(parent_fn_ret) { return; } @@ -69,6 +64,16 @@ pub(super) fn check<'tcx>( fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<FnRetTy<'tcx>> { for (_, parent_node) in cx.tcx.hir().parent_iter(expr.hir_id) { match parent_node { + // Skip `Coroutine` closures, these are the body of `async fn`, not async closures. + // This is because we still need to backtrack one parent node to get the `OpaqueDef` ty. + Node::Expr(Expr { + kind: + ExprKind::Closure(hir::Closure { + kind: hir::ClosureKind::Coroutine(_), + .. + }), + .. + }) => (), Node::Item(hir::Item { kind: hir::ItemKind::Fn(FnSig { decl, .. }, _, _), .. @@ -143,3 +148,41 @@ impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> { } } } + +/// Return `true` if the given [`FnRetTy`] is never (!). +/// +/// Note: This function also take care of return type of async fn, +/// as the actual type is behind an [`OpaqueDef`](TyKind::OpaqueDef). +fn is_never_return(ret_ty: FnRetTy<'_>) -> bool { + let FnRetTy::Return(hir_ty) = ret_ty else { return false }; + + match hir_ty.kind { + TyKind::Never => true, + TyKind::OpaqueDef(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn { .. }, + bounds, + .. + }) => { + if let Some(trait_ref) = bounds.iter().find_map(|b| b.trait_ref()) + && let Some(segment) = trait_ref + .path + .segments + .iter() + .find(|seg| seg.ident.name == sym::future_trait) + && let Some(args) = segment.args + && let Some(cst_kind) = args + .constraints + .iter() + .find_map(|cst| (cst.ident.name == sym::Output).then_some(cst.kind)) + && let hir::AssocItemConstraintKind::Equality { + term: hir::Term::Ty(ty), + } = cst_kind + { + matches!(ty.kind, TyKind::Never) + } else { + false + } + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index 21f9a71f2c5..094b1947324 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -80,7 +80,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { - if bk == ty::BorrowKind::MutBorrow { + if bk == ty::BorrowKind::Mutable { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs index 1a1cde3c5bd..7da4fa76e2c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs @@ -19,10 +19,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' cx, ids: HirIdSet::default(), def_ids: DefIdMap::default(), - skip: false, }; - var_visitor.visit_expr(cond); - if var_visitor.skip { + if var_visitor.visit_expr(cond).is_break() { return; } let used_in_condition = &var_visitor.ids; @@ -81,7 +79,6 @@ struct VarCollectorVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, ids: HirIdSet, def_ids: DefIdMap<bool>, - skip: bool, } impl<'tcx> VarCollectorVisitor<'_, 'tcx> { @@ -104,11 +101,15 @@ impl<'tcx> VarCollectorVisitor<'_, 'tcx> { } impl<'tcx> Visitor<'tcx> for VarCollectorVisitor<'_, 'tcx> { - fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result { match ex.kind { - ExprKind::Path(_) => self.insert_def_id(ex), + ExprKind::Path(_) => { + self.insert_def_id(ex); + ControlFlow::Continue(()) + }, // If there is any function/method call… we just stop analysis - ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true, + ExprKind::Call(..) | ExprKind::MethodCall(..) => ControlFlow::Break(()), _ => walk_expr(self, ex), } diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 74467522619..b7e37c1a876 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; @@ -204,35 +206,32 @@ fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tc struct V<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, - uses_iter: bool, } impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if self.uses_iter { - // return - } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { - self.uses_iter = true; + type Result = ControlFlow<()>; + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + ControlFlow::Break(()) } else if let (e, true) = skip_fields_and_path(e) { if let Some(e) = e { - self.visit_expr(e); + self.visit_expr(e) + } else { + ControlFlow::Continue(()) } } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind { if is_res_used(self.cx, self.iter_expr.path, id) { - self.uses_iter = true; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } else { - walk_expr(self, e); + walk_expr(self, e) } } } - let mut v = V { - cx, - iter_expr, - uses_iter: false, - }; - v.visit_expr(container); - v.uses_iter + let mut v = V { cx, iter_expr }; + v.visit_expr(container).is_break() } #[expect(clippy::too_many_lines)] @@ -242,34 +241,38 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & iter_expr: &'b IterExpr, loop_id: HirId, after_loop: bool, - used_iter: bool, } impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { type NestedFilter = OnlyBodies; + type Result = ControlFlow<()>; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if self.used_iter { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { if self.after_loop { if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { - self.used_iter = true; + ControlFlow::Break(()) } else if let (e, true) = skip_fields_and_path(e) { if let Some(e) = e { - self.visit_expr(e); + self.visit_expr(e) + } else { + ControlFlow::Continue(()) } } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind { - self.used_iter = is_res_used(self.cx, self.iter_expr.path, id); + if is_res_used(self.cx, self.iter_expr.path, id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } } else { - walk_expr(self, e); + walk_expr(self, e) } } else if self.loop_id == e.hir_id { self.after_loop = true; + ControlFlow::Continue(()) } else { - walk_expr(self, e); + walk_expr(self, e) } } } @@ -347,9 +350,8 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & iter_expr, loop_id: loop_expr.hir_id, after_loop: false, - used_iter: false, }; - v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value); - v.used_iter + v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value) + .is_break() } } diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index c904137da1a..3c77db84a40 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -106,13 +106,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, opaque: &'tcx OpaqueTy<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { - if let GenericBound::Trait(poly) = bound { - Some(&poly.trait_ref) - } else { - None - } - }) - && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait() + if let GenericBound::Trait(poly) = bound { + Some(&poly.trait_ref) + } else { + None + } + }) && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait() { return Some(trait_ref); } @@ -156,16 +155,18 @@ fn captures_all_lifetimes(cx: &LateContext<'_>, fn_def_id: LocalDefId, opaque_de .tcx .opaque_captured_lifetimes(opaque_def_id) .iter() - .filter(|&(lifetime, _)| match *lifetime { - ResolvedArg::EarlyBound(_) | ResolvedArg::LateBound(ty::INNERMOST, _, _) => true, - _ => false, + .filter(|&(lifetime, _)| { + matches!( + *lifetime, + ResolvedArg::EarlyBound(_) | ResolvedArg::LateBound(ty::INNERMOST, _, _) + ) }) .count(); num_captured_lifetimes == num_early_lifetimes + num_late_lifetimes } fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { - if let Some(Expr { + if let Some(&Expr { kind: ExprKind::Closure(&Closure { kind, body, .. }), .. }) = block.expr diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index 1bd8813e348..fd71167f814 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -7,8 +7,7 @@ use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind && let BinOpKind::Mul = &bin_op.node - && !in_external_macro(cx.sess(), expr.span) + && !expr.span.from_expansion() && self.msrv.meets(msrvs::MANUAL_BITS) && let ctxt = expr.span.ctxt() && left_expr.span.ctxt() == ctxt diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index c62bd65bea6..7a9c9963742 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -68,7 +68,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Some(init) = local.init && !init.span.from_expansion() && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind - && seg.ident.name == sym!(build_hasher) + && seg.ident.name.as_str() == "build_hasher" && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id) && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id) @@ -96,7 +96,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id) && !finish_expr.span.from_expansion() && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind - && seg.ident.name == sym!(finish) + && seg.ident.name.as_str() == "finish" && self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE) { diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 24207705743..dec8c5d85de 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { check_is_ascii(cx, macro_call.span, recv, &range, None); } } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind - && path.ident.name == sym!(contains) + && path.ident.name.as_str() == "contains" && let Some(higher::Range { start: Some(start), end: Some(end), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 64cb7a06ce9..6d62b530ae7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -148,7 +148,7 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> { }) => Some(*b), ExprKind::Block( rustc_hir::Block { - stmts: &[], + stmts: [], expr: Some(exp), .. }, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 463aa602bc8..1267fc9d0a5 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; @@ -23,11 +25,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind() && let ty::Str = ty.kind() { - let mut visitor = MatchExprVisitor { cx, case_method: None }; - - visitor.visit_expr(scrutinee); - - if let Some(case_method) = visitor.case_method { + let mut visitor = MatchExprVisitor { cx }; + if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) { if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } @@ -37,30 +36,33 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm struct MatchExprVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - case_method: Option<CaseMethod>, } impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> { - fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - match ex.kind { - ExprKind::MethodCall(segment, receiver, [], _) if self.case_altered(segment.ident.as_str(), receiver) => {}, - _ => walk_expr(self, ex), + type Result = ControlFlow<CaseMethod>; + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result { + if let ExprKind::MethodCall(segment, receiver, [], _) = ex.kind { + let result = self.case_altered(segment.ident.as_str(), receiver); + if result.is_break() { + return result; + } } + + walk_expr(self, ex) } } impl MatchExprVisitor<'_, '_> { - fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool { + fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> ControlFlow<CaseMethod> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { - self.case_method = Some(case_method); - return true; + return ControlFlow::Break(case_method); } } - false + ControlFlow::Continue(()) } } diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 42d9efe4ff6..9e54475033c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -10,7 +10,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, sym}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -95,7 +95,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: } else if let ExprKind::MethodCall(path, recv, args, ..) = guard.kind && let Some(binding) = get_pat_binding(cx, recv, outer_arm) { - check_method_calls(cx, outer_arm, path.ident.name, recv, args, guard, &binding); + check_method_calls(cx, outer_arm, path.ident.name.as_str(), recv, args, guard, &binding); } } } @@ -103,7 +103,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: fn check_method_calls<'tcx>( cx: &LateContext<'tcx>, arm: &Arm<'tcx>, - method: Symbol, + method: &str, recv: &Expr<'_>, args: &[Expr<'_>], if_expr: &Expr<'_>, @@ -112,7 +112,7 @@ fn check_method_calls<'tcx>( let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let slice_like = ty.is_slice() || ty.is_array(); - let sugg = if method == sym!(is_empty) { + let sugg = if method == "is_empty" { // `s if s.is_empty()` becomes "" // `arr if arr.is_empty()` becomes [] @@ -137,9 +137,9 @@ fn check_method_calls<'tcx>( if needles.is_empty() { sugg.insert_str(1, ".."); - } else if method == sym!(starts_with) { + } else if method == "starts_with" { sugg.insert_str(sugg.len() - 1, ", .."); - } else if method == sym!(ends_with) { + } else if method == "ends_with" { sugg.insert_str(1, ".., "); } else { return; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 9ca75fb2615..ca45ed6ea59 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -319,10 +319,9 @@ fn found_good_method<'tcx>( node: (&PatKind<'_>, &PatKind<'_>), ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { match node { - ( - PatKind::TupleStruct(ref path_left, patterns_left, _), - PatKind::TupleStruct(ref path_right, patterns_right, _), - ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + (PatKind::TupleStruct(path_left, patterns_left, _), PatKind::TupleStruct(path_right, patterns_right, _)) + if patterns_left.len() == 1 && patterns_right.len() == 1 => + { if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { find_good_method_for_match( cx, @@ -350,8 +349,8 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) + (PatKind::TupleStruct(path_left, patterns, _), PatKind::Path(path_right)) + | (PatKind::Path(path_left), PatKind::TupleStruct(path_right, patterns, _)) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { @@ -381,14 +380,14 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { + (PatKind::TupleStruct(path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { get_good_method(cx, arms, path_left) } else { None } }, - (PatKind::Path(ref path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), + (PatKind::Path(path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 047d070a131..2aea25b5f12 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -247,7 +247,10 @@ impl<'a> PatState<'a> { let states = match self { Self::Wild => return None, Self::Other => { - *self = Self::StdEnum(cx.arena.alloc_from_iter((0..adt.variants().len()).map(|_| Self::Other))); + *self = Self::StdEnum( + cx.arena + .alloc_from_iter(std::iter::repeat_with(|| Self::Other).take(adt.variants().len())), + ); let Self::StdEnum(x) = self else { unreachable!(); }; diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 06482a743dd..c1653b65e98 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -21,8 +21,8 @@ fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, ExprKind::Path(QPath::Resolved(_, segments)) => segments.segments.last().unwrap().ident.name == method_name, ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name, - ExprKind::Closure(&Closure { body, .. }) => { - let body = cx.tcx.hir().body(body); + ExprKind::Closure(Closure { body, .. }) => { + let body = cx.tcx.hir().body(*body); let closure_expr = peel_blocks(body.value); match closure_expr.kind { ExprKind::MethodCall(PathSegment { ident, .. }, receiver, ..) => { @@ -234,12 +234,12 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym!(is_some) { + if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name.as_str() == "is_some" { Some(Self::IsSome { receiver, side_effect_expr_span, }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym!(is_ok) { + } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name.as_str() == "is_ok" { Some(Self::IsOk { receiver, side_effect_expr_span, diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index cc82f6cfd63..a0c21faaa4c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{find_binding_init, path_to_local}; +use clippy_utils::macros::{is_assert_macro, root_macro_call}; +use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local}; use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -14,6 +15,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) { return; } + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(parent) = get_parent_expr(cx, parent) { + if is_inside_always_const_context(cx.tcx, expr.hir_id) + && let Some(macro_call) = root_macro_call(parent.span) + && is_assert_macro(cx, macro_call.def_id) + { + return; + } + } + } let init_expr = expr_or_init(cx, receiver); if !receiver.span.eq_ctxt(init_expr.span) { return; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs index 9d462bd1845..9a62b719a8f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -29,10 +29,7 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) { // For array::IntoIter<T, const N: usize>, the length is the second generic // parameter. - substs - .const_at(1) - .try_to_target_usize(cx.tcx) - .map(u128::from) + substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did) && let ExprKind::MethodCall(_, recv, ..) = iter.kind { diff --git a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs new file mode 100644 index 00000000000..ac11baa2d54 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs @@ -0,0 +1,43 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::{is_expr_identity_function, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::{Span, sym}; + +use super::MAP_ALL_ANY_IDENTITY; + +#[allow(clippy::too_many_arguments)] +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + map_call_span: Span, + map_arg: &Expr<'_>, + any_call_span: Span, + any_arg: &Expr<'_>, + method: &str, +) { + if is_trait_method(cx, expr, sym::Iterator) + && is_trait_method(cx, recv, sym::Iterator) + && is_expr_identity_function(cx, any_arg) + && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) + && let Some(map_arg) = map_arg.span.get_source_text(cx) + { + span_lint_and_then( + cx, + MAP_ALL_ANY_IDENTITY, + map_any_call_span, + format!("usage of `.map(...).{method}(identity)`"), + |diag| { + diag.span_suggestion_verbose( + map_any_call_span, + format!("use `.{method}(...)` instead"), + format!("{method}({map_arg})"), + Applicability::MachineApplicable, + ); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs new file mode 100644 index 00000000000..fc656fd78ba --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -0,0 +1,134 @@ +use crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::{eager_or_lazy, higher, usage}; +use rustc_ast::LitKind; +use rustc_ast::ast::RangeLimits; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{Body, Closure, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +fn extract_count_with_applicability( + cx: &LateContext<'_>, + range: higher::Range<'_>, + applicability: &mut Applicability, +) -> Option<String> { + let start = range.start?; + let end = range.end?; + // TODO: This doens't handle if either the start or end are negative literals, or if the start is + // not a literal. In the first case, we need to be careful about how we handle computing the + // count to avoid overflows. In the second, we may need to add parenthesis to make the + // suggestion correct. + if let ExprKind::Lit(lit) = start.kind + && let LitKind::Int(Pu128(lower_bound), _) = lit.node + { + if let ExprKind::Lit(lit) = end.kind + && let LitKind::Int(Pu128(upper_bound), _) = lit.node + { + // Here we can explicitly calculate the number of iterations + let count = if upper_bound >= lower_bound { + match range.limits { + RangeLimits::HalfOpen => upper_bound - lower_bound, + RangeLimits::Closed => (upper_bound - lower_bound).checked_add(1)?, + } + } else { + 0 + }; + return Some(format!("{count}")); + } + let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability) + .maybe_par() + .into_string(); + if lower_bound == 0 { + if range.limits == RangeLimits::Closed { + return Some(format!("{end_snippet} + 1")); + } + return Some(end_snippet); + } + if range.limits == RangeLimits::Closed { + return Some(format!("{end_snippet} - {}", lower_bound - 1)); + } + return Some(format!("{end_snippet} - {lower_bound}")); + } + None +} + +pub(super) fn check( + cx: &LateContext<'_>, + ex: &Expr<'_>, + receiver: &Expr<'_>, + arg: &Expr<'_>, + msrv: &Msrv, + method_call_span: Span, +) { + let mut applicability = Applicability::MaybeIncorrect; + if let Some(range) = higher::Range::hir(receiver) + && let ExprKind::Closure(Closure { body, .. }) = arg.kind + && let body_hir = cx.tcx.hir().body(*body) + && let Body { + params: [param], + value: body_expr, + } = body_hir + && !usage::BindingUsageFinder::are_params_used(cx, body_hir) + && let Some(count) = extract_count_with_applicability(cx, range, &mut applicability) + { + let method_to_use_name; + let new_span; + let use_take; + + if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { + if msrv.meets(msrvs::REPEAT_N) { + method_to_use_name = "repeat_n"; + let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability); + new_span = (arg.span, format!("{body_snippet}, {count}")); + use_take = false; + } else { + method_to_use_name = "repeat"; + let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability); + new_span = (arg.span, body_snippet.to_string()); + use_take = true; + } + } else if msrv.meets(msrvs::REPEAT_WITH) { + method_to_use_name = "repeat_with"; + new_span = (param.span, String::new()); + use_take = true; + } else { + return; + } + + // We need to provide nonempty parts to diag.multipart_suggestion so we + // collate all our parts here and then remove those that are empty. + let mut parts = vec![ + ( + receiver.span.to(method_call_span), + format!("std::iter::{method_to_use_name}"), + ), + new_span, + ]; + if use_take { + parts.push((ex.span.shrink_to_hi(), format!(".take({count})"))); + } + + span_lint_and_then( + cx, + MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, + ex.span, + "map of a closure that does not depend on its parameter over a range", + |diag| { + diag.multipart_suggestion( + if use_take { + format!("remove the explicit range and use `{method_to_use_name}` and `take`") + } else { + format!("remove the explicit range and use `{method_to_use_name}`") + }, + parts, + applicability, + ); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 2a391870d70..b1809796355 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -60,13 +60,16 @@ mod manual_ok_or; mod manual_saturating_arithmetic; mod manual_str_repeat; mod manual_try_fold; +mod map_all_any_identity; mod map_clone; mod map_collect_result_unit; mod map_err_ignore; mod map_flatten; mod map_identity; mod map_unwrap_or; +mod map_with_unused_argument_over_ranges; mod mut_mutex_lock; +mod needless_as_bytes; mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; @@ -4166,6 +4169,90 @@ declare_clippy_lint! { "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`" } +declare_clippy_lint! { + /// ### What it does + /// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`. + /// + /// ### Why is this bad? + /// The `len()` and `is_empty()` methods are also directly available on strings, and they + /// return identical results. In particular, `len()` on a string returns the number of + /// bytes. + /// + /// ### Example + /// ``` + /// let len = "some string".as_bytes().len(); + /// let b = "some string".as_bytes().is_empty(); + /// ``` + /// Use instead: + /// ``` + /// let len = "some string".len(); + /// let b = "some string".is_empty(); + /// ``` + #[clippy::version = "1.84.0"] + pub NEEDLESS_AS_BYTES, + complexity, + "detect useless calls to `as_bytes()`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.map(…)`, followed by `.all(identity)` or `.any(identity)`. + /// + /// ### Why is this bad? + /// The `.all(…)` or `.any(…)` methods can be called directly in place of `.map(…)`. + /// + /// ### Example + /// ``` + /// # let mut v = [""]; + /// let e1 = v.iter().map(|s| s.is_empty()).all(|a| a); + /// let e2 = v.iter().map(|s| s.is_empty()).any(std::convert::identity); + /// ``` + /// Use instead: + /// ``` + /// # let mut v = [""]; + /// let e1 = v.iter().all(|s| s.is_empty()); + /// let e2 = v.iter().any(|s| s.is_empty()); + /// ``` + #[clippy::version = "1.84.0"] + pub MAP_ALL_ANY_IDENTITY, + complexity, + "combine `.map(_)` followed by `.all(identity)`/`.any(identity)` into a single call" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `Iterator::map` over ranges without using the parameter which + /// could be more clearly expressed using `std::iter::repeat(...).take(...)` + /// or `std::iter::repeat_n`. + /// + /// ### Why is this bad? + /// + /// It expresses the intent more clearly to `take` the correct number of times + /// from a generating function than to apply a closure to each number in a + /// range only to discard them. + /// + /// ### Example + /// + /// ```no_run + /// let random_numbers : Vec<_> = (0..10).map(|_| { 3 + 1 }).collect(); + /// ``` + /// Use instead: + /// ```no_run + /// let f : Vec<_> = std::iter::repeat( 3 + 1 ).take(10).collect(); + /// ``` + /// + /// ### Known Issues + /// + /// This lint may suggest replacing a `Map<Range>` with a `Take<RepeatWith>`. + /// The former implements some traits that the latter does not, such as + /// `DoubleEndedIterator`. + #[clippy::version = "1.84.0"] + pub MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, + restriction, + "map of a trivial closure (not dependent on parameter) over a range" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4327,6 +4414,9 @@ impl_lint_pass!(Methods => [ NEEDLESS_CHARACTER_ITERATION, MANUAL_INSPECT, UNNECESSARY_MIN_OR_MAX, + NEEDLESS_AS_BYTES, + MAP_ALL_ANY_IDENTITY, + MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4534,15 +4624,21 @@ impl Methods { ("all", [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, true); - if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { - iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::NeedlessMove(arg), - false, - ); + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => { + iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::NeedlessMove(arg), + false, + ); + }, + Some(("map", _, [map_arg], _, map_call_span)) => { + map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all"); + }, + _ => {}, } }, ("and_then", [arg]) => { @@ -4571,6 +4667,9 @@ impl Methods { { string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); }, + Some(("map", _, [map_arg], _, map_call_span)) => { + map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any"); + }, _ => {}, } }, @@ -4764,8 +4863,14 @@ impl Methods { unit_hash::check(cx, expr, recv, arg); }, ("is_empty", []) => { - if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { - redundant_as_str::check(cx, expr, recv, as_str_span, span); + match method_call(recv) { + Some(("as_bytes", prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span); + }, + Some(("as_str", recv, [], as_str_span, _)) => { + redundant_as_str::check(cx, expr, recv, as_str_span, span); + }, + _ => {}, } is_empty::check(cx, expr, recv); }, @@ -4795,6 +4900,11 @@ impl Methods { ); } }, + ("len", []) => { + if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span); + } + }, ("lock", []) => { mut_mutex_lock::check(cx, expr, recv, span); }, @@ -4802,6 +4912,7 @@ impl Methods { if name == "map" { unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, &self.msrv); + map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, &self.msrv, span); match method_call(recv) { Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => { iter_kv_map::check(cx, map_name, expr, recv2, m_arg, &self.msrv); @@ -5182,7 +5293,6 @@ impl ShouldImplTraitCase { } #[rustfmt::skip] -#[expect(clippy::large_const_arrays, reason = "`Span` is not sync, so this can't be static")] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs new file mode 100644 index 00000000000..75e9f317230 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs @@ -0,0 +1,28 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_lang_item; +use rustc_errors::Applicability; +use rustc_hir::{Expr, LangItem}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::NEEDLESS_AS_BYTES; + +pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) { + if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() + && let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs() + && (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str()) + { + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); + span_lint_and_sugg( + cx, + NEEDLESS_AS_BYTES, + span, + "needless call to `as_bytes()`", + format!("`{method}()` can be called directly on strings"), + format!("{sugg}.{method}()"), + app, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 96a31812ca4..055107068ae 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -322,7 +322,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { // Check function calls on our collection if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() - && method_name.ident.name == sym!(collect) + && method_name.ident.name.as_str() == "collect" && is_trait_method(self.cx, expr, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index 65e545ed03a..db2b9d4d92f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -44,7 +44,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if let Some(parent) = get_parent_expr(cx, expr) { let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind { if args.is_empty() - && segment.ident.name == sym!(parse) + && segment.ident.name.as_str() == "parse" && let parse_result_ty = cx.typeck_results().expr_ty(parent) && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() @@ -58,7 +58,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< "calling `.parse()` on a string without trimming the trailing newline character", "checking", )) - } else if segment.ident.name == sym!(ends_with) + } else if segment.ident.name.as_str() == "ends_with" && recv.span == expr.span && let [arg] = args && expr_is_string_literal_without_trailing_newline(arg) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index ca46da81fa0..bab439015c5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,13 +1,15 @@ use super::utils::clone_or_copy_needed; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use clippy_utils::{MaybePath, is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; +use rustc_middle::query::Key; use rustc_middle::ty; use rustc_span::sym; @@ -36,9 +38,25 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a ControlFlow::Continue(Descend::Yes) } }); - let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { + // Check if the closure is .filter_map(|x| Some(x)) + if name == "filter_map" + && let hir::ExprKind::Call(expr, args) = body.value.kind + && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) + && arg_id.ty_def_id() == args[0].hir_id().ty_def_id() + && let hir::ExprKind::Path(_) = args[0].kind + { + span_lint_and_sugg( + cx, + UNNECESSARY_FILTER_MAP, + expr.span, + format!("{name} is unnecessary"), + "try removing the filter_map", + String::new(), + Applicability::MaybeIncorrect, + ); + } if name == "filter_map" { "map" } else { "map(..).next()" } } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) { match cx.typeck_results().expr_ty(body.value).kind() { @@ -52,7 +70,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a } else { return; }; - span_lint( + span_lint_and_sugg( cx, if name == "filter_map" { UNNECESSARY_FILTER_MAP @@ -60,7 +78,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a UNNECESSARY_FIND_MAP }, expr.span, - format!("this `.{name}` can be written more simply using `.{sugg}`"), + format!("this `.{name}` can be written more simply"), + "try instead", + sugg.to_string(), + Applicability::MaybeIncorrect, ); } } @@ -78,7 +99,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc (true, true) }, hir::ExprKind::MethodCall(segment, recv, [arg], _) => { - if segment.ident.name == sym!(then_some) + if segment.ident.name.as_str() == "then_some" && cx.typeck_results().expr_ty(recv).is_bool() && path_to_local_id(arg, arg_id) { diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index e95864c6db8..ed89b3b3438 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -79,9 +79,9 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { - if path.ident.name == sym!(max) { + if path.ident.name.as_str() == "max" { fetch_const(cx, Some(receiver), args, MinMax::Max) - } else if path.ident.name == sym!(min) { + } else if path.ident.name.as_str() == "min" { fetch_const(cx, Some(receiver), args, MinMax::Min) } else { None diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index ce508d85d63..1300c7d1062 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -12,11 +12,13 @@ use clippy_utils::is_from_proc_macro; use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Visibility; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::symbol::kw; use rustc_span::{Span, sym}; declare_clippy_lint! { @@ -110,6 +112,21 @@ impl MissingDoc { return; } + if let Some(parent_def_id) = cx.tcx.opt_parent(def_id.to_def_id()) + && let DefKind::AnonConst + | DefKind::AssocConst + | DefKind::AssocFn + | DefKind::Closure + | DefKind::Const + | DefKind::Fn + | DefKind::InlineConst + | DefKind::Static { .. } + | DefKind::SyntheticCoroutineBody = cx.tcx.def_kind(parent_def_id) + { + // Nested item has no generated documentation, so it doesn't need to be documented. + return; + } + let has_doc = attrs .iter() .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())) @@ -184,8 +201,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } } }, - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) + hir::ItemKind::Const(..) => { + if it.ident.name == kw::Underscore { + note_prev_span_then_ret!(self.prev_span, it.span); + } + }, + hir::ItemKind::Enum(..) | hir::ItemKind::Macro(..) | hir::ItemKind::Mod(..) | hir::ItemKind::Static(..) diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index fc01b6753f1..f28b431ab99 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -116,7 +116,7 @@ fn should_lint<'tcx>( if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name == sym!(finish_non_exhaustive) + } else if path.ident.name.as_str() == "finish_non_exhaustive" && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) { has_finish_non_exhaustive = true; diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index f95a0f63fab..e587d695c84 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::AssocItemContainer; use rustc_session::declare_lint_pass; use rustc_span::{Span, sym}; @@ -138,7 +139,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - use rustc_middle::ty::{ImplContainer, TraitContainer}; if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable_or_proc_macro(cx) { return; } @@ -156,8 +156,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { - TraitContainer => Some(container_id), - ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocItemContainer::Trait => Some(container_id), + AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; if let Some(trait_def_id) = trait_def_id { diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index ce97370d4d9..c4836772933 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -330,10 +330,11 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin } fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind + if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind && let Some(last_stmt) = loop_block.stmts.last() && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind - && let ast::ExprKind::Continue(_) = inner_expr.kind + && let ast::ExprKind::Continue(continue_label) = inner_expr.kind + && compare_labels(loop_label.as_ref(), continue_label.as_ref()) { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 46cdb82130f..a67addea948 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -347,7 +347,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { if let LetStmt { init: None, pat: - &Pat { + Pat { kind: PatKind::Binding(BindingMode::NONE, binding_id, _, None), .. }, @@ -357,7 +357,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { && let Some((_, Node::Stmt(local_stmt))) = parents.next() && let Some((_, Node::Block(block))) = parents.next() { - check(cx, local, local_stmt, block, binding_id); + check(cx, local, local_stmt, block, *binding_id); } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs index 9a1c397b5b2..852a0ce8c6d 100644 --- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, BoundPolarity, WherePredicate}; +use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ClauseKind, PredicatePolarity}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 5c631a176c4..b7dc269061c 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { .iter() .zip(fn_sig.inputs()) .zip(body.params) - .filter(|((&input, &ty), arg)| !should_skip(cx, input, ty, arg)) + .filter(|&((&input, &ty), arg)| !should_skip(cx, input, ty, arg)) .peekable(); if it.peek().is_none() { return; @@ -417,8 +417,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { // a closure, it'll return this variant whereas if you have just an index access, it'll // return `ImmBorrow`. So if there is "Unique" and it's a mutable reference, we add it // to the mutably used variables set. - if borrow == ty::BorrowKind::MutBorrow - || (borrow == ty::BorrowKind::UniqueImmBorrow && base_ty.ref_mutability() == Some(Mutability::Mut)) + if borrow == ty::BorrowKind::Mutable + || (borrow == ty::BorrowKind::UniqueImmutable && base_ty.ref_mutability() == Some(Mutability::Mut)) { self.add_mutably_used_var(*vid); } else if self.is_in_unsafe_block(id) { @@ -426,7 +426,7 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { // upon! self.add_mutably_used_var(*vid); } - } else if borrow == ty::ImmBorrow { + } else if borrow == ty::BorrowKind::Immutable { // If there is an `async block`, it'll contain a call to a closure which we need to // go into to ensure all "mutate" checks are found. if let Node::Expr(Expr { diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 2e5195d459f..74536028b5d 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -159,8 +159,12 @@ impl NoEffect { // Remove `impl Future<Output = T>` to get `T` if cx.tcx.ty_is_opaque_future(ret_ty) - && let Some(true_ret_ty) = - cx.tcx.infer_ctxt().build(cx.typing_mode()).err_ctxt().get_impl_future_output_ty(ret_ty) + && let Some(true_ret_ty) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .get_impl_future_output_ty(ret_ty) { ret_ty = true_ret_ty; } diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs index 8d5a523fd8f..64587e08ddc 100644 --- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -18,13 +18,13 @@ declare_clippy_lint! { /// Rust ABI can break this at any point. /// /// ### Example - /// ```no_run + /// ```rust,ignore /// #[no_mangle] /// fn example(arg_one: u32, arg_two: usize) {} /// ``` /// /// Use instead: - /// ```no_run + /// ```rust,ignore /// #[no_mangle] /// extern "C" fn example(arg_one: u32, arg_two: usize) {} /// ``` @@ -40,24 +40,25 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { if let ItemKind::Fn(fn_sig, _, _) = &item.kind { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut app = Applicability::MaybeIncorrect; - let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app); + let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app); for attr in attrs { if let Some(ident) = attr.ident() && ident.name == rustc_span::sym::no_mangle && fn_sig.header.abi == Abi::Rust - && let Some((fn_attrs, _)) = snippet.split_once("fn") + && let Some((fn_attrs, _)) = fn_snippet.rsplit_once("fn") && !fn_attrs.contains("extern") { let sugg_span = fn_sig .span .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len())) .shrink_to_lo(); + let attr_snippet = snippet(cx, attr.span, ".."); span_lint_and_then( cx, NO_MANGLE_WITH_RUST_ABI, fn_sig.span, - "`#[no_mangle]` set on a function with the default (`Rust`) ABI", + format!("`{attr_snippet}` set on a function with the default (`Rust`) ABI"), |diag| { diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app) .span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app); diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 3f156aa5510..0caa19cd844 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -43,12 +43,12 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name == sym!(mode) + && ((path.ident.name.as_str() == "mode" && matches!( cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder) )) - || (path.ident.name == sym!(set_mode) + || (path.ident.name.as_str() == "set_mode" && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 11c97b4ef00..0dcaec1c9a7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -102,7 +102,7 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { struct S(HirIdSet); impl Delegate<'_> for S { fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: HirId, kind: BorrowKind) { - if matches!(kind, BorrowKind::ImmBorrow | BorrowKind::UniqueImmBorrow) { + if matches!(kind, BorrowKind::Immutable | BorrowKind::UniqueImmutable) { self.0.insert(match place.place.base { PlaceBase::Local(id) => id, PlaceBase::Upvar(id) => id.var_path.hir_id, @@ -127,7 +127,7 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { struct S(HirIdSet); impl Delegate<'_> for S { fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: HirId, kind: BorrowKind) { - if matches!(kind, BorrowKind::MutBorrow) { + if matches!(kind, BorrowKind::Mutable) { self.0.insert(match place.place.base { PlaceBase::Local(id) => id, PlaceBase::Upvar(id) => id.var_path.hir_id, diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 8e394944c21..ab5f91c1d67 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -107,7 +107,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind - && sym!(signum) == method_name.ident.name + && method_name.ident.name.as_str() == "signum" // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) { diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 18e6aad9c9a..794bef7b321 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -34,7 +34,7 @@ declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]); impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items: impl_items, .. }) = item.kind diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index 56d07aeae17..808a7e005c6 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -96,7 +96,7 @@ fn expr_as_ptr_offset_call<'tcx>( if path_segment.ident.name == sym::offset { return Some((arg_0, arg_1, Method::Offset)); } - if path_segment.ident.name == sym!(wrapping_offset) { + if path_segment.ident.name.as_str() == "wrapping_offset" { return Some((arg_0, arg_1, Method::WrappingOffset)); } } diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 9344cb41993..cb374fcf20e 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -93,7 +93,7 @@ enum IfBlockType<'hir> { fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir Expr<'hir>> { if let Block { - stmts: &[], + stmts: [], expr: Some(els), .. } = block @@ -163,8 +163,8 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ is_type_diagnostic_item(cx, caller_ty, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { - sym::Option => call_sym == sym!(is_none), - sym::Result => call_sym == sym!(is_err), + sym::Option => call_sym.as_str() == "is_none", + sym::Result => call_sym.as_str() == "is_err", _ => false, } }, diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index 19ce08bde10..7d59bf24d93 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if let TyKind::Ref(_, ref mut_ty) = ty.kind && mut_ty.mutbl == Mutability::Not - && let TyKind::Path(ref qpath) = &mut_ty.ty.kind + && let TyKind::Path(qpath) = &mut_ty.ty.kind && let last = last_path_segment(qpath) && let Some(def_id) = last.res.opt_def_id() && cx.tcx.is_diagnostic_item(sym::Option, def_id) diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 110dea8fb8e..1e0f6dff1ab 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -347,7 +347,7 @@ fn check_final_expr<'tcx>( let peeled_drop_expr = expr.peel_drop_temps(); match &peeled_drop_expr.kind { // simple return is always "bad" - ExprKind::Ret(ref inner) => { + ExprKind::Ret(inner) => { // check if expr return nothing let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index 09f1c112352..d85f4a8fa01 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -101,17 +101,21 @@ impl SemicolonBlock { ); } - fn semicolon_outside_block( - &self, - cx: &LateContext<'_>, - block: &Block<'_>, - tail_stmt_expr: &Expr<'_>, - semi_span: Span, - ) { + fn semicolon_outside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>) { let insert_span = block.span.with_lo(block.span.hi()); - // account for macro calls - let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span); - let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi()); + + // For macro call semicolon statements (`mac!();`), the statement's span does not actually + // include the semicolon itself, so use `mac_call_stmt_semi_span`, which finds the semicolon + // based on a source snippet. + // (Does not use `stmt_span` as that requires `.from_expansion()` to return true, + // which is not the case for e.g. `line!();` and `asm!();`) + let Some(remove_span) = cx + .sess() + .source_map() + .mac_call_stmt_semi_span(tail_stmt_expr.span.source_callsite()) + else { + return; + }; if self.semicolon_outside_block_ignore_multiline && get_line(cx, remove_span) != get_line(cx, insert_span) { return; @@ -150,13 +154,12 @@ impl LateLintPass<'_> for SemicolonBlock { }; let &Stmt { kind: StmtKind::Semi(expr), - span, .. } = stmt else { return; }; - self.semicolon_outside_block(cx, block, expr, span); + self.semicolon_outside_block(cx, block, expr); }, StmtKind::Semi(Expr { kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index fcf1c4cc5c2..6a0dfde2d9c 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -26,7 +26,7 @@ declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]); impl<'tcx> LateLintPass<'tcx> for SerdeApi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items, .. }) = item.kind diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 9fdee8543a8..fa082453504 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -174,7 +174,7 @@ impl SingleComponentPathImports { } match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + ItemKind::Mod(_, ModKind::Loaded(items, ..)) => { self.check_mod(items); }, ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index ec6e45256d1..8c8f11569c8 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -235,7 +235,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name == sym!(extend) + && path.ident.name.as_str() == "extend" && self.is_repeat_take(extend_arg) { self.slow_expression = Some(InitializationType::Extend(expr)); @@ -247,7 +247,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name == sym!(resize) + && path.ident.name.as_str() == "resize" // Check that is filled with 0 && is_integer_literal(fill_arg, 0) { @@ -269,7 +269,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind - && take_path.ident.name == sym!(take) + && take_path.ident.name.as_str() == "take" // Check that take is applied to `repeat(0)` && self.is_repeat_zero(recv) { diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index bf49bb60162..e09c0706006 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if !in_external_macro(cx.sess(), e.span) && let ExprKind::MethodCall(path, receiver, ..) = &e.kind - && path.ident.name == sym!(as_bytes) + && path.ident.name.as_str() == "as_bytes" && let ExprKind::Lit(lit) = &receiver.kind && let LitKind::Str(lit_content, _) = &lit.node { @@ -332,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } if let ExprKind::MethodCall(path, recv, [], _) = &e.kind - && path.ident.name == sym!(into_bytes) + && path.ident.name.as_str() == "into_bytes" && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind && matches!(path.ident.name.as_str(), "to_owned" | "to_string") && let ExprKind::Lit(lit) = &recv.kind @@ -493,7 +493,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { let tyckres = cx.typeck_results(); if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind - && path.ident.name == sym!(split_whitespace) + && path.ident.name.as_str() == "split_whitespace" && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id) && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index be32dc0aab0..0d809c17989 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -334,7 +334,7 @@ fn strip_non_ident_wrappers(expr: &Expr) -> &Expr { let mut output = expr; loop { output = match &output.kind { - ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner, + ExprKind::Paren(inner) | ExprKind::Unary(_, inner) => inner, _ => { return output; }, @@ -348,13 +348,13 @@ fn extract_related_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { fn if_statement_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { match kind { - ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind), - ExprKind::Paren(ref e) => if_statement_binops(&e.kind), - ExprKind::Block(ref block, _) => { + ExprKind::If(condition, _, _) => chained_binops(&condition.kind), + ExprKind::Paren(e) => if_statement_binops(&e.kind), + ExprKind::Block(block, _) => { let mut output = None; for stmt in &block.stmts { - match stmt.kind { - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => { + match &stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) => { output = append_opt_vecs(output, if_statement_binops(&e.kind)); }, _ => {}, @@ -383,7 +383,7 @@ fn append_opt_vecs<A>(target_opt: Option<Vec<A>>, source_opt: Option<Vec<A>>) -> fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { match kind { ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer), - ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind), + ExprKind::Paren(e) | ExprKind::Unary(_, e) => chained_binops(&e.kind), _ => None, } } @@ -391,16 +391,14 @@ fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> { match (&left_outer.kind, &right_outer.kind) { ( - ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), - ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e), + ExprKind::Paren(left_e) | ExprKind::Unary(_, left_e), + ExprKind::Paren(right_e) | ExprKind::Unary(_, right_e), ) => chained_binops_helper(left_e, right_e), - (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer), - (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => { - chained_binops_helper(left_outer, right_e) - }, + (ExprKind::Paren(left_e) | ExprKind::Unary(_, left_e), _) => chained_binops_helper(left_e, right_outer), + (_, ExprKind::Paren(right_e) | ExprKind::Unary(_, right_e)) => chained_binops_helper(left_outer, right_e), ( - ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right), - ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right), + ExprKind::Binary(Spanned { node: left_op, .. }, left_left, left_right), + ExprKind::Binary(Spanned { node: right_op, .. }, right_left, right_right), ) => match ( chained_binops_helper(left_left, left_right), chained_binops_helper(right_left, right_right), diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 6cba560393d..23362506cca 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -392,7 +392,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> { for stmt in self.block.stmts { match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => v.visit_expr(expr), - StmtKind::Let(LetStmt { ref init, .. }) => { + StmtKind::Let(LetStmt { init, .. }) => { if let Some(init) = init.as_ref() { v.visit_expr(init); } diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 3da4bf67558..07bf4319ff0 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -10,8 +10,8 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath, - TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicate, BoundPolarity, + BoundPolarity, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath, + TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let mut self_bounds_map = FxHashMap::default(); for predicate in item.generics.predicates { - if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate + if let WherePredicate::BoundPredicate(bound_predicate) = predicate && bound_predicate.origin != PredicateOrigin::ImplTrait && !bound_predicate.span.from_expansion() && let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind @@ -268,7 +268,7 @@ impl TraitBounds { let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in generics.predicates { - if let WherePredicate::BoundPredicate(ref p) = bound + if let WherePredicate::BoundPredicate(p) = bound && p.origin != PredicateOrigin::ImplTrait && p.bounds.len() as u64 <= self.max_trait_bounds && !p.span.from_expansion() @@ -379,7 +379,7 @@ struct ComparableTraitRef<'a, 'tcx> { impl PartialEq for ComparableTraitRef<'_, '_> { fn eq(&self, other: &Self) -> bool { - SpanlessEq::new(self.cx).eq_modifiers(self.modifiers, other.modifiers) + SpanlessEq::eq_modifiers(self.modifiers, other.modifiers) && SpanlessEq::new(self.cx) .paths_by_resolution() .eq_path(self.trait_ref.path, other.trait_ref.path) diff --git a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs index 1dfc9f7091e..ca9daf2d2a0 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs @@ -43,7 +43,7 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_ binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs) }, ExprKind::MethodCall(path, receiver, [arg], _) - if path.ident.name == sym!(contains) + if path.ident.name.as_str() == "contains" // ... `contains` called on some kind of range && let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def() && let lang_items = cx.tcx.lang_items() @@ -81,7 +81,7 @@ pub(super) fn check<'tcx>( if let Some(then_some_call) = peel_parent_unsafe_blocks(cx, expr) && let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind && cx.typeck_results().expr_ty(receiver).is_bool() - && path.ident.name == sym!(then_some) + && path.ident.name.as_str() == "then_some" && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) && is_normalizable(cx, cx.param_env, from_ty) 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 44eb0a6c593..0bba611116b 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -295,7 +295,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( if cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, ref node)| { matches!( node, - Node::Block(&Block { + Node::Block(Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), .. }), diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index bf2d75ea1a9..1a1284ce9c4 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -194,7 +194,7 @@ fn needs_inferred_result_ty( let (id, receiver, args) = match e.kind { ExprKind::Call( Expr { - kind: ExprKind::Path(ref path), + kind: ExprKind::Path(path), hir_id, .. }, diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index e7d26fa238e..0f4bd286145 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -149,7 +149,7 @@ fn is_empty_block(expr: &Expr<'_>) -> bool { expr.kind, ExprKind::Block( Block { - stmts: &[], + stmts: [], expr: None, .. }, diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index cf406b817da..7c9455bf8ab 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -83,6 +83,28 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { /// to consider the arms, and we want to avoid breaking the logic for situations where things /// get desugared to match. fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) { + let fn_def_id = block.hir_id.owner.to_def_id(); + if let Some(impl_id) = cx.tcx.impl_of_method(fn_def_id) + && let Some(trait_id) = cx.tcx.trait_id_of_impl(impl_id) + { + // We don't want to lint inside io::Read or io::Write implementations, as the author has more + // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836 + if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) { + return; + } + + let async_paths: [&[&str]; 4] = [ + &paths::TOKIO_IO_ASYNCREADEXT, + &paths::TOKIO_IO_ASYNCWRITEEXT, + &paths::FUTURES_IO_ASYNCREADEXT, + &paths::FUTURES_IO_ASYNCWRITEEXT, + ]; + + if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) { + return; + } + } + for stmt in block.stmts { if let hir::StmtKind::Semi(exp) = stmt.kind { check_expr(cx, exp); @@ -222,7 +244,7 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { } fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - while let ExprKind::Call(func, [ref arg_0]) = expr.kind + while let ExprKind::Call(func, [arg_0]) = expr.kind && matches!( func.kind, ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) @@ -244,7 +266,7 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// waited on. Otherwise return None. fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { - if let ExprKind::Call(func, [ref arg_0]) = expr.kind { + if let ExprKind::Call(func, [arg_0]) = expr.kind { if matches!( func.kind, ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 096b3ff9a2e..89bb429e265 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -217,7 +217,7 @@ fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { - if let ty::BorrowKind::MutBorrow = bk + if let ty::BorrowKind::Mutable = bk && is_potentially_local_place(self.local_id, &cat.place) && !is_option_as_mut_use(self.tcx, diag_expr_id) { diff --git a/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs b/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs new file mode 100644 index 00000000000..1522553bbf5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs @@ -0,0 +1,40 @@ +use std::mem; +use std::sync::OnceLock; + +use rustc_ast::{Attribute, Crate}; +use rustc_data_structures::sync::Lrc; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::Span; + +#[derive(Clone, Default)] +pub struct AttrStorage(pub Lrc<OnceLock<Vec<Span>>>); + +pub struct AttrCollector { + storage: AttrStorage, + attrs: Vec<Span>, +} + +impl AttrCollector { + pub fn new(storage: AttrStorage) -> Self { + Self { + storage, + attrs: Vec::new(), + } + } +} + +impl_lint_pass!(AttrCollector => []); + +impl EarlyLintPass for AttrCollector { + fn check_attribute(&mut self, _cx: &EarlyContext<'_>, attr: &Attribute) { + self.attrs.push(attr.span); + } + + fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) { + self.storage + .0 + .set(mem::take(&mut self.attrs)) + .expect("should only be called once"); + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 31f9d84f5e4..1bf24083665 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -396,7 +396,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.pat(field!(let_expr.pat)); // Does what ExprKind::Cast does, only adds a clause for the type // if it's a path - if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { + if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { bind!(self, qpath); chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); self.qpath(qpath); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index f662c7651f6..deb983b6971 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -6,5 +6,6 @@ pub mod lint_without_lint_pass; pub mod msrv_attr_impl; pub mod outer_expn_data_pass; pub mod produce_ice; +pub mod slow_symbol_comparisons; pub mod unnecessary_def_path; pub mod unsorted_clippy_utils_paths; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index a3f9abe4f96..eaeb754a23f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) - && let ExprKind::Closure(&Closure { body, .. }) = &call_f.kind + && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind && let body = cx.tcx.hir().body(body) && let only_expr = peel_blocks_with_stmt(body.value) && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 51235de9f29..af38e066559 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// The compiler only knows lints via a `LintPass`. Without - /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not + /// putting a lint to a `LintPass::lint_vec()`'s return, the compiler will not /// know the name of the lint. /// /// ### Known problems @@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { .expect("lints must have a description field"); if let ExprKind::Lit(Spanned { - node: LitKind::Str(ref sym, _), + node: LitKind::Str(sym, _), .. }) = field.expr.kind { @@ -159,8 +159,8 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { let body = cx.tcx.hir().body_owned_by( impl_item_refs .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") + .find(|iiref| iiref.ident.as_str() == "lint_vec") + .expect("LintPass needs to implement lint_vec") .id .owner_id .def_id, @@ -218,9 +218,7 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { if let Some(value) = extract_clippy_version_value(cx, item) { - // The `sym!` macro doesn't work as it only expects a single token. - // It's better to keep it this way and have a direct `Symbol::intern` call here. - if value == Symbol::intern("pre 1.29.0") { + if value.as_str() == "pre 1.29.0" { return; } @@ -251,7 +249,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> { let attrs = cx.tcx.hir().attrs(item.hir_id()); attrs.iter().find_map(|attr| { - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind + if let ast::AttrKind::Normal(attr_kind) = &attr.kind // Identify attribute && let [tool_name, attr_name] = &attr_kind.item.path.segments[..] && tool_name.ident.name == sym::clippy diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 63fcbd61528..68692246153 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) }) - && !items.iter().any(|item| item.ident.name == sym!(check_attributes)) + && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes") { let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs new file mode 100644 index 00000000000..3742be0e103 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs @@ -0,0 +1,69 @@ +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::match_type; +use clippy_utils::{match_function_call, paths}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects symbol comparision using `Symbol::intern`. + /// + /// ### Why is this bad? + /// + /// Comparision via `Symbol::as_str()` is faster if the interned symbols are not reused. + /// + /// ### Example + /// + /// None, see suggestion. + pub SLOW_SYMBOL_COMPARISONS, + internal, + "detects slow comparisions of symbol" +} + +declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]); + +fn check_slow_comparison<'tcx>( + cx: &LateContext<'tcx>, + op1: &'tcx Expr<'tcx>, + op2: &'tcx Expr<'tcx>, +) -> Option<(Span, String)> { + if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL) + && let Some([symbol_name_expr]) = match_function_call(cx, op2, &paths::SYMBOL_INTERN) + && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr) + { + Some((op1.span, symbol_name)) + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::Binary(op, left, right) = expr.kind + && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) + && let Some((symbol_span, symbol_name)) = + check_slow_comparison(cx, left, right).or_else(|| check_slow_comparison(cx, right, left)) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + SLOW_SYMBOL_COMPARISONS, + expr.span, + "comparing `Symbol` via `Symbol::intern`", + "use `Symbol::as_str` and check the string instead", + format!( + "{}.as_str() {} \"{symbol_name}\"", + snippet_with_applicability(cx, symbol_span, "symbol", &mut applicability), + op.node.as_str() + ), + applicability, + ); + }; + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 13e9ead9a57..4476cd1005e 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,5 +1,7 @@ +pub mod attr_collector; pub mod author; pub mod dump_hir; pub mod format_args_collector; + #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index bfb3a76ad25..75169e05734 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -21,7 +21,7 @@ use rustc_hir::{ ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::symbol::{Ident, kw}; @@ -63,8 +63,8 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit), } && match end_pat { Pat::Str(text) => end_str.ends_with(text), - Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)), - Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), + Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), + Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::Sym(sym) => end_str.ends_with(sym.as_str()), Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit), }) @@ -333,26 +333,32 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { match attr.kind { AttrKind::Normal(..) => { if let Some(ident) = attr.ident() { - // TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of - // refactoring // NOTE: This will likely have false positives, like `allow = 1` - ( - Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]), - Pat::Str(""), - ) + let ident_string = ident.to_string(); + if attr.style == AttrStyle::Outer { + ( + Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]), + Pat::Str(""), + ) + } else { + ( + Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]), + Pat::Str(""), + ) + } } else { (Pat::Str("#"), Pat::Str("]")) } }, AttrKind::DocComment(_kind @ CommentKind::Line, ..) => { - if matches!(attr.style, AttrStyle::Outer) { + if attr.style == AttrStyle::Outer { (Pat::Str("///"), Pat::Str("")) } else { (Pat::Str("//!"), Pat::Str("")) } }, AttrKind::DocComment(_kind @ CommentKind::Block, ..) => { - if matches!(attr.style, AttrStyle::Outer) { + if attr.style == AttrStyle::Outer { (Pat::Str("/**"), Pat::Str("*/")) } else { (Pat::Str("/*!"), Pat::Str("*/")) @@ -429,11 +435,12 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: ImplItem<'_>) => impl_item_ impl_with_search_pat!((_cx: LateContext<'tcx>, self: FieldDef<'_>) => field_def_search_pat(self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Variant<'_>) => variant_search_pat(self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ty<'_>) => ty_search_pat(self)); -impl_with_search_pat!((_cx: LateContext<'tcx>, self: Attribute) => attr_search_pat(self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ident) => ident_search_pat(*self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&self.node)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self)); +impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self)); + impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) { type Context = LateContext<'cx>; diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 67c31abbdda..24a02c7ef87 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -1,3 +1,7 @@ +//! A simple const eval API, for use on arbitrary HIR expressions. +//! +//! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to +//! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] use crate::macros::HirNode; @@ -379,6 +383,8 @@ impl Ord for FullInt { /// The context required to evaluate a constant expression. /// /// This is currently limited to constant folding and reading the value of named constants. +/// +/// See the module level documentation for some context. pub struct ConstEvalCtxt<'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 4877fb65d37..ddb7a6635e0 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -9,6 +9,8 @@ //! ~The `INTERNAL_METADATA_COLLECTOR` lint use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, SubdiagMessage}; +#[cfg(debug_assertions)] +use rustc_errors::{EmissionGuarantee, SubstitutionPart, Suggestions}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::Span; @@ -28,6 +30,43 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { } } +/// Makes sure that a diagnostic is well formed. +/// +/// rustc debug asserts a few properties about spans, +/// but the clippy repo uses a distributed rustc build with debug assertions disabled, +/// so this has historically led to problems during subtree syncs where those debug assertions +/// only started triggered there. +/// +/// This function makes sure we also validate them in debug clippy builds. +#[cfg(debug_assertions)] +fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { + let suggestions = match &diag.suggestions { + Suggestions::Enabled(suggs) => &**suggs, + Suggestions::Sealed(suggs) => &**suggs, + Suggestions::Disabled => return, + }; + + for substitution in suggestions.iter().flat_map(|s| &s.substitutions) { + assert_eq!( + substitution + .parts + .iter() + .find(|SubstitutionPart { snippet, span }| snippet.is_empty() && span.is_empty()), + None, + "span must not be empty and have no suggestion" + ); + + assert_eq!( + substitution + .parts + .array_windows() + .find(|[a, b]| a.span.overlaps(b.span)), + None, + "suggestion must not have overlapping parts" + ); + } +} + /// Emit a basic lint message with a `msg` and a `span`. /// /// This is the most primitive of our lint emission methods and can @@ -64,6 +103,9 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult cx.span_lint(lint, sp, |diag| { diag.primary_message(msg); docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -118,6 +160,9 @@ pub fn span_lint_and_help<T: LintContext>( diag.help(help.into()); } docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -175,6 +220,9 @@ pub fn span_lint_and_note<T: LintContext>( diag.note(note.into()); } docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -208,6 +256,9 @@ where diag.primary_message(msg); f(diag); docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -240,6 +291,9 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s cx.tcx.node_span_lint(lint, hir_id, sp, |diag| { diag.primary_message(msg); docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -280,6 +334,9 @@ pub fn span_lint_hir_and_then( diag.primary_message(msg); f(diag); docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -316,7 +373,7 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[expect(clippy::collapsible_span_lint_calls)] +#[cfg_attr(not(debug_assertions), expect(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg<T: LintContext>( cx: &T, lint: &'static Lint, @@ -328,5 +385,8 @@ pub fn span_lint_and_sugg<T: LintContext>( ) { span_lint_and_then(cx, lint, sp, msg.into(), |diag| { diag.span_suggestion(sp, help.into(), sugg, applicability); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 3175a9a1dd3..11bbe734844 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -103,7 +103,7 @@ impl<'hir> IfLet<'hir> { /// Parses an `if let` expression pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> { if let ExprKind::If( - Expr { + &Expr { kind: ExprKind::Let(&hir::LetExpr { pat: let_pat, @@ -381,12 +381,12 @@ impl<'hir> WhileLet<'hir> { /// Parses a desugared `while let` loop pub const fn hir(expr: &Expr<'hir>) -> Option<Self> { if let ExprKind::Loop( - Block { + &Block { expr: - Some(Expr { + Some(&Expr { kind: ExprKind::If( - Expr { + &Expr { kind: ExprKind::Let(&hir::LetExpr { pat: let_pat, diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 8004bc68b2e..cb69f8e5a0e 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -128,7 +128,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { self.inter_expr().eq_path_segments(left, right) } - pub fn eq_modifiers(&mut self, left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool { + pub fn eq_modifiers(left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool { std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness) && std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity) } @@ -1201,11 +1201,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); self.hash_pat(pat); }, - TyKind::Ptr(ref mut_ty) => { + TyKind::Ptr(mut_ty) => { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, - TyKind::Ref(lifetime, ref mut_ty) => { + TyKind::Ref(lifetime, mut_ty) => { self.hash_lifetime(lifetime); self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); @@ -1230,14 +1230,19 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); } }, - TyKind::Path(ref qpath) => self.hash_qpath(qpath), + TyKind::Path(qpath) => self.hash_qpath(qpath), TyKind::TraitObject(_, lifetime, _) => { self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); }, - TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::OpaqueDef(_) | TyKind::AnonAdt(_) => {}, + TyKind::Err(_) + | TyKind::Infer + | TyKind::Never + | TyKind::InferDelegation(..) + | TyKind::OpaqueDef(_) + | TyKind::AnonAdt(_) => {}, } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index ad85dfa2d1e..50d334acf18 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -9,6 +9,7 @@ #![feature(rustc_private)] #![feature(assert_matches)] #![feature(unwrap_infallible)] +#![feature(array_windows)] #![recursion_limit = "512"] #![allow( clippy::missing_errors_doc, @@ -688,11 +689,11 @@ pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> { /// /// This function is expensive and should be used sparingly. pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> { - let (base, path) = match *path { + let (base, path) = match path { [primitive] => { return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; }, - [base, ref path @ ..] => (base, path), + [base, path @ ..] => (base, path), _ => return Vec::new(), }; @@ -744,7 +745,7 @@ pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[& } /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. -pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> { +pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> { def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id()) } @@ -934,7 +935,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } }, ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func), - ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg), + ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, @@ -947,7 +948,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & { match arg.kind { ExprKind::Lit(hir::Lit { - node: LitKind::Str(ref sym, _), + node: LitKind::Str(sym, _), .. }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), @@ -1209,8 +1210,8 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<' let capture = match capture.info.capture_kind { UpvarCapture::ByValue => CaptureKind::Value, UpvarCapture::ByRef(kind) => match kind { - BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not), - BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => { + BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not), + BorrowKind::UniqueImmutable | BorrowKind::Mutable => { CaptureKind::Ref(Mutability::Mut) }, }, @@ -1337,15 +1338,17 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> { pub struct ContainsName<'a, 'tcx> { pub cx: &'a LateContext<'tcx>, pub name: Symbol, - pub result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; - fn visit_name(&mut self, name: Symbol) { + fn visit_name(&mut self, name: Symbol) -> Self::Result { if self.name == name { - self.result = true; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } @@ -1356,13 +1359,8 @@ impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> { /// Checks if an `Expr` contains a certain name. pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { - let mut cn = ContainsName { - name, - result: false, - cx, - }; - cn.visit_expr(expr); - cn.result + let mut cn = ContainsName { cx, name }; + cn.visit_expr(expr).is_break() } /// Returns `true` if `expr` contains a return expression @@ -3340,8 +3338,8 @@ pub fn get_path_from_caller_to_method_type<'tcx>( let assoc_item = tcx.associated_item(method); let def_id = assoc_item.container_id(tcx); match assoc_item.container { - rustc_ty::TraitContainer => get_path_to_callee(tcx, from, def_id), - rustc_ty::ImplContainer => { + rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id), + rustc_ty::AssocItemContainer::Impl => { let ty = tcx.type_of(def_id).instantiate_identity(); get_path_to_ty(tcx, from, ty, args) }, @@ -3459,7 +3457,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { matches!( cx.tcx.parent_hir_node(id), - Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }) + Node::Stmt(..) | Node::Block(Block { stmts: [], .. }) ) } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 8f9f75d6824..666ec8df930 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -404,7 +404,7 @@ fn is_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { } fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { - // FIXME(effects, fee1-dead) revert to const destruct once it works again + // FIXME(const_trait_impl, fee1-dead) revert to const destruct once it works again #[expect(unused)] fn is_ty_const_destruct_unused<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { // Avoid selecting for simple cases, such as builtin types. @@ -412,7 +412,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> return true; } - // FIXME(effects) constness + // FIXME(const_trait_impl) constness let obligation = Obligation::new( tcx, ObligationCause::dummy_with_span(body.span), diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 41785e161d0..ce1a20e0066 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -19,7 +19,7 @@ use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, TypingMode, + TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -274,11 +274,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into())) .collect::<Vec<_>>(); - let trait_ref = TraitRef::new( - tcx, - trait_id, - [GenericArg::from(ty)].into_iter().chain(args), - ); + let trait_ref = TraitRef::new(tcx, trait_id, [GenericArg::from(ty)].into_iter().chain(args)); debug_assert_matches!( tcx.def_kind(trait_id), @@ -975,9 +971,7 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) { (Ok(size), _) => size, (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(), - (Err(_), ty::Array(t, n)) => { - n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t) - }, + (Err(_), ty::Array(t, n)) => n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t), (Err(_), ty::Adt(def, subst)) if def.is_struct() => def .variants() .iter() @@ -1284,7 +1278,12 @@ pub fn make_normalized_projection_with_regions<'tcx>( pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let cause = ObligationCause::dummy(); - match tcx.infer_ctxt().build(TypingMode::from_param_env(param_env)).at(&cause, param_env).query_normalize(ty) { + match tcx + .infer_ctxt() + .build(TypingMode::from_param_env(param_env)) + .at(&cause, param_env) + .query_normalize(ty) + { Ok(ty) => ty.value, Err(_) => ty, } diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 3021f21df12..d7640ebfb00 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -302,9 +302,9 @@ fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bo // Check that all type parameters appear in the functions input types. (0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| { fn_sig - .inputs() - .iter() - .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) + .inputs() + .iter() + .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) }) } diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 8af3bdccaa1..c8c25456f69 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -67,7 +67,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) { - if bk == ty::BorrowKind::MutBorrow { + if bk == ty::BorrowKind::Mutable { self.update(cmt); } } @@ -109,34 +109,28 @@ impl<'tcx> Visitor<'tcx> for ParamBindingIdCollector { pub struct BindingUsageFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, binding_ids: Vec<HirId>, - usage_found: bool, } impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool { let mut finder = BindingUsageFinder { cx, binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body), - usage_found: false, }; - finder.visit_body(body); - finder.usage_found + finder.visit_body(body).is_break() } } impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if !self.usage_found { - intravisit::walk_expr(self, expr); - } - } - - fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result { if let Res::Local(id) = path.res { if self.binding_ids.contains(&id) { - self.usage_found = true; + return ControlFlow::Break(()); } } + + ControlFlow::Continue(()) } fn nested_visit_map(&mut self) -> Self::Map { diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 8db6502dbfb..8f5ec185bf1 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -324,17 +324,15 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, - is_const: bool, } + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = intravisit::nested_filter::None; - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if !self.is_const { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { match e.kind { - ExprKind::ConstBlock(_) => return, + ExprKind::ConstBlock(_) => return ControlFlow::Continue(()), ExprKind::Call( &Expr { kind: ExprKind::Path(ref p), @@ -394,37 +392,34 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> | ExprKind::Type(..) => (), _ => { - self.is_const = false; - return; + return ControlFlow::Break(()); }, } - walk_expr(self, e); + + walk_expr(self, e) } } - let mut v = V { cx, is_const: true }; - v.visit_expr(e); - v.is_const + let mut v = V { cx }; + v.visit_expr(e).is_continue() } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, - is_unsafe: bool, } impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; + type Result = ControlFlow<()>; + fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if self.is_unsafe { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { match e.kind { ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => { - self.is_unsafe = true; + ControlFlow::Break(()) }, ExprKind::MethodCall(..) if self @@ -435,13 +430,13 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe }) => { - self.is_unsafe = true; + ControlFlow::Break(()) }, ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() { ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => { - self.is_unsafe = true; + ControlFlow::Break(()) }, - ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true, + ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => ControlFlow::Break(()), _ => walk_expr(self, e), }, ExprKind::Path(ref p) @@ -451,56 +446,54 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { .opt_def_id() .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) => { - self.is_unsafe = true; + ControlFlow::Break(()) }, _ => walk_expr(self, e), } } - fn visit_block(&mut self, b: &'tcx Block<'_>) { - if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) { - walk_block(self, b); + fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result { + if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) { + ControlFlow::Continue(()) + } else { + walk_block(self, b) } } - fn visit_nested_item(&mut self, id: ItemId) { - if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind { - self.is_unsafe = i.safety == Safety::Unsafe; + fn visit_nested_item(&mut self, id: ItemId) -> Self::Result { + if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind + && i.safety == Safety::Unsafe + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } } - let mut v = V { cx, is_unsafe: false }; - v.visit_expr(e); - v.is_unsafe + let mut v = V { cx }; + v.visit_expr(e).is_break() } /// Checks if the given expression contains an unsafe block pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, - found_unsafe: bool, } impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_block(&mut self, b: &'tcx Block<'_>) { - if self.found_unsafe { - return; - } + fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result { if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { - self.found_unsafe = true; - return; + ControlFlow::Break(()) + } else { + walk_block(self, b) } - walk_block(self, b); } } - let mut v = V { - cx, - found_unsafe: false, - }; - v.visit_expr(e); - v.found_unsafe + let mut v = V { cx }; + v.visit_expr(e).is_break() } /// Runs the given function for each sub-expression producing the final value consumed by the parent diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index e11ab40b9de..37d9cce2465 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-10-18" +channel = "nightly-2024-11-07" 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 5774e20e0be..b8e0413e97b 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -396,7 +396,8 @@ struct Renderer<'a> { impl Renderer<'_> { fn markdown(input: &str) -> Safe<String> { - let parser = Parser::new_ext(input, Options::all()); + let input = clippy_config::sanitize_explanation(input); + let parser = Parser::new_ext(&input, Options::all()); let mut html_output = String::new(); html::push_html(&mut html_output, parser); // Oh deer, what a hack :O diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs index 1fd03cfe36d..d59e9cbbb61 100644 --- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs +++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs @@ -7,7 +7,7 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; extern crate rustc_lint; -use rustc_lint::LintPass; +use rustc_lint::{LintPass, LintVec}; declare_tool_lint! { pub clippy::TEST_LINT, @@ -35,6 +35,9 @@ impl LintPass for Pass { fn name(&self) -> &'static str { "TEST_LINT" } + fn get_lints(&self) -> LintVec { + vec![TEST_LINT] + } } declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]); diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed new file mode 100644 index 00000000000..2cbd646a0fd --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed @@ -0,0 +1,24 @@ +#![feature(rustc_private)] +#![warn(clippy::slow_symbol_comparisons)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::Symbol; + +fn main() { + let symbol = sym!(example); + let other_symbol = sym!(other_example); + + // Should lint + let slow_comparison = symbol.as_str() == "example"; + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_macro = symbol.as_str() == "example"; + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_backwards = symbol.as_str() == "example"; + //~^ error: comparing `Symbol` via `Symbol::intern` + + // Should not lint + let faster_comparison = symbol.as_str() == "other_example"; + let preinterned_comparison = symbol == other_symbol; +} diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs new file mode 100644 index 00000000000..0cea3c3fcff --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs @@ -0,0 +1,24 @@ +#![feature(rustc_private)] +#![warn(clippy::slow_symbol_comparisons)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::Symbol; + +fn main() { + let symbol = sym!(example); + let other_symbol = sym!(other_example); + + // Should lint + let slow_comparison = symbol == Symbol::intern("example"); + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_macro = symbol == sym!(example); + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_backwards = sym!(example) == symbol; + //~^ error: comparing `Symbol` via `Symbol::intern` + + // Should not lint + let faster_comparison = symbol.as_str() == "other_example"; + let preinterned_comparison = symbol == other_symbol; +} diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr new file mode 100644 index 00000000000..72cb20a7fed --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr @@ -0,0 +1,23 @@ +error: comparing `Symbol` via `Symbol::intern` + --> tests/ui-internal/slow_symbol_comparisons.rs:14:27 + | +LL | let slow_comparison = symbol == Symbol::intern("example"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` + | + = note: `-D clippy::slow-symbol-comparisons` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::slow_symbol_comparisons)]` + +error: comparing `Symbol` via `Symbol::intern` + --> tests/ui-internal/slow_symbol_comparisons.rs:16:33 + | +LL | let slow_comparison_macro = symbol == sym!(example); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` + +error: comparing `Symbol` via `Symbol::intern` + --> tests/ui-internal/slow_symbol_comparisons.rs:18:37 + | +LL | let slow_comparison_backwards = sym!(example) == symbol; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed index eb79fdbc4b4..8e7f020c1f6 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed @@ -1,6 +1,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::slow_symbol_comparisons, clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs index bbea13af92a..9aeeb9aaf3a 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs @@ -1,6 +1,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::slow_symbol_comparisons, clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr index 551167a9ff5..668c11722f9 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr @@ -1,5 +1,5 @@ error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:15:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:16:5 | LL | Symbol::intern("foo").as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` @@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:16:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 | LL | Symbol::intern("foo").to_string() == "self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 | LL | &*Ident::empty().as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 | LL | "clippy" == Ident::empty().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml new file mode 100644 index 00000000000..fbdb0c9c37d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml @@ -0,0 +1 @@ +trait-assoc-item-kinds-order = ["fn", "type", "const", "type"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml new file mode 100644 index 00000000000..720655e5cce --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml @@ -0,0 +1 @@ +trait-assoc-item-kinds-order = ["const", "type"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml new file mode 100644 index 00000000000..0dd5407be10 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["enum", "impl", "module", "struct", "trait", "struct"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml new file mode 100644 index 00000000000..ddca5cfa577 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml @@ -0,0 +1,12 @@ +trait-assoc-item-kinds-order = ["const", "type", "fn"] +source-item-ordering = ["enum", "impl", "module", "struct", "trait"] +module-item-order-groupings = [ + ["modules", ["extern_crate", "mod", "foreign_mod"]], + ["use", ["use"]], + ["macros", ["macro"]], + ["global_asm", ["global_asm"]], + ["UPPER_SNAKE_CASE", ["static", "const"]], + ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], + ["lower_snake_case", ["fn"]] +] + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml new file mode 100644 index 00000000000..2144bdc9a0c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["enum"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml new file mode 100644 index 00000000000..54b6727fabf --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["impl"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml new file mode 100644 index 00000000000..b551611c35e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["trait"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr new file mode 100644 index 00000000000..e441c7c1241 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type] + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml:1:32 + | +LL | trait-assoc-item-kinds-order = ["fn", "type", "const", "type"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr new file mode 100644 index 00000000000..183f0b03319 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type] + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml:1:32 + | +LL | trait-assoc-item-kinds-order = ["const", "type"] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr new file mode 100644 index 00000000000..abf58dbd110 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: The category "Struct" was enabled more than once in the source ordering configuration. + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml:1:24 + | +LL | source-item-ordering = ["enum", "impl", "module", "struct", "trait", "struct"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs new file mode 100644 index 00000000000..e9bcc30c15e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs @@ -0,0 +1,186 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp +//@[bad_conf_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1 +//@[bad_conf_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2 +//@[bad_conf_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3 + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const AFTER: i8 = 0; + + const BEFORE: i8 = 0; +} + +/// Since the upper module passes linting, the lint now recurses into this module. +mod this_is_in_the_wrong_position { + const A: i8 = 1; + const C: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::{Rc, Weak}; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + B, + C, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + B, + C, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl BasicEmptyTrait for StructOrdered {} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Clone, Default)] +struct StructUnordered { + a: bool, + b: bool, + c: bool, + d: bool, +} + +impl TraitUnordered for StructUnordered { + const A: bool = false; + const B: bool = false; + const C: bool = false; + + type SomeType = (); + + fn a() {} + fn b() {} + fn c() {} +} + +impl TraitUnorderedItemKinds for StructUnordered { + const A: bool = false; + const B: bool = false; + const C: bool = false; + + type SomeType = (); + + fn a() {} + fn b() {} + fn c() {} +} + +struct StructUnorderedGeneric<T> { + _1: std::marker::PhantomData<T>, + a: bool, + b: bool, + c: bool, + d: bool, +} + +trait TraitOrdered { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +trait TraitUnordered { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +trait TraitUnorderedItemKinds { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +#[derive(std::clone::Clone, Default)] +struct ZisShouldBeBeforeZeMainFn; + +fn main() { + // test code goes here +} + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs new file mode 100644 index 00000000000..0fccbd4790b --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs @@ -0,0 +1,174 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: var_1 +//@[var_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/var_1 + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const AFTER: i8 = 0; + + const BEFORE: i8 = 0; +} + +/// Since the upper module passes linting, the lint now recurses into this module. +mod this_is_in_the_wrong_position { + const A: i8 = 1; + const C: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::{Rc, Weak}; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + B, + C, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + B, + C, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl BasicEmptyTrait for StructOrdered {} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Clone, Default)] +struct StructUnordered { + a: bool, + b: bool, + c: bool, + d: bool, +} + +impl TraitUnordered for StructUnordered { + fn a() {} + fn b() {} + fn c() {} + + type SomeType = (); + + const A: bool = false; + const B: bool = false; + const C: bool = false; +} + +impl TraitUnorderedItemKinds for StructUnordered { + fn a() {} + + type SomeType = (); + + const A: bool = false; +} + +struct StructUnorderedGeneric<T> { + _1: std::marker::PhantomData<T>, + a: bool, + b: bool, + c: bool, + d: bool, +} + +trait TraitOrdered { + fn a(); + fn b(); + fn c(); + + type SomeType; + + const A: bool; + const B: bool; + const C: bool; +} + +trait TraitUnordered { + fn a(); + fn b(); + fn c(); + + type SomeType; + + const A: bool; + const B: bool; + const C: bool; +} + +trait TraitUnorderedItemKinds { + fn a(); + + type SomeType; + + const A: bool; +} + +#[derive(std::clone::Clone, Default)] +struct ZisShouldBeBeforeZeMainFn; + +fn main() { + // test code goes here +} + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr new file mode 100644 index 00000000000..062bf25ea62 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr @@ -0,0 +1,226 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:21:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:19:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:64:1 + | +LL | / impl CloneSelf for StructOrdered { +LL | | fn clone_self(&self) -> Self { +LL | | Self { +LL | | a: true, +... | +LL | | } +LL | | } + | |_^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:54:1 + | +LL | / impl Default for StructOrdered { +LL | | fn default() -> Self { +LL | | Self { +LL | | a: true, +... | +LL | | } +LL | | } + | |_^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:151:1 + | +LL | impl BasicEmptyTrait for StructOrdered {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:138:1 + | +LL | / impl TraitUnordered for StructUnordered { +LL | | const A: bool = false; +LL | | const C: bool = false; +LL | | const B: bool = false; +... | +LL | | fn b() {} +LL | | } + | |_^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:176:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 + | +LL | const AFTER: i8 = 0; + | ^^^^^ + | +note: should be placed before `BEFORE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:10:11 + | +LL | const BEFORE: i8 = 0; + | ^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:38:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:37:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:88:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:87:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:115:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:114:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:127:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:146:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:156:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:172:11 + | +LL | const A: i8 = 1; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 + | +LL | const C: i8 = 0; + | ^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs new file mode 100644 index 00000000000..2765bf935b7 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs @@ -0,0 +1,185 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const BEFORE: i8 = 0; + + const AFTER: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::Rc; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +use std::rc::Weak; + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + C, + B, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + C, + B, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Default, Clone)] +struct StructUnordered { + a: bool, + c: bool, + b: bool, + d: bool, +} + +struct StructUnorderedGeneric<T> { + _1: std::marker::PhantomData<T>, + a: bool, + c: bool, + b: bool, + d: bool, +} + +trait TraitOrdered { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +trait TraitUnordered { + const A: bool; + const C: bool; + const B: bool; + + type SomeType; + + fn a(); + fn c(); + fn b(); +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + const B: bool; + const C: bool; + + fn a(); + fn b(); + fn c(); +} + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +impl TraitUnordered for StructUnordered { + const A: bool = false; + const C: bool = false; + const B: bool = false; + + type SomeType = (); + + fn a() {} + fn c() {} + fn b() {} +} + +// Trait impls should be located just after the type they implement it for. +impl BasicEmptyTrait for StructOrdered {} + +impl TraitUnorderedItemKinds for StructUnordered { + type SomeType = (); + + const A: bool = false; + const B: bool = false; + const C: bool = false; + + fn a() {} + fn b() {} + fn c() {} +} + +fn main() { + // test code goes here +} + +/// Note that the linting pass is stopped before recursing into this module. +mod this_is_in_the_wrong_position { + const C: i8 = 0; + const A: i8 = 1; +} + +#[derive(Default, std::clone::Clone)] +struct ZisShouldBeBeforeZeMainFn; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs new file mode 100644 index 00000000000..44902336573 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs @@ -0,0 +1,174 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: var_1 +//@[var_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/var_1 + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const AFTER: i8 = 0; + + const BEFORE: i8 = 0; +} + +/// Since the upper module passes linting, the lint now recurses into this module. +mod this_is_in_the_wrong_position { + const A: i8 = 1; + const C: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::{Rc, Weak}; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + B, + C, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + B, + C, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl BasicEmptyTrait for StructOrdered {} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Clone, Default)] +struct StructUnordered { + a: bool, + b: bool, + c: bool, + d: bool, +} + +impl TraitUnordered for StructUnordered { + fn a() {} + fn c() {} + fn b() {} + + type SomeType = (); + + const A: bool = false; + const C: bool = false; + const B: bool = false; +} + +impl TraitUnorderedItemKinds for StructUnordered { + const A: bool = false; + + type SomeType = (); + + fn a() {} +} + +struct StructUnorderedGeneric<T> { + _1: std::marker::PhantomData<T>, + a: bool, + b: bool, + c: bool, + d: bool, +} + +trait TraitOrdered { + fn a(); + fn b(); + fn c(); + + type SomeType; + + const A: bool; + const B: bool; + const C: bool; +} + +trait TraitUnordered { + fn a(); + fn c(); + fn b(); + + type SomeType; + + const A: bool; + const C: bool; + const B: bool; +} + +trait TraitUnorderedItemKinds { + const A: bool; + + type SomeType; + + fn a(); +} + +#[derive(std::clone::Clone, Default)] +struct ZisShouldBeBeforeZeMainFn; + +fn main() { + // test code goes here +} + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr new file mode 100644 index 00000000000..f31f7f68c17 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr @@ -0,0 +1,100 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:105:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:104:8 + | +LL | fn c() {} + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:111:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:110:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of impl items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:117:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `A` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:115:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of impl items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:119:5 + | +LL | fn a() {} + | ^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:117:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:145:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:144:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:151:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:150:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of trait items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:157:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `A` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:155:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of trait items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:159:5 + | +LL | fn a(); + | ^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:157:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr new file mode 100644 index 00000000000..57069fd8fee --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr @@ -0,0 +1,16 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs:22:5 + | +LL | A, + | ^ + | +note: should be placed before `B` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs:21:5 + | +LL | B, + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs new file mode 100644 index 00000000000..e8002c4a163 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs @@ -0,0 +1,43 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: only_enum +//@[only_enum] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_enum + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +fn main() {} + +struct StructUnordered { + b: bool, + a: bool, +} + +enum EnumOrdered { + A, + B, +} + +enum EnumUnordered { + B, + A, +} + +trait TraitUnordered { + const B: bool; + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + + fn a(); +} + +const ZIS_SHOULD_BE_AT_THE_TOP: () = (); diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr new file mode 100644 index 00000000000..40348ecbdae --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr @@ -0,0 +1,40 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:43:8 + | +LL | fn a() {} + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:42:8 + | +LL | fn b() {} + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:45:5 + | +LL | type SomeType = i8; + | ^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `a` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:43:5 + | +LL | fn a() {} + | ^^^^^^^^^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:47:5 + | +LL | const A: bool = true; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:45:5 + | +LL | type SomeType = i8; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs new file mode 100644 index 00000000000..bd969c865b5 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs @@ -0,0 +1,67 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: only_impl +//@[only_impl] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_impl + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +fn main() {} + +struct StructUnordered { + b: bool, + a: bool, +} + +struct BasicStruct {} + +trait BasicTrait { + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +enum EnumUnordered { + B, + A, +} + +trait TraitUnordered { + const B: bool; + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +impl BasicTrait for StructUnordered { + fn b() {} + fn a() {} + + type SomeType = i8; + + const A: bool = true; +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + + fn a(); +} + +const ZIS_SHOULD_BE_AT_THE_TOP: () = (); + +impl BasicTrait for BasicStruct { + const A: bool = true; + + type SomeType = i8; + + fn a() {} + fn b() {} +} diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr new file mode 100644 index 00000000000..9b86ebd48e3 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr @@ -0,0 +1,40 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:32:11 + | +LL | const A: bool; + | ^ + | +note: should be placed before `B` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:31:11 + | +LL | const B: bool; + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:37:8 + | +LL | fn a(); + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:36:8 + | +LL | fn b(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:43:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:41:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs new file mode 100644 index 00000000000..979a52ecb10 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs @@ -0,0 +1,48 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: only_trait +//@[only_trait] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_trait + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +fn main() {} + +struct StructUnordered { + b: bool, + a: bool, +} + +trait TraitOrdered { + const A: bool; + const B: bool; + + type SomeType; + + fn a(); + fn b(); +} + +enum EnumUnordered { + B, + A, +} + +trait TraitUnordered { + const B: bool; + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + + fn a(); +} + +const ZIS_SHOULD_BE_AT_THE_TOP: () = (); diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml new file mode 100644 index 00000000000..bede035e388 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml @@ -0,0 +1 @@ +trait-assoc-item-kinds-order = ["fn", "type", "const"] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr index ddeb2f8cc70..2e3386628b4 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -1,11 +1,39 @@ +error: use of a disallowed macro `std::vec` + --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:16:5 + | +LL | vec![1, 2, 3]; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-macros` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]` + +error: use of a disallowed macro `serde::Serialize` + --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:18:14 + | +LL | #[derive(Serialize)] + | ^^^^^^^^^ + | + = note: no serializing + +error: use of a disallowed macro `macros::attr` + --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:31:1 + | +LL | / macros::attr! { +LL | | struct S; +LL | | } + | |_^ + +error: use of a disallowed macro `proc_macros::Derive` + --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:47:10 + | +LL | #[derive(Derive)] + | ^^^^^^ + error: use of a disallowed macro `std::println` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:13:5 | LL | println!("one"); | ^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-macros` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]` error: use of a disallowed macro `std::println` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:14:5 @@ -19,20 +47,6 @@ error: use of a disallowed macro `std::cfg` LL | cfg!(unix); | ^^^^^^^^^^ -error: use of a disallowed macro `std::vec` - --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:16:5 - | -LL | vec![1, 2, 3]; - | ^^^^^^^^^^^^^ - -error: use of a disallowed macro `serde::Serialize` - --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:18:14 - | -LL | #[derive(Serialize)] - | ^^^^^^^^^ - | - = note: no serializing - error: use of a disallowed macro `macros::expr` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:21:13 | @@ -69,14 +83,6 @@ error: use of a disallowed macro `macros::binop` LL | let _ = macros::binop!(1); | ^^^^^^^^^^^^^^^^^ -error: use of a disallowed macro `macros::attr` - --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:31:1 - | -LL | / macros::attr! { -LL | | struct S; -LL | | } - | |_^ - error: use of a disallowed macro `macros::item` --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:36:5 | @@ -95,11 +101,5 @@ error: use of a disallowed macro `macros::item` LL | macros::item!(); | ^^^^^^^^^^^^^^^ -error: use of a disallowed macro `proc_macros::Derive` - --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:47:10 - | -LL | #[derive(Derive)] - | ^^^^^^ - error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs index f3dbb6ad1cf..dc9349f75a0 100644 --- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs @@ -1,8 +1,8 @@ #![warn(clippy::large_include_file)] // Good -const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs"); -const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs"); +const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs"); +const GOOD_INCLUDE_STR: &str = include_str!("../../ui/author.rs"); #[allow(clippy::large_include_file)] const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); @@ -11,6 +11,9 @@ const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); // Bad const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +//~^ large_include_file const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); +//~^ large_include_file +#[doc = include_str!("too_big.txt")] //~ large_include_file fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr index 34224065f07..9e1494a47bb 100644 --- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr @@ -9,12 +9,20 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]` error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:14:35 + --> tests/ui-toml/large_include_file/large_include_file.rs:15:35 | LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the configuration allows a maximum size of 600 bytes -error: aborting due to 2 previous errors +error: attempted to include a large file + --> tests/ui-toml/large_include_file/large_include_file.rs:18:1 + | +LL | #[doc = include_str!("too_big.txt")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the configuration allows a maximum size of 600 bytes + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs index 79c8751468d..c5d00c91b05 100644 --- a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs +++ b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs @@ -51,4 +51,10 @@ pub mod __macro { } } +#[warn(clippy::missing_errors_doc)] +#[test] +fn test() -> Result<(), ()> { + Ok(()) +} + fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 5cf9c0fb271..6fa583fc041 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -54,12 +54,14 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect max-trait-bounds min-ident-chars-threshold missing-docs-in-crate-items + module-item-order-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold + source-item-ordering stack-size-threshold standard-macro-braces struct-field-name-threshold @@ -68,6 +70,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect too-large-for-stack too-many-arguments-threshold too-many-lines-threshold + trait-assoc-item-kinds-order trivial-copy-size-limit type-complexity-threshold unnecessary-box-size @@ -138,12 +141,14 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect max-trait-bounds min-ident-chars-threshold missing-docs-in-crate-items + module-item-order-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold + source-item-ordering stack-size-threshold standard-macro-braces struct-field-name-threshold @@ -152,6 +157,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect too-large-for-stack too-many-arguments-threshold too-many-lines-threshold + trait-assoc-item-kinds-order trivial-copy-size-limit type-complexity-threshold unnecessary-box-size @@ -222,12 +228,14 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni max-trait-bounds min-ident-chars-threshold missing-docs-in-crate-items + module-item-order-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold + source-item-ordering stack-size-threshold standard-macro-braces struct-field-name-threshold @@ -236,6 +244,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni too-large-for-stack too-many-arguments-threshold too-many-lines-threshold + trait-assoc-item-kinds-order trivial-copy-size-limit type-complexity-threshold unnecessary-box-size diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed index 058dbb77a32..8f6c962e2f5 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.fixed +++ b/src/tools/clippy/tests/ui/allow_attributes.fixed @@ -1,4 +1,5 @@ //@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs #![allow(unused)] #![warn(clippy::allow_attributes)] #![no_main] @@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option<u8> { allow?; Some(42) } + +// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc +// macro check +#[repr(transparent)] +#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr +struct IgnoreDerived; diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs index 6d94ce50e4c..cb6c4dcf715 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.rs +++ b/src/tools/clippy/tests/ui/allow_attributes.rs @@ -1,4 +1,5 @@ //@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs #![allow(unused)] #![warn(clippy::allow_attributes)] #![no_main] @@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option<u8> { allow?; Some(42) } + +// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc +// macro check +#[repr(transparent)] +#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr +struct IgnoreDerived; diff --git a/src/tools/clippy/tests/ui/allow_attributes.stderr b/src/tools/clippy/tests/ui/allow_attributes.stderr index 10dac0bc808..5a4ff287acd 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.stderr +++ b/src/tools/clippy/tests/ui/allow_attributes.stderr @@ -1,5 +1,5 @@ error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:12:3 + --> tests/ui/allow_attributes.rs:13:3 | LL | #[allow(dead_code)] | ^^^^^ help: replace it with: `expect` @@ -8,24 +8,16 @@ LL | #[allow(dead_code)] = help: to override `-D warnings` add `#[allow(clippy::allow_attributes)]` error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:21:30 + --> tests/ui/allow_attributes.rs:22:30 | LL | #[cfg_attr(panic = "unwind", allow(dead_code))] | ^^^^^ help: replace it with: `expect` error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:52:7 + --> tests/ui/allow_attributes.rs:53:7 | LL | #[allow(unused)] | ^^^^^ help: replace it with: `expect` -error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:52:7 - | -LL | #[allow(unused)] - | ^^^^^ help: replace it with: `expect` - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr index 86d7845df04..9c1ac5af91b 100644 --- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr +++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr @@ -43,14 +43,5 @@ LL | #[allow(unused)] | = help: try adding a reason at the end with `, reason = ".."` -error: `allow` attribute without specifying a reason - --> tests/ui/allow_attributes_without_reason.rs:46:5 - | -LL | #[allow(unused)] - | ^^^^^^^^^^^^^^^^ - | - = help: try adding a reason at the end with `, reason = ".."` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/attrs.stderr b/src/tools/clippy/tests/ui/attrs.stderr index cd409fc8701..a7fdceaba6f 100644 --- a/src/tools/clippy/tests/ui/attrs.stderr +++ b/src/tools/clippy/tests/ui/attrs.stderr @@ -1,12 +1,3 @@ -error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea - --> tests/ui/attrs.rs:5:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::inline-always` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::inline_always)]` - error: the since field must contain a semver-compliant version --> tests/ui/attrs.rs:27:14 | @@ -22,5 +13,14 @@ error: the since field must contain a semver-compliant version LL | #[deprecated(since = "1")] | ^^^^^^^^^^^ +error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea + --> tests/ui/attrs.rs:5:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-always` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_always)]` + error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-internal/author.rs b/src/tools/clippy/tests/ui/author.rs index eb1f3e3f870..0a1be356896 100644 --- a/src/tools/clippy/tests/ui-internal/author.rs +++ b/src/tools/clippy/tests/ui/author.rs @@ -1,5 +1,3 @@ -#![warn(clippy::author)] - fn main() { #[clippy::author] let x: char = 0x45 as char; diff --git a/src/tools/clippy/tests/ui-internal/author.stdout b/src/tools/clippy/tests/ui/author.stdout index eed704e82fe..eed704e82fe 100644 --- a/src/tools/clippy/tests/ui-internal/author.stdout +++ b/src/tools/clippy/tests/ui/author.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/blocks.rs b/src/tools/clippy/tests/ui/author/blocks.rs index 164f7d0d9d6..164f7d0d9d6 100644 --- a/src/tools/clippy/tests/ui-internal/author/blocks.rs +++ b/src/tools/clippy/tests/ui/author/blocks.rs diff --git a/src/tools/clippy/tests/ui-internal/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index 6bf48d5ba4e..6bf48d5ba4e 100644 --- a/src/tools/clippy/tests/ui-internal/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/call.rs b/src/tools/clippy/tests/ui/author/call.rs index e99c3c41dc4..e99c3c41dc4 100644 --- a/src/tools/clippy/tests/ui-internal/author/call.rs +++ b/src/tools/clippy/tests/ui/author/call.rs diff --git a/src/tools/clippy/tests/ui-internal/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout index 59d4da490fe..59d4da490fe 100644 --- a/src/tools/clippy/tests/ui-internal/author/call.stdout +++ b/src/tools/clippy/tests/ui/author/call.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs index 946088ab346..946088ab346 100644 --- a/src/tools/clippy/tests/ui-internal/author/if.rs +++ b/src/tools/clippy/tests/ui/author/if.rs diff --git a/src/tools/clippy/tests/ui-internal/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index a85dcddd331..a85dcddd331 100644 --- a/src/tools/clippy/tests/ui-internal/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/issue_3849.rs b/src/tools/clippy/tests/ui/author/issue_3849.rs index 5f65746d71f..5f65746d71f 100644 --- a/src/tools/clippy/tests/ui-internal/author/issue_3849.rs +++ b/src/tools/clippy/tests/ui/author/issue_3849.rs diff --git a/src/tools/clippy/tests/ui-internal/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout index a5a8c0304ee..a5a8c0304ee 100644 --- a/src/tools/clippy/tests/ui-internal/author/issue_3849.stdout +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/loop.rs b/src/tools/clippy/tests/ui/author/loop.rs index ff5b6100117..ff5b6100117 100644 --- a/src/tools/clippy/tests/ui-internal/author/loop.rs +++ b/src/tools/clippy/tests/ui/author/loop.rs diff --git a/src/tools/clippy/tests/ui-internal/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index 609d2491061..609d2491061 100644 --- a/src/tools/clippy/tests/ui-internal/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs b/src/tools/clippy/tests/ui/author/macro_in_closure.rs index 444e6a12165..444e6a12165 100644 --- a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.rs diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout index 66caf382d89..66caf382d89 100644 --- a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs b/src/tools/clippy/tests/ui/author/macro_in_loop.rs index 8a520501f8d..8a520501f8d 100644 --- a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.rs diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout index 3f9be297c33..3f9be297c33 100644 --- a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/matches.rs b/src/tools/clippy/tests/ui/author/matches.rs index 674e07ec2d3..674e07ec2d3 100644 --- a/src/tools/clippy/tests/ui-internal/author/matches.rs +++ b/src/tools/clippy/tests/ui/author/matches.rs diff --git a/src/tools/clippy/tests/ui-internal/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index 91b3b6f6877..91b3b6f6877 100644 --- a/src/tools/clippy/tests/ui-internal/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/repeat.rs b/src/tools/clippy/tests/ui/author/repeat.rs index d8e9d589e68..d8e9d589e68 100644 --- a/src/tools/clippy/tests/ui-internal/author/repeat.rs +++ b/src/tools/clippy/tests/ui/author/repeat.rs diff --git a/src/tools/clippy/tests/ui-internal/author/repeat.stdout b/src/tools/clippy/tests/ui/author/repeat.stdout index d9e3f864f12..d9e3f864f12 100644 --- a/src/tools/clippy/tests/ui-internal/author/repeat.stdout +++ b/src/tools/clippy/tests/ui/author/repeat.stdout diff --git a/src/tools/clippy/tests/ui-internal/author/struct.rs b/src/tools/clippy/tests/ui/author/struct.rs index a99bdfc1313..a99bdfc1313 100644 --- a/src/tools/clippy/tests/ui-internal/author/struct.rs +++ b/src/tools/clippy/tests/ui/author/struct.rs diff --git a/src/tools/clippy/tests/ui-internal/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout index 0b332d5e7d0..0b332d5e7d0 100644 --- a/src/tools/clippy/tests/ui-internal/author/struct.stdout +++ b/src/tools/clippy/tests/ui/author/struct.stdout diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index bd90042c1da..fbf84337382 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -1,4 +1,4 @@ -#![feature(repr128, proc_macro_quote)] +#![feature(repr128, proc_macro_quote, proc_macro_span)] #![allow(incomplete_features)] #![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] @@ -182,3 +182,51 @@ pub fn non_canonical_clone_derive(_: TokenStream) -> TokenStream { impl Copy for NonCanonicalClone {} } } + +// Derive macro that generates the following but where all generated spans are set to the entire +// input span. +// +// ``` +// #[allow(clippy::missing_const_for_fn)] +// fn check() {} +// ``` +#[proc_macro_derive(AllowLintSameSpan)] +pub fn allow_lint_same_span_derive(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let first = iter.next().unwrap(); + let last = iter.last().unwrap(); + let span = first.span().join(last.span()).unwrap(); + let span_help = |mut t: TokenTree| -> TokenTree { + t.set_span(span); + t + }; + // Generate the TokenStream but setting all the spans to the entire input span + <TokenStream as FromIterator<TokenTree>>::from_iter([ + span_help(Punct::new('#', Spacing::Alone).into()), + span_help( + Group::new( + Delimiter::Bracket, + <TokenStream as FromIterator<TokenTree>>::from_iter([ + Ident::new("allow", span).into(), + span_help( + Group::new( + Delimiter::Parenthesis, + <TokenStream as FromIterator<TokenTree>>::from_iter([ + Ident::new("clippy", span).into(), + span_help(Punct::new(':', Spacing::Joint).into()), + span_help(Punct::new(':', Spacing::Alone).into()), + Ident::new("missing_const_for_fn", span).into(), + ]), + ) + .into(), + ), + ]), + ) + .into(), + ), + Ident::new("fn", span).into(), + Ident::new("check", span).into(), + span_help(Group::new(Delimiter::Parenthesis, TokenStream::new()).into()), + span_help(Group::new(Delimiter::Brace, TokenStream::new()).into()), + ]) +} diff --git a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr index d410f25b2c2..1bad259b09a 100644 --- a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr +++ b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr @@ -1,12 +1,17 @@ error: `clippy::restriction` is not meant to be enabled as a group + | + = note: because of the command line `--warn clippy::restriction` + = help: enable the restriction lints you need individually + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` + +error: `clippy::restriction` is not meant to be enabled as a group --> tests/ui/blanket_clippy_restriction_lints.rs:6:9 | LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | = help: enable the restriction lints you need individually - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` error: `clippy::restriction` is not meant to be enabled as a group --> tests/ui/blanket_clippy_restriction_lints.rs:8:9 @@ -24,10 +29,5 @@ LL | #![forbid(clippy::restriction)] | = help: enable the restriction lints you need individually -error: `clippy::restriction` is not meant to be enabled as a group - | - = note: because of the command line `--warn clippy::restriction` - = help: enable the restriction lints you need individually - error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed index ea5e983de3b..22e984c46d2 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed @@ -71,3 +71,9 @@ mod false_negative { assert_ne!(addr_x, addr_y); } } + +fn issue_13584() { + let s = "Hello, world!\n"; + let p = &raw const *s; + let _ = p as *const i8; +} diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.rs b/src/tools/clippy/tests/ui/borrow_deref_ref.rs index 8c8905b150e..61d89193f42 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.rs +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.rs @@ -71,3 +71,9 @@ mod false_negative { assert_ne!(addr_x, addr_y); } } + +fn issue_13584() { + let s = "Hello, world!\n"; + let p = &raw const *s; + let _ = p as *const i8; +} diff --git a/src/tools/clippy/tests/ui/const_is_empty.rs b/src/tools/clippy/tests/ui/const_is_empty.rs index 04e0de91ecf..b5e27b61548 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.rs +++ b/src/tools/clippy/tests/ui/const_is_empty.rs @@ -171,3 +171,17 @@ fn constant_from_external_crate() { let _ = std::env::consts::EXE_EXTENSION.is_empty(); // Do not lint, `exe_ext` comes from the `std` crate } + +fn issue_13106() { + const { + assert!(!NON_EMPTY_STR.is_empty()); + } + + const { + assert!(EMPTY_STR.is_empty()); + } + + const { + EMPTY_STR.is_empty(); + } +} diff --git a/src/tools/clippy/tests/ui/const_is_empty.stderr b/src/tools/clippy/tests/ui/const_is_empty.stderr index 7f80b520b1a..0afba940d8b 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.stderr +++ b/src/tools/clippy/tests/ui/const_is_empty.stderr @@ -157,5 +157,11 @@ error: this expression always evaluates to true LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:185:9 + | +LL | EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 355f2bc7736..5cf5c608a85 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -54,8 +54,9 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB +/// MHz GHz THz /// AccessKit -/// CoreFoundation CoreGraphics CoreText +/// CoAP CoreFoundation CoreGraphics CoreText /// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 9ced2677622..420211c6539 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -54,8 +54,9 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB +/// MHz GHz THz /// AccessKit -/// CoreFoundation CoreGraphics CoreText +/// CoAP CoreFoundation CoreGraphics CoreText /// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index 67c0464149c..27a04e4b558 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -133,7 +133,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:78:5 + --> tests/ui/doc/doc-fixable.rs:79:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:95:5 + --> tests/ui/doc/doc-fixable.rs:96:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:103:8 + --> tests/ui/doc/doc-fixable.rs:104:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | /// ## `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:106:7 + --> tests/ui/doc/doc-fixable.rs:107:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | /// # `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:108:22 + --> tests/ui/doc/doc-fixable.rs:109:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL | /// Not a title #897 `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:109:5 + --> tests/ui/doc/doc-fixable.rs:110:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:116:5 + --> tests/ui/doc/doc-fixable.rs:117:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:129:5 + --> tests/ui/doc/doc-fixable.rs:130:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,7 +221,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:140:43 + --> tests/ui/doc/doc-fixable.rs:141:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -232,7 +232,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:145:5 + --> tests/ui/doc/doc-fixable.rs:146:5 | LL | And BarQuz too. | ^^^^^^ @@ -243,7 +243,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:146:1 + --> tests/ui/doc/doc-fixable.rs:147:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -254,7 +254,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:151:43 + --> tests/ui/doc/doc-fixable.rs:152:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -265,7 +265,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:156:5 + --> tests/ui/doc/doc-fixable.rs:157:5 | LL | And BarQuz too. | ^^^^^^ @@ -276,7 +276,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:157:1 + --> tests/ui/doc/doc-fixable.rs:158:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,7 +287,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:168:5 + --> tests/ui/doc/doc-fixable.rs:169:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -298,7 +298,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:187:22 + --> tests/ui/doc/doc-fixable.rs:188:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -309,7 +309,7 @@ LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:211:34 + --> tests/ui/doc/doc-fixable.rs:212:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | ~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:234:22 + --> tests/ui/doc/doc-fixable.rs:235:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -331,7 +331,7 @@ LL | /// There is no try (`do()` or do_not()). | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:234:30 + --> tests/ui/doc/doc-fixable.rs:235:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -342,7 +342,7 @@ LL | /// There is no try (do() or `do_not()`). | ~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:237:5 + --> tests/ui/doc/doc-fixable.rs:238:5 | LL | /// ABes | ^^^^ @@ -353,7 +353,7 @@ LL | /// `ABes` | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:243:9 + --> tests/ui/doc/doc-fixable.rs:244:9 | LL | /// foo() | ^^^^^ @@ -364,7 +364,7 @@ LL | /// `foo()` | ~~~~~~~ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:247:5 + --> tests/ui/doc/doc-fixable.rs:248:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<https://github.com/rust-lang/rust-clippy/pull/12836>` diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index b6cb7ff49b0..0613eddce26 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -3,6 +3,7 @@ #![allow(clippy::never_loop)] #![warn(clippy::infinite_loop)] +#![feature(async_closure)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -428,4 +429,23 @@ fn continue_outer() { } } +// don't suggest adding `-> !` to async fn/closure that already returning `-> !` +mod issue_12338 { + use super::do_something; + + async fn foo() -> ! { + loop { + do_something(); + } + } + + fn bar() { + let _ = async || -> ! { + loop { + do_something(); + } + }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr index 7635a7442f4..3a3ed7d0fe8 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.stderr +++ b/src/tools/clippy/tests/ui/infinite_loops.stderr @@ -1,5 +1,5 @@ error: infinite loop detected - --> tests/ui/infinite_loops.rs:13:5 + --> tests/ui/infinite_loops.rs:14:5 | LL | / loop { LL | | @@ -15,7 +15,7 @@ LL | fn no_break() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:20:5 + --> tests/ui/infinite_loops.rs:21:5 | LL | / loop { LL | | @@ -32,7 +32,7 @@ LL | fn all_inf() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:22:9 + --> tests/ui/infinite_loops.rs:23:9 | LL | / loop { LL | | @@ -49,7 +49,7 @@ LL | fn all_inf() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:24:13 + --> tests/ui/infinite_loops.rs:25:13 | LL | / loop { LL | | @@ -63,7 +63,7 @@ LL | fn all_inf() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:38:5 + --> tests/ui/infinite_loops.rs:39:5 | LL | / loop { LL | | @@ -74,7 +74,7 @@ LL | | } = help: if this is not intended, try adding a `break` or `return` condition in the loop error: infinite loop detected - --> tests/ui/infinite_loops.rs:51:5 + --> tests/ui/infinite_loops.rs:52:5 | LL | / loop { LL | | fn inner_fn() -> ! { @@ -90,7 +90,7 @@ LL | fn no_break_never_ret_noise() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:94:5 + --> tests/ui/infinite_loops.rs:95:5 | LL | / loop { LL | | @@ -107,7 +107,7 @@ LL | fn break_inner_but_not_outer_1(cond: bool) -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:105:5 + --> tests/ui/infinite_loops.rs:106:5 | LL | / loop { LL | | @@ -124,7 +124,7 @@ LL | fn break_inner_but_not_outer_2(cond: bool) -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:119:9 + --> tests/ui/infinite_loops.rs:120:9 | LL | / loop { LL | | @@ -138,7 +138,7 @@ LL | fn break_outer_but_not_inner() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:142:9 + --> tests/ui/infinite_loops.rs:143:9 | LL | / loop { LL | | @@ -155,7 +155,7 @@ LL | fn break_wrong_loop(cond: bool) -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:182:5 + --> tests/ui/infinite_loops.rs:183:5 | LL | / loop { LL | | @@ -172,7 +172,7 @@ LL | fn match_like() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:223:5 + --> tests/ui/infinite_loops.rs:224:5 | LL | / loop { LL | | @@ -186,7 +186,7 @@ LL | fn match_like() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:228:5 + --> tests/ui/infinite_loops.rs:229:5 | LL | / loop { LL | | @@ -203,7 +203,7 @@ LL | fn match_like() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:333:9 + --> tests/ui/infinite_loops.rs:334:9 | LL | / loop { LL | | @@ -217,7 +217,7 @@ LL | fn problematic_trait_method() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:343:9 + --> tests/ui/infinite_loops.rs:344:9 | LL | / loop { LL | | @@ -231,7 +231,7 @@ LL | fn could_be_problematic() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:352:9 + --> tests/ui/infinite_loops.rs:353:9 | LL | / loop { LL | | @@ -245,7 +245,7 @@ LL | let _loop_forever = || -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:366:8 + --> tests/ui/infinite_loops.rs:367:8 | LL | Ok(loop { | ________^ @@ -256,7 +256,7 @@ LL | | }) = help: if this is not intended, try adding a `break` or `return` condition in the loop error: infinite loop detected - --> tests/ui/infinite_loops.rs:408:5 + --> tests/ui/infinite_loops.rs:409:5 | LL | / 'infinite: loop { LL | | @@ -272,7 +272,7 @@ LL | fn continue_outer() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:415:5 + --> tests/ui/infinite_loops.rs:416:5 | LL | / loop { LL | | @@ -289,7 +289,7 @@ LL | fn continue_outer() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:417:9 + --> tests/ui/infinite_loops.rs:418:9 | LL | / 'inner: loop { LL | | loop { @@ -304,7 +304,7 @@ LL | fn continue_outer() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:425:5 + --> tests/ui/infinite_loops.rs:426:5 | LL | / loop { LL | | diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr index 7c42fa1dd89..d748c85003b 100644 --- a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr +++ b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr @@ -13,8 +13,8 @@ help: consider implementing `IntoIterator` for `&S1` | LL + LL + impl IntoIterator for &S1 { -LL + type IntoIter = std::slice::Iter<'_, u8>; LL + type Item = &u8; +LL + type IntoIter = std::slice::Iter<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -34,8 +34,8 @@ help: consider implementing `IntoIterator` for `&mut S1` | LL + LL + impl IntoIterator for &mut S1 { -LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + type Item = &mut u8; +LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -55,8 +55,8 @@ help: consider implementing `IntoIterator` for `&S3<'a>` | LL + LL + impl IntoIterator for &S3<'a> { -LL + type IntoIter = std::slice::Iter<'_, u8>; LL + type Item = &u8; +LL + type IntoIter = std::slice::Iter<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -76,8 +76,8 @@ help: consider implementing `IntoIterator` for `&mut S3<'a>` | LL + LL + impl IntoIterator for &mut S3<'a> { -LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + type Item = &mut u8; +LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -96,8 +96,8 @@ help: consider implementing `IntoIterator` for `&S8<T>` | LL + LL + impl IntoIterator for &S8<T> { -LL + type IntoIter = std::slice::Iter<'static, T>; LL + type Item = &T; +LL + type IntoIter = std::slice::Iter<'static, T>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -117,8 +117,8 @@ help: consider implementing `IntoIterator` for `&S9<T>` | LL + LL + impl IntoIterator for &S9<T> { -LL + type IntoIter = std::slice::Iter<'_, T>; LL + type Item = &T; +LL + type IntoIter = std::slice::Iter<'_, T>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -138,8 +138,8 @@ help: consider implementing `IntoIterator` for `&mut S9<T>` | LL + LL + impl IntoIterator for &mut S9<T> { -LL + type IntoIter = std::slice::IterMut<'_, T>; LL + type Item = &mut T; +LL + type IntoIter = std::slice::IterMut<'_, T>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -162,8 +162,8 @@ help: consider implementing `IntoIterator` for `&Issue12037` | LL ~ LL + impl IntoIterator for &Issue12037 { -LL + type IntoIter = std::slice::Iter<'_, u8>; LL + type Item = &u8; +LL + type IntoIter = std::slice::Iter<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } diff --git a/src/tools/clippy/tests/ui/large_const_arrays.fixed b/src/tools/clippy/tests/ui/large_const_arrays.fixed index 543ce460e7b..e5b28cb6a9d 100644 --- a/src/tools/clippy/tests/ui/large_const_arrays.fixed +++ b/src/tools/clippy/tests/ui/large_const_arrays.fixed @@ -9,11 +9,13 @@ pub struct S { // Should lint pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +static FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100]; static FOO: [u32; 1_000_000] = [0u32; 1_000_000]; // Good pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250]; pub const G_FOO_PUB: [u32; 250] = [0u32; 250]; +const G_FOO_COMPUTED: [u32; 25 * 10] = [0u32; 25 * 10]; const G_FOO: [u32; 250] = [0u32; 250]; fn main() { diff --git a/src/tools/clippy/tests/ui/large_const_arrays.rs b/src/tools/clippy/tests/ui/large_const_arrays.rs index e23a8081171..b9593225da2 100644 --- a/src/tools/clippy/tests/ui/large_const_arrays.rs +++ b/src/tools/clippy/tests/ui/large_const_arrays.rs @@ -9,11 +9,13 @@ pub struct S { // Should lint pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +const FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100]; const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; // Good pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250]; pub const G_FOO_PUB: [u32; 250] = [0u32; 250]; +const G_FOO_COMPUTED: [u32; 25 * 10] = [0u32; 25 * 10]; const G_FOO: [u32; 250] = [0u32; 250]; fn main() { diff --git a/src/tools/clippy/tests/ui/large_const_arrays.stderr b/src/tools/clippy/tests/ui/large_const_arrays.stderr index 88f921b81f7..27112205390 100644 --- a/src/tools/clippy/tests/ui/large_const_arrays.stderr +++ b/src/tools/clippy/tests/ui/large_const_arrays.stderr @@ -20,13 +20,21 @@ LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; error: large array defined as const --> tests/ui/large_const_arrays.rs:12:1 | +LL | const FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> tests/ui/large_const_arrays.rs:13:1 + | LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:21:5 + --> tests/ui/large_const_arrays.rs:23:5 | LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +42,7 @@ LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:22:5 + --> tests/ui/large_const_arrays.rs:24:5 | LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,7 +50,7 @@ LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:23:5 + --> tests/ui/large_const_arrays.rs:25:5 | LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +58,7 @@ LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:24:5 + --> tests/ui/large_const_arrays.rs:26:5 | LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,7 +66,7 @@ LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:25:5 + --> tests/ui/large_const_arrays.rs:27:5 | LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,12 +74,12 @@ LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:26:5 + --> tests/ui/large_const_arrays.rs:28:5 | LL | const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | help: make this a static item: `static` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/manual_bits.fixed b/src/tools/clippy/tests/ui/manual_bits.fixed index 4de01905e8a..8e5cb7d38b9 100644 --- a/src/tools/clippy/tests/ui/manual_bits.fixed +++ b/src/tools/clippy/tests/ui/manual_bits.fixed @@ -56,3 +56,14 @@ fn main() { let _ = (u128::BITS as usize).pow(5); let _ = &(u128::BITS as usize); } + +fn should_not_lint() { + macro_rules! bits_via_macro { + ($T: ty) => { + size_of::<$T>() * 8; + }; + } + + bits_via_macro!(u8); + bits_via_macro!(String); +} diff --git a/src/tools/clippy/tests/ui/manual_bits.rs b/src/tools/clippy/tests/ui/manual_bits.rs index d4f369fcf87..5e492ed15d2 100644 --- a/src/tools/clippy/tests/ui/manual_bits.rs +++ b/src/tools/clippy/tests/ui/manual_bits.rs @@ -56,3 +56,14 @@ fn main() { let _ = (size_of::<u128>() * 8).pow(5); let _ = &(size_of::<u128>() * 8); } + +fn should_not_lint() { + macro_rules! bits_via_macro { + ($T: ty) => { + size_of::<$T>() * 8; + }; + } + + bits_via_macro!(u8); + bits_via_macro!(String); +} diff --git a/src/tools/clippy/tests/ui/map_all_any_identity.fixed b/src/tools/clippy/tests/ui/map_all_any_identity.fixed new file mode 100644 index 00000000000..c92462ab9c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_all_any_identity.fixed @@ -0,0 +1,21 @@ +#![warn(clippy::map_all_any_identity)] + +fn main() { + let _ = ["foo"].into_iter().any(|s| s == "foo"); + //~^ map_all_any_identity + let _ = ["foo"].into_iter().all(|s| s == "foo"); + //~^ map_all_any_identity + + // + // Do not lint + // + // Not identity + let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0); + // Macro + macro_rules! map { + ($x:expr) => { + $x.into_iter().map(|s| s == "foo") + }; + } + map!(["foo"]).any(|a| a); +} diff --git a/src/tools/clippy/tests/ui/map_all_any_identity.rs b/src/tools/clippy/tests/ui/map_all_any_identity.rs new file mode 100644 index 00000000000..0e4a564ca04 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_all_any_identity.rs @@ -0,0 +1,21 @@ +#![warn(clippy::map_all_any_identity)] + +fn main() { + let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a); + //~^ map_all_any_identity + let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity); + //~^ map_all_any_identity + + // + // Do not lint + // + // Not identity + let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0); + // Macro + macro_rules! map { + ($x:expr) => { + $x.into_iter().map(|s| s == "foo") + }; + } + map!(["foo"]).any(|a| a); +} diff --git a/src/tools/clippy/tests/ui/map_all_any_identity.stderr b/src/tools/clippy/tests/ui/map_all_any_identity.stderr new file mode 100644 index 00000000000..98fdcc2a939 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_all_any_identity.stderr @@ -0,0 +1,26 @@ +error: usage of `.map(...).any(identity)` + --> tests/ui/map_all_any_identity.rs:4:33 + | +LL | let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-all-any-identity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_all_any_identity)]` +help: use `.any(...)` instead + | +LL | let _ = ["foo"].into_iter().any(|s| s == "foo"); + | ~~~~~~~~~~~~~~~~~~~ + +error: usage of `.map(...).all(identity)` + --> tests/ui/map_all_any_identity.rs:6:33 + | +LL | let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.all(...)` instead + | +LL | let _ = ["foo"].into_iter().all(|s| s == "foo"); + | ~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed new file mode 100644 index 00000000000..cf520e71a64 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed @@ -0,0 +1,73 @@ +#![allow( + unused, + clippy::redundant_closure, + clippy::reversed_empty_ranges, + clippy::identity_op +)] +#![warn(clippy::map_with_unused_argument_over_ranges)] + +fn do_something() -> usize { + todo!() +} + +fn do_something_interesting(x: usize, y: usize) -> usize { + todo!() +} + +macro_rules! gen { + () => { + (0..10).map(|_| do_something()); + }; +} + +fn main() { + // These should be raised + std::iter::repeat_with(|| do_something()).take(10); + std::iter::repeat_with(|| do_something()).take(10); + std::iter::repeat_with(|| do_something()).take(11); + std::iter::repeat_with(|| do_something()).take(7); + std::iter::repeat_with(|| do_something()).take(8); + std::iter::repeat_n(3, 10); + std::iter::repeat_with(|| { + let x = 3; + x + 2 + }).take(10); + std::iter::repeat_with(|| do_something()).take(10).collect::<Vec<_>>(); + let upper = 4; + std::iter::repeat_with(|| do_something()).take(upper); + let upper_fn = || 4; + std::iter::repeat_with(|| do_something()).take(upper_fn()); + std::iter::repeat_with(|| do_something()).take(upper_fn() + 1); + std::iter::repeat_with(|| do_something()).take(upper_fn() - 2); + std::iter::repeat_with(|| do_something()).take(upper_fn() - 1); + (-3..9).map(|_| do_something()); + std::iter::repeat_with(|| do_something()).take(0); + std::iter::repeat_with(|| do_something()).take(1); + std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); + // These should not be raised + gen!(); + let lower = 2; + let lower_fn = || 2; + (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..=upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (0..10).map(|x| do_something_interesting(x, 4)); // Actual map over range + "Foobar".chars().map(|_| do_something()); // Not a map over range + // i128::MAX == 340282366920938463463374607431768211455 + (0..=340282366920938463463374607431768211455).map(|_: u128| do_something()); // Can't be replaced due to overflow +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + (0..10).map(|_| do_something()); +} + +#[clippy::msrv = "1.28"] +fn msrv_1_28() { + std::iter::repeat_with(|| do_something()).take(10); +} + +#[clippy::msrv = "1.81"] +fn msrv_1_82() { + std::iter::repeat(3).take(10); +} diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs new file mode 100644 index 00000000000..298eee9ca3f --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs @@ -0,0 +1,73 @@ +#![allow( + unused, + clippy::redundant_closure, + clippy::reversed_empty_ranges, + clippy::identity_op +)] +#![warn(clippy::map_with_unused_argument_over_ranges)] + +fn do_something() -> usize { + todo!() +} + +fn do_something_interesting(x: usize, y: usize) -> usize { + todo!() +} + +macro_rules! gen { + () => { + (0..10).map(|_| do_something()); + }; +} + +fn main() { + // These should be raised + (0..10).map(|_| do_something()); + (0..10).map(|_foo| do_something()); + (0..=10).map(|_| do_something()); + (3..10).map(|_| do_something()); + (3..=10).map(|_| do_something()); + (0..10).map(|_| 3); + (0..10).map(|_| { + let x = 3; + x + 2 + }); + (0..10).map(|_| do_something()).collect::<Vec<_>>(); + let upper = 4; + (0..upper).map(|_| do_something()); + let upper_fn = || 4; + (0..upper_fn()).map(|_| do_something()); + (0..=upper_fn()).map(|_| do_something()); + (2..upper_fn()).map(|_| do_something()); + (2..=upper_fn()).map(|_| do_something()); + (-3..9).map(|_| do_something()); + (9..3).map(|_| do_something()); + (9..=9).map(|_| do_something()); + (1..=1 << 4).map(|_| do_something()); + // These should not be raised + gen!(); + let lower = 2; + let lower_fn = || 2; + (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..=upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (0..10).map(|x| do_something_interesting(x, 4)); // Actual map over range + "Foobar".chars().map(|_| do_something()); // Not a map over range + // i128::MAX == 340282366920938463463374607431768211455 + (0..=340282366920938463463374607431768211455).map(|_: u128| do_something()); // Can't be replaced due to overflow +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + (0..10).map(|_| do_something()); +} + +#[clippy::msrv = "1.28"] +fn msrv_1_28() { + (0..10).map(|_| do_something()); +} + +#[clippy::msrv = "1.81"] +fn msrv_1_82() { + (0..10).map(|_| 3); +} diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr new file mode 100644 index 00000000000..0b56c6d9521 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr @@ -0,0 +1,223 @@ +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:25:5 + | +LL | (0..10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-with-unused-argument-over-ranges` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]` +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:26:5 + | +LL | (0..10).map(|_foo| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_foo| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:27:5 + | +LL | (0..=10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..=10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(11); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:28:5 + | +LL | (3..10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (3..10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(7); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:29:5 + | +LL | (3..=10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (3..=10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(8); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:30:5 + | +LL | (0..10).map(|_| 3); + | ^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_n` + | +LL | std::iter::repeat_n(3, 10); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~ + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:31:5 + | +LL | / (0..10).map(|_| { +LL | | let x = 3; +LL | | x + 2 +LL | | }); + | |______^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL ~ std::iter::repeat_with(|| { +LL | let x = 3; +LL | x + 2 +LL ~ }).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:35:5 + | +LL | (0..10).map(|_| do_something()).collect::<Vec<_>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_| do_something()).collect::<Vec<_>>(); +LL + std::iter::repeat_with(|| do_something()).take(10).collect::<Vec<_>>(); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:37:5 + | +LL | (0..upper).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..upper).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:39:5 + | +LL | (0..upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn()); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:40:5 + | +LL | (0..=upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..=upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn() + 1); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:41:5 + | +LL | (2..upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (2..upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn() - 2); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:42:5 + | +LL | (2..=upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (2..=upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn() - 1); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:44:5 + | +LL | (9..3).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (9..3).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(0); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:45:5 + | +LL | (9..=9).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (9..=9).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(1); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:46:5 + | +LL | (1..=1 << 4).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (1..=1 << 4).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:67:5 + | +LL | (0..10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:72:5 + | +LL | (0..10).map(|_| 3); + | ^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat` and `take` + | +LL | std::iter::repeat(3).take(10); + | ~~~~~~~~~~~~~~~~~ ~ +++++++++ + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index f7b6e1a186b..754fe061c4a 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -2,7 +2,6 @@ #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] #![feature(const_trait_impl, abi_vectorcall)] - use std::mem::transmute; struct Game { @@ -104,7 +103,7 @@ fn main() {} struct D; -/* FIXME(effects) +/* FIXME(const_trait_impl) impl const Drop for D { fn drop(&mut self) { todo!(); @@ -113,7 +112,7 @@ impl const Drop for D { */ // Lint this, since it can be dropped in const contexts -// FIXME(effects) +// FIXME(const_trait_impl) const fn d(this: D) {} //~^ ERROR: this could be a `const fn` diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 4866e321024..460be0733e0 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -2,7 +2,6 @@ #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] #![feature(const_trait_impl, abi_vectorcall)] - use std::mem::transmute; struct Game { @@ -104,7 +103,7 @@ fn main() {} struct D; -/* FIXME(effects) +/* FIXME(const_trait_impl) impl const Drop for D { fn drop(&mut self) { todo!(); @@ -113,7 +112,7 @@ impl const Drop for D { */ // Lint this, since it can be dropped in const contexts -// FIXME(effects) +// FIXME(const_trait_impl) fn d(this: D) {} //~^ ERROR: this could be a `const fn` diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index f28dec5c7f1..d553c522556 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:14:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:13:5 | LL | / pub fn new() -> Self { LL | | @@ -16,7 +16,7 @@ LL | pub const fn new() -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:20:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:19:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | @@ -30,7 +30,7 @@ LL | const fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:27:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:26:1 | LL | / fn one() -> i32 { LL | | @@ -44,7 +44,7 @@ LL | const fn one() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:33:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:32:1 | LL | / fn two() -> i32 { LL | | @@ -59,7 +59,7 @@ LL | const fn two() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:40:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:39:1 | LL | / fn string() -> String { LL | | @@ -73,7 +73,7 @@ LL | const fn string() -> String { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:46:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:45:1 | LL | / unsafe fn four() -> i32 { LL | | @@ -87,7 +87,7 @@ LL | const unsafe fn four() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:52:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:51:1 | LL | / fn generic<T>(t: T) -> T { LL | | @@ -101,7 +101,7 @@ LL | const fn generic<T>(t: T) -> T { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:61:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:60:1 | LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T { LL | | @@ -115,7 +115,7 @@ LL | const fn generic_arr<T: Copy>(t: [T; 1]) -> T { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:75:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:74:9 | LL | / pub fn b(self, a: &A) -> B { LL | | @@ -129,7 +129,7 @@ LL | pub const fn b(self, a: &A) -> B { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:85:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:84:5 | LL | / fn const_fn_stabilized_before_msrv(byte: u8) { LL | | @@ -143,7 +143,7 @@ LL | const fn const_fn_stabilized_before_msrv(byte: u8) { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:97:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:96:1 | LL | / fn msrv_1_46() -> i32 { LL | | @@ -157,7 +157,7 @@ LL | const fn msrv_1_46() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:117:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:116:1 | LL | fn d(this: D) {} | ^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL | const fn d(this: D) {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:125:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:124:9 | LL | / fn deref_ptr_can_be_const(self) -> usize { LL | | @@ -182,7 +182,7 @@ LL | const fn deref_ptr_can_be_const(self) -> usize { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:130:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:129:9 | LL | / fn deref_copied_val(self) -> usize { LL | | @@ -196,7 +196,7 @@ LL | const fn deref_copied_val(self) -> usize { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:141:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:140:5 | LL | / fn union_access_can_be_const() { LL | | @@ -211,7 +211,7 @@ LL | const fn union_access_can_be_const() { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:149:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:148:9 | LL | extern "C" fn c() {} | ^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +222,7 @@ LL | const extern "C" fn c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:153:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9 | LL | extern fn implicit_c() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | const extern fn implicit_c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:170:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:169:9 | LL | / pub fn new(strings: Vec<String>) -> Self { LL | | Self { strings } @@ -246,7 +246,7 @@ LL | pub const fn new(strings: Vec<String>) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:175:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:174:9 | LL | / pub fn empty() -> Self { LL | | Self { strings: Vec::new() } @@ -259,7 +259,7 @@ LL | pub const fn empty() -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:186:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:185:9 | LL | / pub fn new(text: String) -> Self { LL | | let vec = Vec::new(); @@ -273,7 +273,7 @@ LL | pub const fn new(text: String) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:205:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:204:5 | LL | fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL | const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:209:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:208:5 | LL | extern "C-unwind" fn c_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -295,7 +295,7 @@ LL | const extern "C-unwind" fn c_unwind() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:211:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:210:5 | LL | extern "system" fn system() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -306,7 +306,7 @@ LL | const extern "system" fn system() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:213:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:212:5 | LL | extern "system-unwind" fn system_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -317,7 +317,7 @@ LL | const extern "system-unwind" fn system_unwind() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:215:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:214:5 | LL | pub extern "vectorcall" fn std_call() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL | pub const extern "vectorcall" fn std_call() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:217:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:216:5 | LL | pub extern "vectorcall-unwind" fn std_call_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_doc.rs b/src/tools/clippy/tests/ui/missing_doc.rs index 9c936d7fa23..31bc4309582 100644 --- a/src/tools/clippy/tests/ui/missing_doc.rs +++ b/src/tools/clippy/tests/ui/missing_doc.rs @@ -116,6 +116,14 @@ with_span!(span pub fn foo_pm() {}); with_span!(span pub static FOO_PM: u32 = 0;); with_span!(span pub const FOO2_PM: u32 = 0;); +// Don't lint unnamed constants +const _: () = (); + +fn issue13298() { + // Rustdoc doesn't generate documentation for items within other items like fns or consts + const MSG: &str = "Hello, world!"; +} + // issue #12197 // Undocumented field originated inside of spanned proc-macro attribute /// Some dox for struct. diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr index ef0f96a5b71..133c76ac9d4 100644 --- a/src/tools/clippy/tests/ui/missing_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_doc.stderr @@ -88,5 +88,14 @@ error: missing documentation for a function LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: missing documentation for a function + --> tests/ui/missing_doc.rs:122:1 + | +LL | / fn issue13298() { +LL | | // Rustdoc doesn't generate documentation for items within other items like fns or consts +LL | | const MSG: &str = "Hello, world!"; +LL | | } + | |_^ + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.fixed b/src/tools/clippy/tests/ui/needless_as_bytes.fixed new file mode 100644 index 00000000000..042342311fd --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_as_bytes.fixed @@ -0,0 +1,50 @@ +#![warn(clippy::needless_as_bytes)] +#![allow(clippy::const_is_empty)] + +struct S; + +impl S { + fn as_bytes(&self) -> &[u8] { + &[] + } +} + +fn main() { + if "some string".is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".len()); + //~^ needless_as_bytes + } + + let s = String::from("yet another string"); + if s.is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.len()); + //~^ needless_as_bytes + } + + // Do not lint + let _ = S.as_bytes().is_empty(); + let _ = S.as_bytes().len(); + let _ = (&String::new() as &dyn AsBytes).as_bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".as_bytes() + }; + } + m!(1).as_bytes().len(); + m!(2).len(); +} + +pub trait AsBytes { + fn as_bytes(&self) -> &[u8]; +} + +impl AsBytes for String { + fn as_bytes(&self) -> &[u8] { + &[] + } +} diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.rs b/src/tools/clippy/tests/ui/needless_as_bytes.rs new file mode 100644 index 00000000000..c481e041e0a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_as_bytes.rs @@ -0,0 +1,50 @@ +#![warn(clippy::needless_as_bytes)] +#![allow(clippy::const_is_empty)] + +struct S; + +impl S { + fn as_bytes(&self) -> &[u8] { + &[] + } +} + +fn main() { + if "some string".as_bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".as_bytes().len()); + //~^ needless_as_bytes + } + + let s = String::from("yet another string"); + if s.as_bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.as_bytes().len()); + //~^ needless_as_bytes + } + + // Do not lint + let _ = S.as_bytes().is_empty(); + let _ = S.as_bytes().len(); + let _ = (&String::new() as &dyn AsBytes).as_bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".as_bytes() + }; + } + m!(1).as_bytes().len(); + m!(2).len(); +} + +pub trait AsBytes { + fn as_bytes(&self) -> &[u8]; +} + +impl AsBytes for String { + fn as_bytes(&self) -> &[u8] { + &[] + } +} diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.stderr b/src/tools/clippy/tests/ui/needless_as_bytes.stderr new file mode 100644 index 00000000000..3391238a142 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_as_bytes.stderr @@ -0,0 +1,29 @@ +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:13:8 + | +LL | if "some string".as_bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` + | + = note: `-D clippy::needless-as-bytes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]` + +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:15:30 + | +LL | println!("len = {}", "some string".as_bytes().len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` + +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:20:8 + | +LL | if s.as_bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` + +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:22:30 + | +LL | println!("len = {}", s.as_bytes().len()); + | ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs index c26a292c8cb..b6d8a8f61ae 100644 --- a/src/tools/clippy/tests/ui/needless_continue.rs +++ b/src/tools/clippy/tests/ui/needless_continue.rs @@ -151,3 +151,20 @@ mod issue_2329 { } } } + +fn issue_13641() { + 'a: while std::hint::black_box(true) { + #[allow(clippy::never_loop)] + loop { + continue 'a; + } + } + + #[allow(clippy::never_loop)] + while std::hint::black_box(true) { + 'b: loop { + continue 'b; + //~^ ERROR: this `continue` expression is redundant + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr index ec7c9ba39a7..0741ba69248 100644 --- a/src/tools/clippy/tests/ui/needless_continue.stderr +++ b/src/tools/clippy/tests/ui/needless_continue.stderr @@ -136,5 +136,13 @@ LL | | } println!("bar-5"); } -error: aborting due to 8 previous errors +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:166:13 + | +LL | continue 'b; + | ^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/no_lints.rs b/src/tools/clippy/tests/ui/no_lints.rs deleted file mode 100644 index a8467bb6ef7..00000000000 --- a/src/tools/clippy/tests/ui/no_lints.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![deny(clippy::all)] - -fn main() {} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs index 8c1ea81d2ac..8be149d1863 100644 --- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs @@ -2,30 +2,30 @@ #![allow(unused)] #![warn(clippy::no_mangle_with_rust_abi)] -#[no_mangle] +#[unsafe(no_mangle)] fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI //~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` -#[no_mangle] +#[unsafe(no_mangle)] pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI /// # Safety /// This function shouldn't be called unless the horsemen are ready -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI /// # Safety /// This function shouldn't be called unless the horsemen are ready -#[no_mangle] +#[unsafe(no_mangle)] unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI -#[no_mangle] +#[unsafe(no_mangle)] fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( - //~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + //~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI arg_one: u32, arg_two: usize, ) -> u32 { @@ -33,13 +33,13 @@ fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lin } // Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"` -#[no_mangle] +#[unsafe(no_mangle)] #[rustfmt::skip] extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {} fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {} -#[no_mangle] +#[unsafe(no_mangle)] extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} @@ -48,6 +48,11 @@ extern "C" { fn c_abi_in_block(arg_one: u32, arg_two: usize); } +mod r#fn { + #[unsafe(no_mangle)] + pub(in super::r#fn) fn with_some_fn_around() {} +} + fn main() { // test code goes here } diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr index 27b8b39a21a..a00ebe5e1ac 100644 --- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr @@ -1,4 +1,4 @@ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:6:1 | LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} @@ -15,7 +15,7 @@ help: or explicitly set the default LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:11:1 | LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} @@ -30,7 +30,7 @@ help: or explicitly set the default LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:17:1 | LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} @@ -45,7 +45,7 @@ help: or explicitly set the default LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:23:1 | LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} @@ -60,7 +60,7 @@ help: or explicitly set the default LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:27:1 | LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( @@ -79,5 +79,20 @@ help: or explicitly set the default LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( | +++++++++++++ -error: aborting due to 5 previous errors +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi.rs:53:5 + | +LL | pub(in super::r#fn) fn with_some_fn_around() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub(in super::r#fn) extern "C" fn with_some_fn_around() {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub(in super::r#fn) extern "Rust" fn with_some_fn_around() {} + | +++++++++++++ + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.rs new file mode 100644 index 00000000000..c7c97335348 --- /dev/null +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.rs @@ -0,0 +1,57 @@ +//@edition:2021 +// +// Edition 2024 requires the use of #[unsafe(no_mangle)] + +//@no-rustfix: overlapping suggestions +#![allow(unused)] +#![warn(clippy::no_mangle_with_rust_abi)] + +#[no_mangle] +fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` + +#[no_mangle] +pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + +#[no_mangle] +fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + //~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + arg_one: u32, + arg_two: usize, +) -> u32 { + 0 +} + +// Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"` +#[no_mangle] +#[rustfmt::skip] +extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {} + +fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} + +extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} + +extern "C" { + fn c_abi_in_block(arg_one: u32, arg_two: usize); +} + +fn main() { + // test code goes here +} diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.stderr b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.stderr new file mode 100644 index 00000000000..15075be72d0 --- /dev/null +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.stderr @@ -0,0 +1,83 @@ +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:10:1 + | +LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::no_mangle_with_rust_abi)]` +help: set an ABI + | +LL | extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:15:1 + | +LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:21:1 + | +LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:27:1 + | +LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:31:1 + | +LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( +LL | | +LL | | arg_one: u32, +LL | | arg_two: usize, +LL | | ) -> u32 { + | |________^ + | +help: set an ABI + | +LL | extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + | ++++++++++ +help: or explicitly set the default + | +LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + | +++++++++++++ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr index eafffdaf8a6..578f918f013 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr @@ -118,25 +118,25 @@ error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:161:8 | LL | if !(12 == a) {} - | ^^^^^^^^^^ help: try: `12 != a` + | ^^^^^^^^^^ help: try: `(12 != a)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:162:8 | LL | if !(a == 12) {} - | ^^^^^^^^^^ help: try: `a != 12` + | ^^^^^^^^^^ help: try: `(a != 12)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:163:8 | LL | if !(12 != a) {} - | ^^^^^^^^^^ help: try: `12 == a` + | ^^^^^^^^^^ help: try: `(12 == a)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:164:8 | LL | if !(a != 12) {} - | ^^^^^^^^^^ help: try: `a == 12` + | ^^^^^^^^^^ help: try: `(a == 12)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:168:8 diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed index a23310c1ad9..65ccaaca891 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed @@ -110,9 +110,33 @@ fn dont_warn_for_negated_partial_ord_comparison() { fn issue_12625() { let a = 0; let b = 0; - if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified - if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified - if a as u64 > b {} //~ ERROR: this boolean expression can be simplified + if ((a as u64) < b) {} //~ ERROR: this boolean expression can be simplified + if ((a as u64) < b) {} //~ ERROR: this boolean expression can be simplified + if (a as u64 > b) {} //~ ERROR: this boolean expression can be simplified +} + +fn issue_12761() { + let a = 0; + let b = 0; + let c = 0; + if (a < b) as i32 == c {} //~ ERROR: this boolean expression can be simplified + if (a < b) | (a > c) {} //~ ERROR: this boolean expression can be simplified + let opt: Option<usize> = Some(1); + let res: Result<usize, usize> = Ok(1); + if res.is_err() as i32 == c {} //~ ERROR: this boolean expression can be simplified + if res.is_err() | opt.is_some() {} //~ ERROR: this boolean expression can be simplified + + fn a(a: bool) -> bool { + (4 <= 3).b() //~ ERROR: this boolean expression can be simplified + } + + trait B { + fn b(&self) -> bool { + true + } + } + + impl B for bool {} } fn issue_13436() { diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs index 6c844373af7..06db3a1d4a5 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs @@ -115,6 +115,30 @@ fn issue_12625() { if !(a as u64 <= b) {} //~ ERROR: this boolean expression can be simplified } +fn issue_12761() { + let a = 0; + let b = 0; + let c = 0; + if !(a >= b) as i32 == c {} //~ ERROR: this boolean expression can be simplified + if !(a >= b) | !(a <= c) {} //~ ERROR: this boolean expression can be simplified + let opt: Option<usize> = Some(1); + let res: Result<usize, usize> = Ok(1); + if !res.is_ok() as i32 == c {} //~ ERROR: this boolean expression can be simplified + if !res.is_ok() | !opt.is_none() {} //~ ERROR: this boolean expression can be simplified + + fn a(a: bool) -> bool { + (!(4 > 3)).b() //~ ERROR: this boolean expression can be simplified + } + + trait B { + fn b(&self) -> bool { + true + } + } + + impl B for bool {} +} + fn issue_13436() { fn not_zero(x: i32) -> bool { x != 0 diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr index 52803e828ae..66c50f9ff1e 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr @@ -83,127 +83,169 @@ error: this boolean expression can be simplified --> tests/ui/nonminimal_bool_methods.rs:113:8 | LL | if !(a as u64 >= b) {} - | ^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` + | ^^^^^^^^^^^^^^^^ help: try: `((a as u64) < b)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool_methods.rs:114:8 | LL | if !((a as u64) >= b) {} - | ^^^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` + | ^^^^^^^^^^^^^^^^^^ help: try: `((a as u64) < b)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool_methods.rs:115:8 | LL | if !(a as u64 <= b) {} - | ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b` + | ^^^^^^^^^^^^^^^^ help: try: `(a as u64 > b)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:131:9 + --> tests/ui/nonminimal_bool_methods.rs:122:8 + | +LL | if !(a >= b) as i32 == c {} + | ^^^^^^^^^ help: try: `(a < b)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:123:8 + | +LL | if !(a >= b) | !(a <= c) {} + | ^^^^^^^^^ help: try: `(a < b)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:123:20 + | +LL | if !(a >= b) | !(a <= c) {} + | ^^^^^^^^^ help: try: `(a > c)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:126:8 + | +LL | if !res.is_ok() as i32 == c {} + | ^^^^^^^^^^^^ help: try: `res.is_err()` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:127:8 + | +LL | if !res.is_ok() | !opt.is_none() {} + | ^^^^^^^^^^^^ help: try: `res.is_err()` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:127:23 + | +LL | if !res.is_ok() | !opt.is_none() {} + | ^^^^^^^^^^^^^^ help: try: `opt.is_some()` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:130:9 + | +LL | (!(4 > 3)).b() + | ^^^^^^^^^^ help: try: `(4 <= 3)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:155:9 | LL | _ = !opt.is_some_and(|x| x < 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x >= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:132:9 + --> tests/ui/nonminimal_bool_methods.rs:156:9 | LL | _ = !opt.is_some_and(|x| x <= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x > 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:133:9 + --> tests/ui/nonminimal_bool_methods.rs:157:9 | LL | _ = !opt.is_some_and(|x| x > 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x <= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:134:9 + --> tests/ui/nonminimal_bool_methods.rs:158:9 | LL | _ = !opt.is_some_and(|x| x >= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x < 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:135:9 + --> tests/ui/nonminimal_bool_methods.rs:159:9 | LL | _ = !opt.is_some_and(|x| x == 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x != 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:136:9 + --> tests/ui/nonminimal_bool_methods.rs:160:9 | LL | _ = !opt.is_some_and(|x| x != 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x == 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:145:9 + --> tests/ui/nonminimal_bool_methods.rs:169:9 | LL | _ = !opt.is_none_or(|x| x < 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x >= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:146:9 + --> tests/ui/nonminimal_bool_methods.rs:170:9 | LL | _ = !opt.is_none_or(|x| x <= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x > 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:147:9 + --> tests/ui/nonminimal_bool_methods.rs:171:9 | LL | _ = !opt.is_none_or(|x| x > 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x <= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:148:9 + --> tests/ui/nonminimal_bool_methods.rs:172:9 | LL | _ = !opt.is_none_or(|x| x >= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x < 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:149:9 + --> tests/ui/nonminimal_bool_methods.rs:173:9 | LL | _ = !opt.is_none_or(|x| x == 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x != 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:150:9 + --> tests/ui/nonminimal_bool_methods.rs:174:9 | LL | _ = !opt.is_none_or(|x| x != 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x == 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:157:9 + --> tests/ui/nonminimal_bool_methods.rs:181:9 | LL | _ = !opt.is_some_and(|x| !x); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:161:9 + --> tests/ui/nonminimal_bool_methods.rs:185:9 | LL | _ = !opt.is_none_or(|x| !x); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:168:9 + --> tests/ui/nonminimal_bool_methods.rs:192:9 | LL | _ = !opt.is_some_and(|x| x.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_err())` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:169:9 + --> tests/ui/nonminimal_bool_methods.rs:193:9 | LL | _ = !opt.is_some_and(|x| x.is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_ok())` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:170:9 + --> tests/ui/nonminimal_bool_methods.rs:194:9 | LL | _ = !opt.is_none_or(|x| x.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_err())` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:171:9 + --> tests/ui/nonminimal_bool_methods.rs:195:9 | LL | _ = !opt.is_none_or(|x| x.is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_ok())` -error: aborting due to 34 previous errors +error: aborting due to 41 previous errors diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index e03df1658ee..2ac59718786 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -54,7 +54,7 @@ #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] -#![allow(temporary_cstring_as_ptr)] +#![allow(dangling_pointers_from_temporaries)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index b906079d7df..a5ae727b7ee 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,17 +1,11 @@ -error: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:57:10 - | -LL | #![allow(temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` - error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` --> tests/ui/rename.rs:64:9 @@ -403,5 +397,5 @@ error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_e LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: aborting due to 67 previous errors +error: aborting due to 66 previous errors diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed index 2afe2f43325..f72b61b5f6c 100644 --- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::map_with_unused_argument_over_ranges)] #![warn(clippy::repeat_vec_with_capacity)] fn main() { diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs index 659f2a3953d..c0cc81f7843 100644 --- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs @@ -1,3 +1,4 @@ +#![allow(clippy::map_with_unused_argument_over_ranges)] #![warn(clippy::repeat_vec_with_capacity)] fn main() { diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr index cec9c6ea84a..43027c9cb89 100644 --- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr @@ -1,5 +1,5 @@ error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity - --> tests/ui/repeat_vec_with_capacity.rs:5:9 + --> tests/ui/repeat_vec_with_capacity.rs:6:9 | LL | vec![Vec::<()>::with_capacity(42); 123]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity - --> tests/ui/repeat_vec_with_capacity.rs:11:9 + --> tests/ui/repeat_vec_with_capacity.rs:12:9 | LL | vec![Vec::<()>::with_capacity(42); n]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity - --> tests/ui/repeat_vec_with_capacity.rs:26:9 + --> tests/ui/repeat_vec_with_capacity.rs:27:9 | LL | std::iter::repeat(Vec::<()>::with_capacity(42)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed index 148e112e0bc..ac7e86631ca 100644 --- a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed +++ b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed @@ -80,5 +80,13 @@ fn main() { { unit_fn_block(); }; + unsafe { + std::arch::asm!("") + }; + + { + line!() + }; + unit_fn_block() } diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.rs b/src/tools/clippy/tests/ui/semicolon_outside_block.rs index c767201469a..68f25339e32 100644 --- a/src/tools/clippy/tests/ui/semicolon_outside_block.rs +++ b/src/tools/clippy/tests/ui/semicolon_outside_block.rs @@ -80,5 +80,13 @@ fn main() { { unit_fn_block(); }; + unsafe { + std::arch::asm!(""); + } + + { + line!(); + } + unit_fn_block() } diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.stderr b/src/tools/clippy/tests/ui/semicolon_outside_block.stderr index 68b44c8f980..ff8c00048f6 100644 --- a/src/tools/clippy/tests/ui/semicolon_outside_block.stderr +++ b/src/tools/clippy/tests/ui/semicolon_outside_block.stderr @@ -51,5 +51,33 @@ LL - { m!(()); } LL + { m!(()) }; | -error: aborting due to 4 previous errors +error: consider moving the `;` outside the block for consistent formatting + --> tests/ui/semicolon_outside_block.rs:83:5 + | +LL | / unsafe { +LL | | std::arch::asm!(""); +LL | | } + | |_____^ + | +help: put the `;` here + | +LL ~ std::arch::asm!("") +LL ~ }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> tests/ui/semicolon_outside_block.rs:87:5 + | +LL | / { +LL | | line!(); +LL | | } + | |_____^ + | +help: put the `;` here + | +LL ~ line!() +LL ~ }; + | + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_map.rs b/src/tools/clippy/tests/ui/suspicious_map.rs index d4247fcd926..d4a52cb110f 100644 --- a/src/tools/clippy/tests/ui/suspicious_map.rs +++ b/src/tools/clippy/tests/ui/suspicious_map.rs @@ -1,3 +1,4 @@ +#![allow(clippy::map_with_unused_argument_over_ranges)] #![warn(clippy::suspicious_map)] fn main() { diff --git a/src/tools/clippy/tests/ui/suspicious_map.stderr b/src/tools/clippy/tests/ui/suspicious_map.stderr index 2a5e2bf2a34..769adebaede 100644 --- a/src/tools/clippy/tests/ui/suspicious_map.stderr +++ b/src/tools/clippy/tests/ui/suspicious_map.stderr @@ -1,5 +1,5 @@ error: this call to `map()` won't have an effect on the call to `count()` - --> tests/ui/suspicious_map.rs:4:13 + --> tests/ui/suspicious_map.rs:5:13 | LL | let _ = (0..3).map(|x| x + 2).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = (0..3).map(|x| x + 2).count(); = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]` error: this call to `map()` won't have an effect on the call to `count()` - --> tests/ui/suspicious_map.rs:8:13 + --> tests/ui/suspicious_map.rs:9:13 | LL | let _ = (0..3).map(f).count(); | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs index 1e0d7d12965..8cf102ab0a5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs @@ -1,26 +1,31 @@ +//@no-rustfix #![allow(dead_code)] fn main() { let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - //~^ ERROR: this `.filter_map` can be written more simply using `.filter` + //~^ ERROR: this `.filter_map` can be written more simply //~| NOTE: `-D clippy::unnecessary-filter-map` implied by `-D warnings` let _ = (0..4).filter_map(|x| { - //~^ ERROR: this `.filter_map` can be written more simply using `.filter` + //~^ ERROR: this `.filter_map` can be written more simply if x > 1 { return Some(x); }; None }); let _ = (0..4).filter_map(|x| match x { - //~^ ERROR: this `.filter_map` can be written more simply using `.filter` + //~^ ERROR: this `.filter_map` can be written more simply 0 | 1 => None, _ => Some(x), }); let _ = (0..4).filter_map(|x| Some(x + 1)); - //~^ ERROR: this `.filter_map` can be written more simply using `.map` + //~^ ERROR: this `.filter_map` can be written more simply let _ = (0..4).filter_map(i32::checked_abs); + + let _ = (0..4).filter_map(Some); + + let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); } fn filter_map_none_changes_item_type() -> impl Iterator<Item = bool> { diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr index f32d444ba9e..b21589c5f84 100644 --- a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr @@ -1,14 +1,14 @@ -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:4:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:5:13 | LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` | = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]` -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:7:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:8:13 | LL | let _ = (0..4).filter_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | return Some(x); LL | | }; LL | | None LL | | }); - | |______^ + | |______^ help: try instead: `filter` -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:14:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:15:13 | LL | let _ = (0..4).filter_map(|x| match x { | _____________^ @@ -29,19 +29,40 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ + | |______^ help: try instead: `filter` -error: this `.filter_map` can be written more simply using `.map` - --> tests/ui/unnecessary_filter_map.rs:20:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:21:13 | LL | let _ = (0..4).filter_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:160:14 +error: redundant closure + --> tests/ui/unnecessary_filter_map.rs:28:57 + | +LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); + | ^^^^^^^^^^^ help: replace the closure with the function itself: `Some` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` + +error: filter_map is unnecessary + --> tests/ui/unnecessary_filter_map.rs:28:61 + | +LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); + | ^^^^ help: try removing the filter_map + +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:28:13 + | +LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:165:14 | LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.rs b/src/tools/clippy/tests/ui/unnecessary_find_map.rs index 9972b68092a..c357d853248 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.rs +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.rs @@ -1,24 +1,25 @@ +//@no-rustfix #![allow(dead_code)] fn main() { let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - //~^ ERROR: this `.find_map` can be written more simply using `.find` + //~^ ERROR: this `.find_map` can be written more simply //~| NOTE: `-D clippy::unnecessary-find-map` implied by `-D warnings` let _ = (0..4).find_map(|x| { - //~^ ERROR: this `.find_map` can be written more simply using `.find` + //~^ ERROR: this `.find_map` can be written more simply if x > 1 { return Some(x); }; None }); let _ = (0..4).find_map(|x| match x { - //~^ ERROR: this `.find_map` can be written more simply using `.find` + //~^ ERROR: this `.find_map` can be written more simply 0 | 1 => None, _ => Some(x), }); let _ = (0..4).find_map(|x| Some(x + 1)); - //~^ ERROR: this `.find_map` can be written more simply using `.map(..).next()` + //~^ ERROR: this `.find_map` can be written more simply let _ = (0..4).find_map(i32::checked_abs); } diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr index bb939a99214..98a6c3d164a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr @@ -1,14 +1,14 @@ -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:4:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:5:13 | LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` | = note: `-D clippy::unnecessary-find-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]` -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:7:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:8:13 | LL | let _ = (0..4).find_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | return Some(x); LL | | }; LL | | None LL | | }); - | |______^ + | |______^ help: try instead: `find` -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:14:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:15:13 | LL | let _ = (0..4).find_map(|x| match x { | _____________^ @@ -29,19 +29,19 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ + | |______^ help: try instead: `find` -error: this `.find_map` can be written more simply using `.map(..).next()` - --> tests/ui/unnecessary_find_map.rs:20:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:21:13 | LL | let _ = (0..4).find_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map(..).next()` -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:32:14 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:33:14 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs index f5b200d5ffe..175c4ca7689 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.rs +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -277,4 +277,18 @@ fn allow_works<F: std::io::Read>(mut f: F) { f.read(&mut data).unwrap(); } +struct Reader {} + +impl Read for Reader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + todo!() + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + // We shouldn't recommend using Read::read_exact inside Read::read_exact! + self.read(buf).unwrap(); + Ok(()) + } +} + fn main() {} diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index cd9641eedd8..eadfd7107c7 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -21,13 +21,11 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", - "giraffate", ] [assign.owners] "/.github" = ["@flip1995"] "/book" = ["@flip1995"] -"/util/gh-pages" = ["@xFrednet"] "*" = [ "@Manishearth", "@llogiq", diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index 2412f0fd181..deb0ef0b499 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -52,12 +52,12 @@ Otherwise, have a great day =^.^= <noscript> {# #} <div class="alert alert-danger" role="alert"> {# #} - Sorry, this site only works with JavaScript! :( {# #} + Lints search and filtering only works with JS enabled. :( {# #} </div> {# #} </noscript> {# #} <div> {# #} - <div class="panel panel-default"> {# #} + <div class="panel panel-default" id="menu-filters"> {# #} <div class="panel-body row"> {# #} <div id="upper-filters" class="col-12 col-md-5"> {# #} <div class="btn-group" id="lint-levels" tabindex="-1"> {# #} @@ -104,7 +104,7 @@ Otherwise, have a great day =^.^= <li role="separator" class="divider"></li> {# #} </ul> {# #} </div> {# #} - <div class="btn-group", id="lint-applicabilities" tabindex="-1"> {# #} + <div class="btn-group" id="lint-applicabilities" tabindex="-1"> {# #} <button type="button" class="btn btn-default dropdown-toggle"> {# #} Applicability {#+ #} <span class="badge">4</span> {#+ #} @@ -143,26 +143,29 @@ Otherwise, have a great day =^.^= </div> {# #} </div> {% for lint in lints %} - <article class="panel panel-default collapsed" id="{{lint.id}}"> {# #} - <header class="panel-heading" onclick="expandLint('{{lint.id}}')"> {# #} - <h2 class="panel-title"> {# #} - <div class="panel-title-name" id="lint-{{lint.id}}"> {# #} - <span>{{lint.id}}</span> {#+ #} - <a href="#{{lint.id}}" class="anchor label label-default" onclick="openLint(event)">¶</a> {#+ #} - <a href="" class="anchor label label-default" onclick="copyToClipboard(event)"> {# #} - 📋 {# #} - </a> {# #} - </div> {# #} + <article class="panel panel-default" id="{{lint.id}}"> {# #} + <input id="label-{{lint.id}}" type="checkbox"> {# #} + <label for="label-{{lint.id}}" onclick="highlightIfNeeded('{{lint.id}}')"> {# #} + <header class="panel-heading"> {# #} + <h2 class="panel-title"> {# #} + <div class="panel-title-name" id="lint-{{lint.id}}"> {# #} + <span>{{lint.id}}</span> {#+ #} + <a href="#{{lint.id}}" onclick="lintAnchor(event)" class="anchor label label-default">¶</a> {#+ #} + <a href="" class="anchor label label-default" onclick="copyToClipboard(event)"> {# #} + 📋 {# #} + </a> {# #} + </div> {# #} - <div class="panel-title-addons"> {# #} - <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #} + <div class="panel-title-addons"> {# #} + <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #} - <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #} + <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #} - <span class="label label-doc-folding">+</span> {# #} - </div> {# #} - </h2> {# #} - </header> {# #} + <span class="label label-doc-folding"></span> {# #} + </div> {# #} + </h2> {# #} + </header> {# #} + </label> {# #} <div class="list-group lint-docs"> {# #} <div class="list-group-item lint-doc-md">{{Self::markdown(lint.docs)}}</div> {# #} diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index cc22a39b3d1..9a5365b2158 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -138,25 +138,27 @@ function onEachLazy(lazyArray, func) { } } -function highlightIfNeeded(elem) { - onEachLazy(elem.querySelectorAll("pre > code.language-rust:not(.highlighted)"), el => { +function highlightIfNeeded(lintId) { + onEachLazy(document.querySelectorAll(`#${lintId} pre > code:not(.hljs)`), el => { hljs.highlightElement(el.parentElement) el.classList.add("highlighted"); }); } function expandLint(lintId) { - const lintElem = document.getElementById(lintId); - const isCollapsed = lintElem.classList.toggle("collapsed"); - lintElem.querySelector(".label-doc-folding").innerText = isCollapsed ? "+" : "−"; - highlightIfNeeded(lintElem); + const elem = document.querySelector(`#${lintId} > input[type="checkbox"]`); + elem.checked = true; + highlightIfNeeded(lintId); } -// Show details for one lint -function openLint(event) { +function lintAnchor(event) { event.preventDefault(); event.stopPropagation(); - expandLint(event.target.getAttribute("href").slice(1)); + + const id = event.target.getAttribute("href").replace("#", ""); + window.location.hash = id; + + expandLint(id); } function copyToClipboard(event) { @@ -520,7 +522,7 @@ function scrollToLint(lintId) { // If the page we arrive on has link to a given lint, we scroll to it. function scrollToLintByURL() { - const lintId = window.location.hash.substring(2); + const lintId = window.location.hash.substring(1); if (lintId.length > 0) { scrollToLint(lintId); } diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css index a68a10b1401..896f2fdac76 100644 --- a/src/tools/clippy/util/gh-pages/style.css +++ b/src/tools/clippy/util/gh-pages/style.css @@ -204,7 +204,7 @@ details[open] { } /* Expanding the mdBook theme*/ -.light { +.light, body:not([class]) { --inline-code-bg: #f6f7f6; } .rust { @@ -220,6 +220,21 @@ details[open] { --inline-code-bg: #191f26; } +@media (prefers-color-scheme: dark) { + body:not([class]) { + /* + In case JS is disabled and the user's system is in dark mode, we take "coal" as default + dark theme. + */ + --inline-code-bg: #1d1f21; + } +} + +html:not(.js) #settings-dropdown, +html:not(.js)#menu-filters { + display: none; +} + #settings-dropdown { position: absolute; margin: 0.7em; @@ -309,12 +324,12 @@ L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.649 .page-header { border-color: var(--theme-popup-border); } -.panel-default > .panel-heading { +.panel-default .panel-heading { background: var(--theme-hover); color: var(--fg); border: 1px solid var(--theme-popup-border); } -.panel-default > .panel-heading:hover { +.panel-default .panel-heading:hover { filter: brightness(90%); } .list-group-item { @@ -410,9 +425,25 @@ body { color: var(--fg); } -article.collapsed .lint-docs { +article > label { + width: 100%; + margin: 0; +} +article > input[type="checkbox"] { display: none; } +article > input[type="checkbox"] + label .label-doc-folding::before { + content: "+"; +} +article > input[type="checkbox"]:checked + label .label-doc-folding::before { + content: "−"; +} +.lint-docs { + display: none; +} +article > input[type="checkbox"]:checked ~ .lint-docs { + display: block; +} .github-corner svg { fill: var(--fg); diff --git a/src/tools/clippy/util/gh-pages/theme.js b/src/tools/clippy/util/gh-pages/theme.js index bc296955ddf..90f57d4469d 100644 --- a/src/tools/clippy/util/gh-pages/theme.js +++ b/src/tools/clippy/util/gh-pages/theme.js @@ -45,6 +45,10 @@ function setTheme(theme, store) { } (function() { + // This file is loaded first. If so, we add the `js` class on the `<html>` + // element. + document.documentElement.classList.add("js"); + // loading the theme after the initial load const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); const theme = loadValue("theme"); diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index ecb8343fba3..e6fbba943a4 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -236,8 +236,11 @@ pub struct Config { /// Run ignored tests pub run_ignored: bool, - /// Whether to run tests with `ignore-debug` header - pub with_debug_assertions: bool, + /// Whether rustc was built with debug assertions. + pub with_rustc_debug_assertions: bool, + + /// Whether std was built with debug assertions. + pub with_std_debug_assertions: bool, /// Only run tests that match these filters pub filters: Vec<String>, diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 980b3f6829a..8570a8ce115 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -46,7 +46,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-coverage-map", "ignore-coverage-run", "ignore-cross-compile", - "ignore-debug", "ignore-eabi", "ignore-emscripten", "ignore-endian-big", @@ -82,6 +81,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-powerpc", "ignore-remote", "ignore-riscv64", + "ignore-rustc-debug-assertions", "ignore-s390x", "ignore-sgx", "ignore-sparc64", @@ -89,6 +89,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-stable", "ignore-stage1", "ignore-stage2", + "ignore-std-debug-assertions", "ignore-test", "ignore-thumb", "ignore-thumbv8m.base-none-eabi", @@ -135,6 +136,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-relocation-model-pic", "needs-run-enabled", "needs-rust-lld", + "needs-rustc-debug-assertions", "needs-sanitizer-address", "needs-sanitizer-cfi", "needs-sanitizer-dataflow", @@ -147,6 +149,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-sanitizer-shadow-call-stack", "needs-sanitizer-support", "needs-sanitizer-thread", + "needs-std-debug-assertions", "needs-symlink", "needs-threads", "needs-unwind", diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs index b9314f0abbb..3ab552903dc 100644 --- a/src/tools/compiletest/src/header/cfg.rs +++ b/src/tools/compiletest/src/header/cfg.rs @@ -202,9 +202,14 @@ pub(super) fn parse_cfg_name_directive<'a>( message: "when running tests remotely", } condition! { - name: "debug", - condition: config.with_debug_assertions, - message: "when running tests with `ignore-debug` header", + name: "rustc-debug-assertions", + condition: config.with_rustc_debug_assertions, + message: "when rustc is built with debug assertions", + } + condition! { + name: "std-debug-assertions", + condition: config.with_std_debug_assertions, + message: "when std is built with debug assertions", } condition! { name: config.debugger.as_ref().map(|d| d.to_str()), diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index a744fb61b9c..77570c58db5 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -159,6 +159,16 @@ pub(super) fn handle_needs( condition: cache.llvm_zstd, ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression", }, + Need { + name: "needs-rustc-debug-assertions", + condition: config.with_rustc_debug_assertions, + ignore_reason: "ignored if rustc wasn't built with debug assertions", + }, + Need { + name: "needs-std-debug-assertions", + condition: config.with_std_debug_assertions, + ignore_reason: "ignored if std wasn't built with debug assertions", + }, ]; let (name, comment) = match ln.split_once([':', ' ']) { diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 0e735dc77c4..c3fb8d4ab80 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -74,6 +74,8 @@ struct ConfigBuilder { git_hash: bool, system_llvm: bool, profiler_runtime: bool, + rustc_debug_assertions: bool, + std_debug_assertions: bool, } impl ConfigBuilder { @@ -122,6 +124,16 @@ impl ConfigBuilder { self } + fn rustc_debug_assertions(&mut self, is_enabled: bool) -> &mut Self { + self.rustc_debug_assertions = is_enabled; + self + } + + fn std_debug_assertions(&mut self, is_enabled: bool) -> &mut Self { + self.std_debug_assertions = is_enabled; + self + } + fn build(&mut self) -> Config { let args = &[ "compiletest", @@ -170,6 +182,12 @@ impl ConfigBuilder { if self.profiler_runtime { args.push("--profiler-runtime".to_owned()); } + if self.rustc_debug_assertions { + args.push("--with-rustc-debug-assertions".to_owned()); + } + if self.std_debug_assertions { + args.push("--with-std-debug-assertions".to_owned()); + } args.push("--rustc-path".to_string()); // This is a subtle/fragile thing. On rust-lang CI, there is no global @@ -315,6 +333,32 @@ fn only_target() { } #[test] +fn rustc_debug_assertions() { + let config: Config = cfg().rustc_debug_assertions(false).build(); + + assert!(check_ignore(&config, "//@ needs-rustc-debug-assertions")); + assert!(!check_ignore(&config, "//@ ignore-rustc-debug-assertions")); + + let config: Config = cfg().rustc_debug_assertions(true).build(); + + assert!(!check_ignore(&config, "//@ needs-rustc-debug-assertions")); + assert!(check_ignore(&config, "//@ ignore-rustc-debug-assertions")); +} + +#[test] +fn std_debug_assertions() { + let config: Config = cfg().std_debug_assertions(false).build(); + + assert!(check_ignore(&config, "//@ needs-std-debug-assertions")); + assert!(!check_ignore(&config, "//@ ignore-std-debug-assertions")); + + let config: Config = cfg().std_debug_assertions(true).build(); + + assert!(!check_ignore(&config, "//@ needs-std-debug-assertions")); + assert!(check_ignore(&config, "//@ ignore-std-debug-assertions")); +} + +#[test] fn stage() { let config: Config = cfg().stage_id("stage1-x86_64-unknown-linux-gnu").build(); @@ -583,10 +627,6 @@ fn wasm_special() { ("wasm32-unknown-emscripten", "emscripten", true), ("wasm32-unknown-emscripten", "wasm32", true), ("wasm32-unknown-emscripten", "wasm32-bare", false), - ("wasm32-wasi", "emscripten", false), - ("wasm32-wasi", "wasm32", true), - ("wasm32-wasi", "wasm32-bare", false), - ("wasm32-wasi", "wasi", true), ("wasm32-wasip1", "emscripten", false), ("wasm32-wasip1", "wasm32", true), ("wasm32-wasip1", "wasm32-bare", false), diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 5c06a39c477..bf4a3124075 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -88,7 +88,8 @@ pub fn parse_config(args: Vec<String>) -> Config { .optopt("", "run", "whether to execute run-* tests", "auto | always | never") .optflag("", "ignored", "run tests marked as ignored") .optflag("", "has-enzyme", "run tests that require enzyme") - .optflag("", "with-debug-assertions", "whether to run tests with `ignore-debug` header") + .optflag("", "with-rustc-debug-assertions", "whether rustc was built with debug assertions") + .optflag("", "with-std-debug-assertions", "whether std was built with debug assertions") .optmulti( "", "skip", @@ -235,7 +236,8 @@ pub fn parse_config(args: Vec<String>) -> Config { let src_base = opt_path(matches, "src-base"); let run_ignored = matches.opt_present("ignored"); - let with_debug_assertions = matches.opt_present("with-debug-assertions"); + let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions"); + let with_std_debug_assertions = matches.opt_present("with-std-debug-assertions"); let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode"); let has_html_tidy = if mode == Mode::Rustdoc { Command::new("tidy") @@ -293,7 +295,8 @@ pub fn parse_config(args: Vec<String>) -> Config { suite: matches.opt_str("suite").unwrap(), debugger: None, run_ignored, - with_debug_assertions, + with_rustc_debug_assertions, + with_std_debug_assertions, filters, skip: matches.opt_strs("skip"), filter_exact: matches.opt_present("exact"), diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 96d5acd27ad..5adbfbfebfb 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -67395551d07b0eaf6a45ed3bf1759530ca2235d7 +328b759142ddeae96da83176f103200009d3e3f1 diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs index fa7c0bf5c0c..e95ef4d9062 100644 --- a/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs +++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs @@ -1,4 +1,4 @@ -#![feature(rustc_attrs)] +#![feature(intrinsics, rustc_attrs)] #[rustc_intrinsic] #[rustc_nounwind] diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs index b5feac8c677..cd48bd2accb 100644 --- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs +++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs @@ -1,3 +1,5 @@ +#![feature(never_type)] + use std::rc::Rc; use std::{mem, num, ptr}; @@ -12,6 +14,18 @@ fn id<T>(x: T) -> T { x } +#[derive(Copy, Clone)] +enum Either<T, U> { + Left(T), + Right(U), +} +#[derive(Copy, Clone)] +enum Either2<T, U> { + Left(T), + #[allow(unused)] + Right(U, ()), +} + fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) { fn id<T>(x: T) -> T { x @@ -81,6 +95,8 @@ fn main() { test_abi_compat(main as fn(), id::<i32> as fn(i32) -> i32); // - 1-ZST test_abi_compat((), [0u8; 0]); + + // Guaranteed null-pointer-layout optimizations: // - Guaranteed Option<X> null-pointer-optimizations (RFC 3391). test_abi_compat(&0u32 as *const u32, Some(&0u32)); test_abi_compat(main as fn(), Some(main as fn())); @@ -89,6 +105,7 @@ fn main() { test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1u32).unwrap()))); // - Guaranteed Result<X, ZST1> does the same as Option<X> (RFC 3391) test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(&0u32)); + test_abi_compat(&0u32 as *const u32, Result::<_, !>::Ok(&0u32)); test_abi_compat(main as fn(), Result::<_, ()>::Ok(main as fn())); test_abi_compat(0u32, Result::<_, ()>::Ok(num::NonZeroU32::new(1).unwrap())); test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(Wrapper(&0u32))); @@ -99,6 +116,13 @@ fn main() { test_abi_compat(0u32, Result::<(), _>::Err(num::NonZeroU32::new(1).unwrap())); test_abi_compat(&0u32 as *const u32, Result::<(), _>::Err(Wrapper(&0u32))); test_abi_compat(0u32, Result::<(), _>::Err(Wrapper(num::NonZeroU32::new(1).unwrap()))); + // - Guaranteed null-pointer-optimizations for custom option-like types + test_abi_compat(&0u32 as *const u32, Either::<_, ()>::Left(&0u32)); + test_abi_compat(&0u32 as *const u32, Either::<_, !>::Left(&0u32)); + test_abi_compat(&0u32 as *const u32, Either::<(), _>::Right(&0u32)); + test_abi_compat(&0u32 as *const u32, Either::<!, _>::Right(&0u32)); + test_abi_compat(&0u32 as *const u32, Either2::<_, ()>::Left(&0u32)); + test_abi_compat(&0u32 as *const u32, Either2::<_, [u8; 0]>::Left(&0u32)); // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible // with the wrapped field. diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index 4b97637088c..e0135a0269a 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -51,6 +51,7 @@ jobs: cargo workspaces rename --from proc-macro-api proc_macro_api cargo workspaces rename --from proc-macro-srv proc_macro_srv cargo workspaces rename --from project-model project_model + cargo workspaces rename --from test-fixture test_fixture cargo workspaces rename --from test-utils test_utils cargo workspaces rename --from text-edit text_edit # Remove library crates from the workspaces so we don't auto-publish them as well diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 424357e8473..55705de9b2c 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1492,9 +1492,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2cfc7264d84215a08875ef90a1d35f76b5c9ad1993515d2da7e4e40b2b4b" +checksum = "709fde78db053c78c87776ec738677649f791645883f82ff145f68caf9f18e1a" dependencies = [ "bitflags 2.6.0", "ra-ap-rustc_index", @@ -1503,9 +1503,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8929140697812e5dd09e19cf446d85146332363f0dbc125d4214834c34ead96" +checksum = "da115d496e5abd65e2dceb6883d7597593badfe23fea3439202b8da5a11ea250" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1514,9 +1514,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514a3f5d04c8b4a2750f29746cc9abb1f78deb7e72e4ad1dc95bbc608f3db157" +checksum = "be86d06a75a8125c1ace197d5030e6e02721348d32e572baea35c891669ad1e2" dependencies = [ "proc-macro2", "quote", @@ -1525,9 +1525,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276fcb1205da071a0cd64416f3f0e198043c11f176c5b501a45dbf0cb33979f2" +checksum = "b64b46ae0d8f59acc32e64e0085532b831f0d6182d870a7cd86c046c2c46e722" dependencies = [ "unicode-properties", "unicode-xid", @@ -1535,9 +1535,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "961b30b22cfac296b14b72e9f95e79c16cebc8c926872755fb1568a6c4243a62" +checksum = "dbdaad19ddbd0ff46e947ca8dbb6ae678a112d3938669fb3ad6bfd244917e24b" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1545,9 +1545,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614232513814a4b714fea7f11345d31c0c277bca3089bb6ca1ec20870bfc022a" +checksum = "dc5761e37c78d98ede9f20f6b66526093d0be66aa256d5cbdf214495843ba74d" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.0.0", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 1099d2cb918..94e7de553bf 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.75", default-features = false } -ra-ap-rustc_parse_format = { version = "0.75", default-features = false } -ra-ap-rustc_index = { version = "0.75", default-features = false } -ra-ap-rustc_abi = { version = "0.75", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.75", default-features = false } +ra-ap-rustc_lexer = { version = "0.76", default-features = false } +ra-ap-rustc_parse_format = { version = "0.76", default-features = false } +ra-ap-rustc_index = { version = "0.76", default-features = false } +ra-ap-rustc_abi = { version = "0.76", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.76", default-features = false } # local crates that aren't published to crates.io. These should not have versions. test-fixture = { path = "./crates/test-fixture" } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 0b108b54e67..1ab49e91569 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -407,7 +407,7 @@ impl ExprCollector<'_> { let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); let generic_args = e .generic_arg_list() - .and_then(|it| GenericArgs::from_ast(&self.ctx(), it)) + .and_then(|it| GenericArgs::from_ast(&mut self.ctx(), it)) .map(Box::new); self.alloc_expr( Expr::MethodCall { receiver, method_name, args, generic_args }, @@ -533,7 +533,7 @@ impl ExprCollector<'_> { ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e), ast::Expr::CastExpr(e) => { let expr = self.collect_expr_opt(e.expr()); - let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty()); + let type_ref = TypeRef::from_ast_opt(&mut self.ctx(), e.ty()); self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) } ast::Expr::RefExpr(e) => { @@ -572,13 +572,15 @@ impl ExprCollector<'_> { arg_types.reserve_exact(num_params); for param in pl.params() { let pat = this.collect_pat_top(param.pat()); - let type_ref = param.ty().map(|it| TypeRef::from_ast(&this.ctx(), it)); + let type_ref = param.ty().map(|it| TypeRef::from_ast(&mut this.ctx(), it)); args.push(pat); arg_types.push(type_ref); } } - let ret_type = - e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&this.ctx(), it)); + let ret_type = e + .ret_type() + .and_then(|r| r.ty()) + .map(|it| TypeRef::from_ast(&mut this.ctx(), it)); let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine); let prev_try_block_label = this.current_try_block_label.take(); @@ -705,7 +707,7 @@ impl ExprCollector<'_> { ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr), ast::Expr::OffsetOfExpr(e) => { - let container = TypeRef::from_ast_opt(&self.ctx(), e.ty()); + let container = TypeRef::from_ast_opt(&mut self.ctx(), e.ty()); let fields = e.fields().map(|it| it.as_name()).collect(); self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) } @@ -1317,7 +1319,7 @@ impl ExprCollector<'_> { return; } let pat = self.collect_pat_top(stmt.pat()); - let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); + let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&mut self.ctx(), it)); let initializer = stmt.initializer().map(|e| self.collect_expr(e)); let else_branch = stmt .let_else() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index f49018eaf38..6d07dc8f9be 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -37,6 +37,7 @@ pub struct FunctionData { pub name: Name, pub params: Box<[TypeRefId]>, pub ret_type: TypeRefId, + // FIXME: why are these stored here? They should be accessed via the query pub attrs: Attrs, pub visibility: RawVisibility, pub abi: Option<Symbol>, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index a59bbf7e221..22005695af6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -21,7 +21,79 @@ //! //! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are //! a coincidence. -pub mod keys; + +pub mod keys { + use std::marker::PhantomData; + + use hir_expand::{attrs::AttrId, MacroCallId}; + use rustc_hash::FxHashMap; + use syntax::{ast, AstNode, AstPtr}; + + use crate::{ + dyn_map::{DynMap, Policy}, + BlockId, ConstId, EnumId, EnumVariantId, ExternCrateId, FieldId, FunctionId, ImplId, + LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, + TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, + }; + + pub type Key<K, V> = crate::dyn_map::Key<AstPtr<K>, V, AstPtrPolicy<K, V>>; + + pub const BLOCK: Key<ast::BlockExpr, BlockId> = Key::new(); + pub const FUNCTION: Key<ast::Fn, FunctionId> = Key::new(); + pub const CONST: Key<ast::Const, ConstId> = Key::new(); + pub const STATIC: Key<ast::Static, StaticId> = Key::new(); + pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new(); + pub const IMPL: Key<ast::Impl, ImplId> = Key::new(); + pub const TRAIT: Key<ast::Trait, TraitId> = Key::new(); + pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new(); + pub const STRUCT: Key<ast::Struct, StructId> = Key::new(); + pub const UNION: Key<ast::Union, UnionId> = Key::new(); + pub const ENUM: Key<ast::Enum, EnumId> = Key::new(); + pub const EXTERN_CRATE: Key<ast::ExternCrate, ExternCrateId> = Key::new(); + pub const USE: Key<ast::Use, UseId> = Key::new(); + + pub const ENUM_VARIANT: Key<ast::Variant, EnumVariantId> = Key::new(); + pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new(); + pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); + pub const TYPE_PARAM: Key<ast::TypeParam, TypeOrConstParamId> = Key::new(); + pub const CONST_PARAM: Key<ast::ConstParam, TypeOrConstParamId> = Key::new(); + pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); + + pub const MACRO_RULES: Key<ast::MacroRules, MacroRulesId> = Key::new(); + pub const MACRO2: Key<ast::MacroDef, Macro2Id> = Key::new(); + pub const PROC_MACRO: Key<ast::Fn, ProcMacroId> = Key::new(); + pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new(); + pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new(); + pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, MacroCallId, Box<[Option<MacroCallId>]>)> = + Key::new(); + + /// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are + /// equal if they point to exactly the same object. + /// + /// In general, we do not guarantee that we have exactly one instance of a + /// syntax tree for each file. We probably should add such guarantee, but, for + /// the time being, we will use identity-less AstPtr comparison. + pub struct AstPtrPolicy<AST, ID> { + _phantom: PhantomData<(AST, ID)>, + } + + impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> { + type K = AstPtr<AST>; + type V = ID; + fn insert(map: &mut DynMap, key: AstPtr<AST>, value: ID) { + map.map + .entry::<FxHashMap<AstPtr<AST>, ID>>() + .or_insert_with(Default::default) + .insert(key, value); + } + fn get<'a>(map: &'a DynMap, key: &AstPtr<AST>) -> Option<&'a ID> { + map.map.get::<FxHashMap<AstPtr<AST>, ID>>()?.get(key) + } + fn is_empty(map: &DynMap) -> bool { + map.map.get::<FxHashMap<AstPtr<AST>, ID>>().map_or(true, |it| it.is_empty()) + } + } +} use std::{ hash::Hash, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs deleted file mode 100644 index 9d330a7bf1c..00000000000 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! keys to be used with `DynMap` - -use std::marker::PhantomData; - -use hir_expand::{attrs::AttrId, MacroCallId}; -use rustc_hash::FxHashMap; -use syntax::{ast, AstNode, AstPtr}; - -use crate::{ - dyn_map::{DynMap, Policy}, - BlockId, ConstId, EnumId, EnumVariantId, ExternCrateId, FieldId, FunctionId, ImplId, - LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, - TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, -}; - -pub type Key<K, V> = crate::dyn_map::Key<AstPtr<K>, V, AstPtrPolicy<K, V>>; - -pub const BLOCK: Key<ast::BlockExpr, BlockId> = Key::new(); -pub const FUNCTION: Key<ast::Fn, FunctionId> = Key::new(); -pub const CONST: Key<ast::Const, ConstId> = Key::new(); -pub const STATIC: Key<ast::Static, StaticId> = Key::new(); -pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new(); -pub const IMPL: Key<ast::Impl, ImplId> = Key::new(); -pub const TRAIT: Key<ast::Trait, TraitId> = Key::new(); -pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new(); -pub const STRUCT: Key<ast::Struct, StructId> = Key::new(); -pub const UNION: Key<ast::Union, UnionId> = Key::new(); -pub const ENUM: Key<ast::Enum, EnumId> = Key::new(); -pub const EXTERN_CRATE: Key<ast::ExternCrate, ExternCrateId> = Key::new(); -pub const USE: Key<ast::Use, UseId> = Key::new(); - -pub const ENUM_VARIANT: Key<ast::Variant, EnumVariantId> = Key::new(); -pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new(); -pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); -pub const TYPE_PARAM: Key<ast::TypeParam, TypeOrConstParamId> = Key::new(); -pub const CONST_PARAM: Key<ast::ConstParam, TypeOrConstParamId> = Key::new(); -pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); - -pub const MACRO_RULES: Key<ast::MacroRules, MacroRulesId> = Key::new(); -pub const MACRO2: Key<ast::MacroDef, Macro2Id> = Key::new(); -pub const PROC_MACRO: Key<ast::Fn, ProcMacroId> = Key::new(); -pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new(); -pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new(); -pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, MacroCallId, Box<[Option<MacroCallId>]>)> = - Key::new(); - -/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are -/// equal if they point to exactly the same object. -/// -/// In general, we do not guarantee that we have exactly one instance of a -/// syntax tree for each file. We probably should add such guarantee, but, for -/// the time being, we will use identity-less AstPtr comparison. -pub struct AstPtrPolicy<AST, ID> { - _phantom: PhantomData<(AST, ID)>, -} - -impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> { - type K = AstPtr<AST>; - type V = ID; - fn insert(map: &mut DynMap, key: AstPtr<AST>, value: ID) { - map.map - .entry::<FxHashMap<AstPtr<AST>, ID>>() - .or_insert_with(Default::default) - .insert(key, value); - } - fn get<'a>(map: &'a DynMap, key: &AstPtr<AST>) -> Option<&'a ID> { - map.map.get::<FxHashMap<AstPtr<AST>, ID>>()?.get(key) - } - fn is_empty(map: &DynMap) -> bool { - map.map.get::<FxHashMap<AstPtr<AST>, ID>>().map_or(true, |it| it.is_empty()) - } -} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs index d430733fcad..5315c1c6fbd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs @@ -161,14 +161,14 @@ impl Expander { types_map: &mut TypesMap, types_source_map: &mut TypesSourceMap, ) -> Option<Path> { - let ctx = LowerCtx::with_span_map_cell( + let mut ctx = LowerCtx::with_span_map_cell( db, self.current_file_id, self.span_map.clone(), types_map, types_source_map, ); - Path::from_src(&ctx, path) + Path::from_src(&mut ctx, path) } fn within_limit<F, T: ast::AstNode>( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index 6b79850e9c4..11e9bb0d886 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -451,7 +451,7 @@ pub(crate) struct GenericParamsCollector { impl GenericParamsCollector { pub(crate) fn fill( &mut self, - lower_ctx: &LowerCtx<'_>, + lower_ctx: &mut LowerCtx<'_>, node: &dyn HasGenericParams, add_param_attrs: impl FnMut( Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, @@ -468,7 +468,7 @@ impl GenericParamsCollector { pub(crate) fn fill_bounds( &mut self, - lower_ctx: &LowerCtx<'_>, + lower_ctx: &mut LowerCtx<'_>, type_bounds: Option<ast::TypeBoundList>, target: Either<TypeRefId, LifetimeRef>, ) { @@ -479,7 +479,7 @@ impl GenericParamsCollector { fn fill_params( &mut self, - lower_ctx: &LowerCtx<'_>, + lower_ctx: &mut LowerCtx<'_>, params: ast::GenericParamList, mut add_param_attrs: impl FnMut( Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, @@ -535,7 +535,11 @@ impl GenericParamsCollector { } } - fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx<'_>, where_clause: ast::WhereClause) { + fn fill_where_predicates( + &mut self, + lower_ctx: &mut LowerCtx<'_>, + where_clause: ast::WhereClause, + ) { for pred in where_clause.predicates() { let target = if let Some(type_ref) = pred.ty() { Either::Left(TypeRef::from_ast(lower_ctx, type_ref)) @@ -569,7 +573,7 @@ impl GenericParamsCollector { fn add_where_predicate_from_bound( &mut self, - lower_ctx: &LowerCtx<'_>, + lower_ctx: &mut LowerCtx<'_>, bound: ast::TypeBound, hrtb_lifetimes: Option<&[Name]>, target: Either<TypeRefId, LifetimeRef>, @@ -670,8 +674,9 @@ impl GenericParamsCollector { { let (mut macro_types_map, mut macro_types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = expander.ctx(db, &mut macro_types_map, &mut macro_types_source_map); - let type_ref = TypeRef::from_ast(&ctx, expanded.tree()); + let mut ctx = + expander.ctx(db, &mut macro_types_map, &mut macro_types_source_map); + let type_ref = TypeRef::from_ast(&mut ctx, expanded.tree()); self.fill_implicit_impl_trait_args( db, generics_types_map, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index 2582340c0f8..4d83ef99c84 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -98,7 +98,7 @@ pub struct TraitRef { impl TraitRef { /// Converts an `ast::PathType` to a `hir::TraitRef`. - pub(crate) fn from_ast(ctx: &LowerCtx<'_>, node: ast::Type) -> Option<Self> { + pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::Type) -> Option<Self> { // FIXME: Use `Path::from_src` match node { ast::Type::PathType(path) => { @@ -240,7 +240,7 @@ pub enum TraitBoundModifier { impl TypeRef { /// Converts an `ast::TypeRef` to a `hir::TypeRef`. - pub fn from_ast(ctx: &LowerCtx<'_>, node: ast::Type) -> TypeRefId { + pub fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::Type) -> TypeRefId { let ty = match &node { ast::Type::ParenType(inner) => return TypeRef::from_ast_opt(ctx, inner.ty()), ast::Type::TupleType(inner) => TypeRef::Tuple(EmptyOptimizedThinVec::from_iter( @@ -321,8 +321,9 @@ impl TypeRef { // Disallow nested impl traits TypeRef::Error } else { - let _guard = ctx.outer_impl_trait_scope(true); - TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) + ctx.with_outer_impl_trait_scope(true, |ctx| { + TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) + }) } } ast::Type::DynTraitType(inner) => { @@ -336,7 +337,7 @@ impl TypeRef { ctx.alloc_type_ref(ty, AstPtr::new(&node)) } - pub(crate) fn from_ast_opt(ctx: &LowerCtx<'_>, node: Option<ast::Type>) -> TypeRefId { + pub(crate) fn from_ast_opt(ctx: &mut LowerCtx<'_>, node: Option<ast::Type>) -> TypeRefId { match node { Some(node) => TypeRef::from_ast(ctx, node), None => ctx.alloc_error_type(), @@ -410,7 +411,7 @@ impl TypeRef { } pub(crate) fn type_bounds_from_ast( - lower_ctx: &LowerCtx<'_>, + lower_ctx: &mut LowerCtx<'_>, type_bounds_opt: Option<ast::TypeBoundList>, ) -> ThinVec<TypeBound> { if let Some(type_bounds) = type_bounds_opt { @@ -423,8 +424,8 @@ pub(crate) fn type_bounds_from_ast( } impl TypeBound { - pub(crate) fn from_ast(ctx: &LowerCtx<'_>, node: ast::TypeBound) -> Self { - let lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?); + pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::TypeBound) -> Self { + let mut lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?); match node.kind() { ast::TypeBoundKind::PathType(path_type) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index a04f12cab76..e96e38eceeb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -361,9 +361,7 @@ impl ItemScope { self.macro_invocations.get(&call).copied() } - pub(crate) fn iter_macro_invoc( - &self, - ) -> impl Iterator<Item = (&AstId<ast::MacroCall>, &MacroCallId)> { + pub fn iter_macro_invoc(&self) -> impl Iterator<Item = (&AstId<ast::MacroCall>, &MacroCallId)> { self.macro_invocations.iter() } } @@ -401,9 +399,7 @@ impl ItemScope { self.macro_invocations.insert(call, call_id); } - pub(crate) fn attr_macro_invocs( - &self, - ) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ { + pub fn attr_macro_invocs(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ { self.attr_macros.iter().map(|(k, v)| (*k, *v)) } @@ -440,7 +436,7 @@ impl ItemScope { }); } - pub(crate) fn derive_macro_invocs( + pub fn derive_macro_invocs( &self, ) -> impl Iterator< Item = ( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index bd17fce37b7..d519c1708b3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -234,11 +234,11 @@ impl<'a> Ctx<'a> { fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<FileItemTreeId<Struct>> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let visibility = self.lower_visibility(strukt); let name = strukt.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(strukt); - let (fields, kind, attrs) = self.lower_fields(&strukt.kind(), &body_ctx); + let (fields, kind, attrs) = self.lower_fields(&strukt.kind(), &mut body_ctx); let (generic_params, generics_source_map) = self.lower_generic_params(HasImplicitSelf::No, strukt); types_map.shrink_to_fit(); @@ -273,7 +273,7 @@ impl<'a> Ctx<'a> { fn lower_fields( &mut self, strukt_kind: &ast::StructKind, - body_ctx: &LowerCtx<'_>, + body_ctx: &mut LowerCtx<'_>, ) -> (Box<[Field]>, FieldsShape, Vec<(usize, RawAttrs)>) { match strukt_kind { ast::StructKind::Record(it) => { @@ -308,7 +308,11 @@ impl<'a> Ctx<'a> { } } - fn lower_record_field(&mut self, field: &ast::RecordField, body_ctx: &LowerCtx<'_>) -> Field { + fn lower_record_field( + &mut self, + field: &ast::RecordField, + body_ctx: &mut LowerCtx<'_>, + ) -> Field { let name = match field.name() { Some(name) => name.as_name(), None => Name::missing(), @@ -323,7 +327,7 @@ impl<'a> Ctx<'a> { &mut self, idx: usize, field: &ast::TupleField, - body_ctx: &LowerCtx<'_>, + body_ctx: &mut LowerCtx<'_>, ) -> Field { let name = Name::new_tuple_field(idx); let visibility = self.lower_visibility(field); @@ -334,13 +338,13 @@ impl<'a> Ctx<'a> { fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let visibility = self.lower_visibility(union); let name = union.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(union); let (fields, _, attrs) = match union.record_field_list() { Some(record_field_list) => { - self.lower_fields(&StructKind::Record(record_field_list), &body_ctx) + self.lower_fields(&StructKind::Record(record_field_list), &mut body_ctx) } None => (Box::default(), FieldsShape::Record, Vec::default()), }; @@ -409,12 +413,12 @@ impl<'a> Ctx<'a> { fn lower_variant(&mut self, variant: &ast::Variant) -> Idx<Variant> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let name = match variant.name() { Some(name) => name.as_name(), None => Name::missing(), }; - let (fields, kind, attrs) = self.lower_fields(&variant.kind(), &body_ctx); + let (fields, kind, attrs) = self.lower_fields(&variant.kind(), &mut body_ctx); let ast_id = self.source_ast_id_map.ast_id(variant); types_map.shrink_to_fit(); types_source_map.shrink_to_fit(); @@ -436,7 +440,7 @@ impl<'a> Ctx<'a> { fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let visibility = self.lower_visibility(func); let name = func.name()?.as_name(); @@ -457,7 +461,7 @@ impl<'a> Ctx<'a> { RawAttrs::new(self.db.upcast(), &self_param, self.span_map()), ); let self_type = match self_param.ty() { - Some(type_ref) => TypeRef::from_ast(&body_ctx, type_ref), + Some(type_ref) => TypeRef::from_ast(&mut body_ctx, type_ref), None => { let self_type = body_ctx.alloc_type_ref_desugared(TypeRef::Path( Name::new_symbol_root(sym::Self_.clone()).into(), @@ -492,7 +496,7 @@ impl<'a> Ctx<'a> { Param { type_ref: None } } None => { - let type_ref = TypeRef::from_ast_opt(&body_ctx, param.ty()); + let type_ref = TypeRef::from_ast_opt(&mut body_ctx, param.ty()); Param { type_ref: Some(type_ref) } } }; @@ -502,7 +506,7 @@ impl<'a> Ctx<'a> { let ret_type = match func.ret_type() { Some(rt) => match rt.ty() { - Some(type_ref) => TypeRef::from_ast(&body_ctx, type_ref), + Some(type_ref) => TypeRef::from_ast(&mut body_ctx, type_ref), None if rt.thin_arrow_token().is_some() => body_ctx.alloc_error_type(), None => body_ctx.alloc_type_ref_desugared(TypeRef::unit()), }, @@ -581,11 +585,11 @@ impl<'a> Ctx<'a> { ) -> Option<FileItemTreeId<TypeAlias>> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let name = type_alias.name()?.as_name(); - let type_ref = type_alias.ty().map(|it| TypeRef::from_ast(&body_ctx, it)); + let type_ref = type_alias.ty().map(|it| TypeRef::from_ast(&mut body_ctx, it)); let visibility = self.lower_visibility(type_alias); - let bounds = self.lower_type_bounds(type_alias, &body_ctx); + let bounds = self.lower_type_bounds(type_alias, &mut body_ctx); let ast_id = self.source_ast_id_map.ast_id(type_alias); let (generic_params, generics_source_map) = self.lower_generic_params(HasImplicitSelf::No, type_alias); @@ -612,9 +616,9 @@ impl<'a> Ctx<'a> { fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let name = static_.name()?.as_name(); - let type_ref = TypeRef::from_ast_opt(&body_ctx, static_.ty()); + let type_ref = TypeRef::from_ast_opt(&mut body_ctx, static_.ty()); let visibility = self.lower_visibility(static_); let mutable = static_.mut_token().is_some(); let has_safe_kw = static_.safe_token().is_some(); @@ -639,9 +643,9 @@ impl<'a> Ctx<'a> { fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let name = konst.name().map(|it| it.as_name()); - let type_ref = TypeRef::from_ast_opt(&body_ctx, konst.ty()); + let type_ref = TypeRef::from_ast_opt(&mut body_ctx, konst.ty()); let visibility = self.lower_visibility(konst); let ast_id = self.source_ast_id_map.ast_id(konst); types_map.shrink_to_fit(); @@ -724,14 +728,14 @@ impl<'a> Ctx<'a> { fn lower_impl(&mut self, impl_def: &ast::Impl) -> FileItemTreeId<Impl> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); let ast_id = self.source_ast_id_map.ast_id(impl_def); // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only // equals itself. - let self_ty = TypeRef::from_ast_opt(&body_ctx, impl_def.self_ty()); - let target_trait = impl_def.trait_().and_then(|tr| TraitRef::from_ast(&body_ctx, tr)); + let self_ty = TypeRef::from_ast_opt(&mut body_ctx, impl_def.self_ty()); + let target_trait = impl_def.trait_().and_then(|tr| TraitRef::from_ast(&mut body_ctx, tr)); let is_negative = impl_def.excl_token().is_some(); let is_unsafe = impl_def.unsafe_token().is_some(); @@ -870,13 +874,8 @@ impl<'a> Ctx<'a> { ) -> (Arc<GenericParams>, TypesSourceMap) { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); + let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map); debug_assert!(self.generic_param_attr_buffer.is_empty(),); - let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, - param| { - let attrs = RawAttrs::new(self.db.upcast(), ¶m, body_ctx.span_map()); - debug_assert!(self.generic_param_attr_buffer.insert(item, attrs).is_none()); - }; body_ctx.take_impl_traits_bounds(); let mut generics = GenericParamsCollector::default(); @@ -892,16 +891,19 @@ impl<'a> Ctx<'a> { ); // add super traits as bounds on Self // i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar` - generics.fill_bounds( - &body_ctx, - bounds, - Either::Left(body_ctx.alloc_type_ref_desugared(TypeRef::Path( - Name::new_symbol_root(sym::Self_.clone()).into(), - ))), - ); + let bound_target = Either::Left(body_ctx.alloc_type_ref_desugared(TypeRef::Path( + Name::new_symbol_root(sym::Self_.clone()).into(), + ))); + generics.fill_bounds(&mut body_ctx, bounds, bound_target); } - generics.fill(&body_ctx, node, add_param_attrs); + let span_map = body_ctx.span_map().clone(); + let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, + param| { + let attrs = RawAttrs::new(self.db.upcast(), ¶m, span_map.as_ref()); + debug_assert!(self.generic_param_attr_buffer.insert(item, attrs).is_none()); + }; + generics.fill(&mut body_ctx, node, add_param_attrs); let generics = generics.finish(types_map, &mut types_source_map); (generics, types_source_map) @@ -910,7 +912,7 @@ impl<'a> Ctx<'a> { fn lower_type_bounds( &mut self, node: &dyn ast::HasTypeBounds, - body_ctx: &LowerCtx<'_>, + body_ctx: &mut LowerCtx<'_>, ) -> Box<[TypeBound]> { match node.type_bound_list() { Some(bound_list) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index f6ed826f04c..eb55ba1d53d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -47,7 +47,6 @@ pub mod resolver; pub mod nameres; -pub mod child_by_source; pub mod src; pub mod find_path; @@ -354,9 +353,9 @@ impl_loc!(ProcMacroLoc, id: Function, container: CrateRootModuleId); pub struct BlockId(ra_salsa::InternId); #[derive(Debug, Hash, PartialEq, Eq, Clone)] pub struct BlockLoc { - ast_id: AstId<ast::BlockExpr>, + pub ast_id: AstId<ast::BlockExpr>, /// The containing module. - module: ModuleId, + pub module: ModuleId, } impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); @@ -838,16 +837,18 @@ impl InTypeConstId { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum GeneralConstId { ConstId(ConstId), + StaticId(StaticId), ConstBlockId(ConstBlockId), InTypeConstId(InTypeConstId), } -impl_from!(ConstId, ConstBlockId, InTypeConstId for GeneralConstId); +impl_from!(ConstId, StaticId, ConstBlockId, InTypeConstId for GeneralConstId); impl GeneralConstId { pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> { match self { GeneralConstId::ConstId(it) => Some(it.into()), + GeneralConstId::StaticId(_) => None, GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db), GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db), } @@ -855,6 +856,9 @@ impl GeneralConstId { pub fn name(self, db: &dyn DefDatabase) -> String { match self { + GeneralConstId::StaticId(it) => { + db.static_data(it).name.display(db.upcast(), Edition::CURRENT).to_string() + } GeneralConstId::ConstId(const_id) => db .const_data(const_id) .name @@ -935,7 +939,7 @@ impl_from!( ); impl GenericDefId { - fn file_id_and_params_of( + pub fn file_id_and_params_of( self, db: &dyn DefDatabase, ) -> (HirFileId, Option<ast::GenericParamList>) { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs index df5847929c5..6d1a3d17447 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs @@ -1,10 +1,7 @@ //! Context for lowering paths. -use std::cell::{OnceCell, RefCell}; +use std::{cell::OnceCell, mem}; -use hir_expand::{ - span_map::{SpanMap, SpanMapRef}, - AstId, HirFileId, InFile, -}; +use hir_expand::{span_map::SpanMap, AstId, HirFileId, InFile}; use span::{AstIdMap, AstIdNode}; use stdx::thin_vec::ThinVec; use syntax::ast; @@ -21,28 +18,11 @@ pub struct LowerCtx<'a> { file_id: HirFileId, span_map: OnceCell<SpanMap>, ast_id_map: OnceCell<Arc<AstIdMap>>, - impl_trait_bounds: RefCell<Vec<ThinVec<TypeBound>>>, + impl_trait_bounds: Vec<ThinVec<TypeBound>>, // Prevent nested impl traits like `impl Foo<impl Bar>`. - outer_impl_trait: RefCell<bool>, - types_map: RefCell<(&'a mut TypesMap, &'a mut TypesSourceMap)>, -} - -pub(crate) struct OuterImplTraitGuard<'a, 'b> { - ctx: &'a LowerCtx<'b>, - old: bool, -} - -impl<'a, 'b> OuterImplTraitGuard<'a, 'b> { - fn new(ctx: &'a LowerCtx<'b>, impl_trait: bool) -> Self { - let old = ctx.outer_impl_trait.replace(impl_trait); - Self { ctx, old } - } -} - -impl Drop for OuterImplTraitGuard<'_, '_> { - fn drop(&mut self) { - self.ctx.outer_impl_trait.replace(self.old); - } + outer_impl_trait: bool, + types_map: &'a mut TypesMap, + types_source_map: &'a mut TypesSourceMap, } impl<'a> LowerCtx<'a> { @@ -57,9 +37,10 @@ impl<'a> LowerCtx<'a> { file_id, span_map: OnceCell::new(), ast_id_map: OnceCell::new(), - impl_trait_bounds: RefCell::new(Vec::new()), - outer_impl_trait: RefCell::default(), - types_map: RefCell::new((types_map, types_source_map)), + impl_trait_bounds: Vec::new(), + outer_impl_trait: false, + types_map, + types_source_map, } } @@ -75,17 +56,18 @@ impl<'a> LowerCtx<'a> { file_id, span_map, ast_id_map: OnceCell::new(), - impl_trait_bounds: RefCell::new(Vec::new()), - outer_impl_trait: RefCell::default(), - types_map: RefCell::new((types_map, types_source_map)), + impl_trait_bounds: Vec::new(), + outer_impl_trait: false, + types_map, + types_source_map, } } - pub(crate) fn span_map(&self) -> SpanMapRef<'_> { - self.span_map.get_or_init(|| self.db.span_map(self.file_id)).as_ref() + pub(crate) fn span_map(&self) -> &SpanMap { + self.span_map.get_or_init(|| self.db.span_map(self.file_id)) } - pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> { + pub(crate) fn lower_path(&mut self, ast: ast::Path) -> Option<Path> { Path::from_src(self, ast) } @@ -96,44 +78,44 @@ impl<'a> LowerCtx<'a> { ) } - pub fn update_impl_traits_bounds(&self, bounds: ThinVec<TypeBound>) { - self.impl_trait_bounds.borrow_mut().push(bounds); + pub fn update_impl_traits_bounds_from_type_ref(&mut self, type_ref: TypeRefId) { + TypeRef::walk(type_ref, self.types_map, &mut |tr| { + if let TypeRef::ImplTrait(bounds) = tr { + self.impl_trait_bounds.push(bounds.clone()); + } + }); } - pub fn take_impl_traits_bounds(&self) -> Vec<ThinVec<TypeBound>> { - self.impl_trait_bounds.take() + pub fn take_impl_traits_bounds(&mut self) -> Vec<ThinVec<TypeBound>> { + mem::take(&mut self.impl_trait_bounds) } pub(crate) fn outer_impl_trait(&self) -> bool { - *self.outer_impl_trait.borrow() + self.outer_impl_trait } - pub(crate) fn outer_impl_trait_scope<'b>( - &'b self, + pub(crate) fn with_outer_impl_trait_scope<R>( + &mut self, impl_trait: bool, - ) -> OuterImplTraitGuard<'b, 'a> { - OuterImplTraitGuard::new(self, impl_trait) + f: impl FnOnce(&mut Self) -> R, + ) -> R { + let old = mem::replace(&mut self.outer_impl_trait, impl_trait); + let result = f(self); + self.outer_impl_trait = old; + result } - pub(crate) fn alloc_type_ref(&self, type_ref: TypeRef, node: TypePtr) -> TypeRefId { - let mut types_map = self.types_map.borrow_mut(); - let (types_map, types_source_map) = &mut *types_map; - let id = types_map.types.alloc(type_ref); - types_source_map.types_map_back.insert(id, InFile::new(self.file_id, node)); + pub(crate) fn alloc_type_ref(&mut self, type_ref: TypeRef, node: TypePtr) -> TypeRefId { + let id = self.types_map.types.alloc(type_ref); + self.types_source_map.types_map_back.insert(id, InFile::new(self.file_id, node)); id } - pub(crate) fn alloc_type_ref_desugared(&self, type_ref: TypeRef) -> TypeRefId { - self.types_map.borrow_mut().0.types.alloc(type_ref) - } - - pub(crate) fn alloc_error_type(&self) -> TypeRefId { - self.types_map.borrow_mut().0.types.alloc(TypeRef::Error) + pub(crate) fn alloc_type_ref_desugared(&mut self, type_ref: TypeRef) -> TypeRefId { + self.types_map.types.alloc(type_ref) } - // FIXME: If we alloc while holding this, well... Bad Things will happen. Need to change this - // to use proper mutability instead of interior mutability. - pub(crate) fn types_map(&self) -> std::cell::Ref<'_, TypesMap> { - std::cell::Ref::map(self.types_map.borrow(), |it| &*it.0) + pub(crate) fn alloc_error_type(&mut self) -> TypeRefId { + self.types_map.types.alloc(TypeRef::Error) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs index dc6947c5b56..aa2c4a6f1bc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs @@ -121,7 +121,7 @@ pub enum GenericArg { impl Path { /// Converts an `ast::Path` to `Path`. Works with use trees. /// It correctly handles `$crate` based path from macro call. - pub fn from_src(ctx: &LowerCtx<'_>, path: ast::Path) -> Option<Path> { + pub fn from_src(ctx: &mut LowerCtx<'_>, path: ast::Path) -> Option<Path> { lower::lower_path(ctx, path) } @@ -284,7 +284,7 @@ impl<'a> PathSegments<'a> { impl GenericArgs { pub(crate) fn from_ast( - lower_ctx: &LowerCtx<'_>, + lower_ctx: &mut LowerCtx<'_>, node: ast::GenericArgList, ) -> Option<GenericArgs> { lower::lower_generic_args(lower_ctx, node) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs index c328b9c6ce2..553e615b94f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs @@ -19,12 +19,11 @@ use crate::{ /// Converts an `ast::Path` to `Path`. Works with use trees. /// It correctly handles `$crate` based path from macro call. -pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path> { +pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<Path> { let mut kind = PathKind::Plain; let mut type_anchor = None; let mut segments = Vec::new(); let mut generic_args = Vec::new(); - let span_map = ctx.span_map(); loop { let segment = path.segment()?; @@ -37,7 +36,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path if name_ref.text() == "$crate" { break kind = resolve_crate_root( ctx.db.upcast(), - span_map.span_for_range(name_ref.syntax().text_range()).ctx, + ctx.span_map().span_for_range(name_ref.syntax().text_range()).ctx, ) .map(PathKind::DollarCrate) .unwrap_or(PathKind::Crate); @@ -151,7 +150,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path // We follow what it did anyway :) if segments.len() == 1 && kind == PathKind::Plain { if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { - let syn_ctxt = span_map.span_for_range(path.segment()?.syntax().text_range()).ctx; + let syn_ctxt = ctx.span_map().span_for_range(path.segment()?.syntax().text_range()).ctx; if let Some(macro_call_id) = ctx.db.lookup_intern_syntax_context(syn_ctxt).outer_expn { if ctx.db.lookup_intern_macro_call(macro_call_id).def.local_inner { kind = match resolve_crate_root(ctx.db.upcast(), syn_ctxt) { @@ -183,7 +182,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path } pub(super) fn lower_generic_args( - lower_ctx: &LowerCtx<'_>, + lower_ctx: &mut LowerCtx<'_>, node: ast::GenericArgList, ) -> Option<GenericArgs> { let mut args = Vec::new(); @@ -192,13 +191,7 @@ pub(super) fn lower_generic_args( match generic_arg { ast::GenericArg::TypeArg(type_arg) => { let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty()); - let types_map = lower_ctx.types_map(); - TypeRef::walk(type_ref, &types_map, &mut |tr| { - if let TypeRef::ImplTrait(bounds) = tr { - lower_ctx.update_impl_traits_bounds(bounds.clone()); - } - }); - drop(types_map); + lower_ctx.update_impl_traits_bounds_from_type_ref(type_ref); args.push(GenericArg::Type(type_ref)); } ast::GenericArg::AssocTypeArg(assoc_type_arg) => { @@ -208,27 +201,22 @@ pub(super) fn lower_generic_args( } if let Some(name_ref) = assoc_type_arg.name_ref() { // Nested impl traits like `impl Foo<Assoc = impl Bar>` are allowed - let _guard = lower_ctx.outer_impl_trait_scope(false); - let name = name_ref.as_name(); - let args = assoc_type_arg - .generic_arg_list() - .and_then(|args| lower_generic_args(lower_ctx, args)); - let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it)); - let type_ref = type_ref.inspect(|&tr| { - let types_map = lower_ctx.types_map(); - TypeRef::walk(tr, &types_map, &mut |tr| { - if let TypeRef::ImplTrait(bounds) = tr { - lower_ctx.update_impl_traits_bounds(bounds.clone()); - } - }); - drop(types_map); + lower_ctx.with_outer_impl_trait_scope(false, |lower_ctx| { + let name = name_ref.as_name(); + let args = assoc_type_arg + .generic_arg_list() + .and_then(|args| lower_generic_args(lower_ctx, args)); + let type_ref = + assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it)); + let type_ref = type_ref + .inspect(|&tr| lower_ctx.update_impl_traits_bounds_from_type_ref(tr)); + let bounds = if let Some(l) = assoc_type_arg.type_bound_list() { + l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect() + } else { + Box::default() + }; + bindings.push(AssociatedTypeBinding { name, args, type_ref, bounds }); }); - let bounds = if let Some(l) = assoc_type_arg.type_bound_list() { - l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect() - } else { - Box::default() - }; - bindings.push(AssociatedTypeBinding { name, args, type_ref, bounds }); } } ast::GenericArg::LifetimeArg(lifetime_arg) => { @@ -258,7 +246,7 @@ pub(super) fn lower_generic_args( /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y) /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`). fn lower_generic_args_from_fn_path( - ctx: &LowerCtx<'_>, + ctx: &mut LowerCtx<'_>, params: Option<ast::ParamList>, ret_type: Option<ast::RetType>, ) -> Option<GenericArgs> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 4bc78afacc0..05cd7bd37b4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -615,7 +615,7 @@ pub(crate) fn associated_ty_data_query( let type_alias_data = db.type_alias_data(type_alias); let generic_params = generics(db.upcast(), type_alias.into()); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); - let ctx = + let mut ctx = crate::TyLoweringContext::new(db, &resolver, &type_alias_data.types_map, type_alias.into()) .with_type_param_mode(crate::lower::ParamLoweringMode::Variable); @@ -627,14 +627,16 @@ pub(crate) fn associated_ty_data_query( .build(); let self_ty = TyKind::Alias(AliasTy::Projection(pro_ty)).intern(Interner); - let mut bounds: Vec<_> = type_alias_data - .bounds - .iter() - .flat_map(|bound| ctx.lower_type_bound(bound, self_ty.clone(), false)) - .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) - .collect(); + let mut bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, self_ty.clone(), false).for_each(|pred| { + if let Some(pred) = generic_predicate_to_inline_bound(db, &pred, &self_ty) { + bounds.push(pred); + } + }); + } - if !ctx.unsized_types.borrow().contains(&self_ty) { + if !ctx.unsized_types.contains(&self_ty) { let sized_trait = db .lang_item(resolver.krate(), LangItem::Sized) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 091cfcd4654..142766c039b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -8,7 +8,7 @@ use hir_def::{ path::Path, resolver::{Resolver, ValueNs}, type_ref::LiteralConstRef, - ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId, + ConstBlockLoc, EnumVariantId, GeneralConstId, HasModule as _, StaticId, }; use hir_expand::Lookup; use stdx::never; @@ -56,6 +56,21 @@ pub enum ConstEvalError { MirEvalError(MirEvalError), } +impl ConstEvalError { + pub fn pretty_print( + &self, + f: &mut String, + db: &dyn HirDatabase, + span_formatter: impl Fn(span::FileId, span::TextRange) -> String, + edition: span::Edition, + ) -> std::result::Result<(), std::fmt::Error> { + match self { + ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter, edition), + ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter, edition), + } + } +} + impl From<MirLowerError> for ConstEvalError { fn from(value: MirLowerError) -> Self { match value { @@ -236,6 +251,10 @@ pub(crate) fn const_eval_query( GeneralConstId::ConstId(c) => { db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))? } + GeneralConstId::StaticId(s) => { + let krate = s.module(db.upcast()).krate(); + db.monomorphized_mir_body(s.into(), subst, TraitEnvironment::empty(krate))? + } GeneralConstId::ConstBlockId(c) => { let ConstBlockLoc { parent, root } = db.lookup_intern_anonymous_const(c); let body = db.body(parent); @@ -249,7 +268,7 @@ pub(crate) fn const_eval_query( } GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?, }; - let c = interpret_mir(db, body, false, trait_env).0?; + let c = interpret_mir(db, body, false, trait_env)?.0?; Ok(c) } @@ -262,7 +281,7 @@ pub(crate) fn const_eval_static_query( Substitution::empty(Interner), db.trait_environment_for_body(def.into()), )?; - let c = interpret_mir(db, body, false, None).0?; + let c = interpret_mir(db, body, false, None)?.0?; Ok(c) } @@ -294,7 +313,7 @@ pub(crate) fn const_eval_discriminant_variant( Substitution::empty(Interner), db.trait_environment_for_body(def), )?; - let c = interpret_mir(db, mir_body, false, None).0?; + let c = interpret_mir(db, mir_body, false, None)?.0?; let c = if is_signed { try_const_isize(db, &c).unwrap() } else { @@ -335,7 +354,7 @@ pub(crate) fn eval_to_const( } } if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true, None).0 { + if let Ok((Ok(result), _)) = interpret_mir(db, Arc::new(mir_body), true, None) { return result; } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 277dabe9aa3..4e95bdf219f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -2033,7 +2033,7 @@ impl HirDisplayWithTypesMap for TypeRefId { TypeRef::Macro(macro_call) => { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = hir_def::lower::LowerCtx::new( + let mut ctx = hir_def::lower::LowerCtx::new( f.db.upcast(), macro_call.file_id, &mut types_map, @@ -2041,7 +2041,7 @@ impl HirDisplayWithTypesMap for TypeRefId { ); let macro_call = macro_call.to_node(f.db.upcast()); match macro_call.path() { - Some(path) => match Path::from_src(&ctx, path) { + Some(path) => match Path::from_src(&mut ctx, path) { Some(path) => path.hir_fmt(f, &types_map)?, None => write!(f, "{{macro}}")?, }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 3685ed56964..01e0b635b22 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1420,7 +1420,7 @@ impl<'a> InferenceContext<'a> { Some(path) => path, None => return (self.err_ty(), None), }; - let ctx = crate::lower::TyLoweringContext::new( + let mut ctx = crate::lower::TyLoweringContext::new( self.db, &self.resolver, &self.body.types, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 442daa9f9ee..7550d197a3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -151,7 +151,7 @@ impl InferenceContext<'_> { let last = path.segments().last()?; // Don't use `self.make_ty()` here as we need `orig_ns`. - let ctx = crate::lower::TyLoweringContext::new( + let mut ctx = crate::lower::TyLoweringContext::new( self.db, &self.resolver, &self.body.types, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index c5fa20bc8ac..0c1f63880cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -6,8 +6,9 @@ use base_db::ra_salsa::Cycle; use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ - BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, - Primitive, ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, + BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, LayoutCalculatorError, + LayoutData, Primitive, ReprOptions, Scalar, Size, StructKind, TargetDataLayout, + WrappingRange, }, LocalFieldId, StructId, }; @@ -294,11 +295,12 @@ pub fn layout_of_ty_query( .checked_mul(count, dl) .ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?; - let backend_repr = if count != 0 && matches!(element.backend_repr, BackendRepr::Uninhabited) { - BackendRepr::Uninhabited - } else { - BackendRepr::Memory { sized: true } - }; + let backend_repr = + if count != 0 && matches!(element.backend_repr, BackendRepr::Uninhabited) { + BackendRepr::Uninhabited + } else { + BackendRepr::Memory { sized: true } + }; let largest_niche = if count != 0 { element.largest_niche } else { None }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index e3a92e52f61..b868ea95f85 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -6,8 +6,8 @@ //! //! This usually involves resolving names, collecting generic arguments etc. use std::{ - cell::{Cell, OnceCell, RefCell, RefMut}, - iter, + cell::OnceCell, + iter, mem, ops::{self, Not as _}, }; @@ -72,47 +72,32 @@ use crate::{ TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; -#[derive(Debug)] -enum ImplTraitLoweringState { +#[derive(Debug, Default)] +struct ImplTraitLoweringState { /// When turning `impl Trait` into opaque types, we have to collect the /// bounds at the same time to get the IDs correct (without becoming too - /// complicated). I don't like using interior mutability (as for the - /// counter), but I've tried and failed to make the lifetimes work for - /// passing around a `&mut TyLoweringContext`. The core problem is that - /// we're grouping the mutable data (the counter and this field) together - /// with the immutable context (the references to the DB and resolver). - /// Splitting this up would be a possible fix. - Opaque(RefCell<Arena<ImplTrait>>), - Param(Cell<u16>), - Variable(Cell<u16>), - Disallowed, + /// complicated). + mode: ImplTraitLoweringMode, + // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. + opaque_type_data: Arena<ImplTrait>, + param_and_variable_counter: u16, } impl ImplTraitLoweringState { - fn new(impl_trait_mode: ImplTraitLoweringMode) -> ImplTraitLoweringState { - match impl_trait_mode { - ImplTraitLoweringMode::Opaque => Self::Opaque(RefCell::new(Arena::new())), - ImplTraitLoweringMode::Param => Self::Param(Cell::new(0)), - ImplTraitLoweringMode::Variable => Self::Variable(Cell::new(0)), - ImplTraitLoweringMode::Disallowed => Self::Disallowed, - } + fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState { + Self { mode, opaque_type_data: Arena::new(), param_and_variable_counter: 0 } } - - fn take(&self) -> Self { - match self { - Self::Opaque(x) => Self::Opaque(RefCell::new(x.take())), - Self::Param(x) => Self::Param(Cell::new(x.get())), - Self::Variable(x) => Self::Variable(Cell::new(x.get())), - Self::Disallowed => Self::Disallowed, + fn param(counter: u16) -> Self { + Self { + mode: ImplTraitLoweringMode::Param, + opaque_type_data: Arena::new(), + param_and_variable_counter: counter, } } - - fn swap(&self, impl_trait_mode: &Self) { - match (self, impl_trait_mode) { - (Self::Opaque(x), Self::Opaque(y)) => x.swap(y), - (Self::Param(x), Self::Param(y)) => x.swap(y), - (Self::Variable(x), Self::Variable(y)) => x.swap(y), - (Self::Disallowed, Self::Disallowed) => (), - _ => panic!("mismatched lowering mode"), + fn variable(counter: u16) -> Self { + Self { + mode: ImplTraitLoweringMode::Variable, + opaque_type_data: Arena::new(), + param_and_variable_counter: counter, } } } @@ -137,9 +122,9 @@ pub struct TyLoweringContext<'a> { /// possible currently, so this should be fine for now. pub type_param_mode: ParamLoweringMode, impl_trait_mode: ImplTraitLoweringState, - expander: RefCell<Option<Expander>>, + expander: Option<Expander>, /// Tracks types with explicit `?Sized` bounds. - pub(crate) unsized_types: RefCell<FxHashSet<Ty>>, + pub(crate) unsized_types: FxHashSet<Ty>, } impl<'a> TyLoweringContext<'a> { @@ -159,7 +144,7 @@ impl<'a> TyLoweringContext<'a> { types_source_map: Option<&'a TypesSourceMap>, owner: Option<TypeOwnerId>, ) -> Self { - let impl_trait_mode = ImplTraitLoweringState::Disallowed; + let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); let type_param_mode = ParamLoweringMode::Placeholder; let in_binders = DebruijnIndex::INNERMOST; Self { @@ -172,38 +157,26 @@ impl<'a> TyLoweringContext<'a> { in_binders, impl_trait_mode, type_param_mode, - expander: RefCell::new(None), - unsized_types: RefCell::default(), + expander: None, + unsized_types: FxHashSet::default(), } } pub fn with_debruijn<T>( - &self, + &mut self, debruijn: DebruijnIndex, - f: impl FnOnce(&TyLoweringContext<'_>) -> T, + f: impl FnOnce(&mut TyLoweringContext<'_>) -> T, ) -> T { - let impl_trait_mode = self.impl_trait_mode.take(); - let expander = self.expander.take(); - let unsized_types = self.unsized_types.take(); - let new_ctx = Self { - in_binders: debruijn, - impl_trait_mode, - expander: RefCell::new(expander), - unsized_types: RefCell::new(unsized_types), - generics: self.generics.clone(), - ..*self - }; - let result = f(&new_ctx); - self.impl_trait_mode.swap(&new_ctx.impl_trait_mode); - self.expander.replace(new_ctx.expander.into_inner()); - self.unsized_types.replace(new_ctx.unsized_types.into_inner()); + let old_debruijn = mem::replace(&mut self.in_binders, debruijn); + let result = f(self); + self.in_binders = old_debruijn; result } pub fn with_shifted_in<T>( - &self, + &mut self, debruijn: DebruijnIndex, - f: impl FnOnce(&TyLoweringContext<'_>) -> T, + f: impl FnOnce(&mut TyLoweringContext<'_>) -> T, ) -> T { self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f) } @@ -227,7 +200,7 @@ impl<'a> TyLoweringContext<'a> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] pub enum ImplTraitLoweringMode { /// `impl Trait` gets lowered into an opaque type that doesn't unify with /// anything except itself. This is used in places where values flow 'out', @@ -244,6 +217,7 @@ pub enum ImplTraitLoweringMode { /// currently checking. Variable, /// `impl Trait` is disallowed and will be an error. + #[default] Disallowed, } @@ -254,12 +228,13 @@ pub enum ParamLoweringMode { } impl<'a> TyLoweringContext<'a> { - pub fn lower_ty(&self, type_ref: TypeRefId) -> Ty { + pub fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty { self.lower_ty_ext(type_ref).0 } - pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const { + pub fn lower_const(&mut self, const_ref: &ConstRef, const_type: Ty) -> Const { let Some(owner) = self.owner else { return unknown_const(const_type) }; + let debruijn = self.in_binders; const_or_path_to_chalk( self.db, self.resolver, @@ -268,7 +243,7 @@ impl<'a> TyLoweringContext<'a> { const_ref, self.type_param_mode, || self.generics(), - self.in_binders, + debruijn, ) } @@ -278,7 +253,7 @@ impl<'a> TyLoweringContext<'a> { .as_ref() } - pub fn lower_ty_ext(&self, type_ref_id: TypeRefId) -> (Ty, Option<TypeNs>) { + pub fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty, Option<TypeNs>) { let mut res = None; let type_ref = &self.types_map[type_ref_id]; let ty = match type_ref { @@ -337,8 +312,8 @@ impl<'a> TyLoweringContext<'a> { } TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { - match &self.impl_trait_mode { - ImplTraitLoweringState::Opaque(opaque_type_data) => { + match self.impl_trait_mode.mode { + ImplTraitLoweringMode::Opaque => { let origin = match self.resolver.generic_def() { Some(GenericDefId::FunctionId(it)) => Either::Left(it), Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), @@ -350,7 +325,7 @@ impl<'a> TyLoweringContext<'a> { // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { + let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { bounds: crate::make_single_type_binders(Vec::default()), }); // We don't want to lower the bounds inside the binders @@ -366,7 +341,7 @@ impl<'a> TyLoweringContext<'a> { .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { ctx.lower_impl_trait(bounds, self.resolver.krate()) }); - opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data; + self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; let impl_trait_id = origin.either( |f| ImplTraitId::ReturnTypeImplTrait(f, idx), @@ -378,11 +353,13 @@ impl<'a> TyLoweringContext<'a> { let parameters = generics.bound_vars_subst(self.db, self.in_binders); TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) } - ImplTraitLoweringState::Param(counter) => { - let idx = counter.get(); + ImplTraitLoweringMode::Param => { + let idx = self.impl_trait_mode.param_and_variable_counter; // Count the number of `impl Trait` things that appear within our bounds. // Since those have been emitted as implicit type args already. - counter.set(idx + self.count_impl_traits(type_ref_id) as u16); + self.impl_trait_mode.param_and_variable_counter = + idx + self.count_impl_traits(type_ref_id) as u16; + let db = self.db; let kind = self .generics() .expect("param impl trait lowering must be in a generic def") @@ -398,15 +375,17 @@ impl<'a> TyLoweringContext<'a> { }) .nth(idx as usize) .map_or(TyKind::Error, |id| { - TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) + TyKind::Placeholder(to_placeholder_idx(db, id.into())) }); kind.intern(Interner) } - ImplTraitLoweringState::Variable(counter) => { - let idx = counter.get(); + ImplTraitLoweringMode::Variable => { + let idx = self.impl_trait_mode.param_and_variable_counter; // Count the number of `impl Trait` things that appear within our bounds. // Since t hose have been emitted as implicit type args already. - counter.set(idx + self.count_impl_traits(type_ref_id) as u16); + self.impl_trait_mode.param_and_variable_counter = + idx + self.count_impl_traits(type_ref_id) as u16; + let debruijn = self.in_binders; let kind = self .generics() .expect("variable impl trait lowering must be in a generic def") @@ -423,33 +402,31 @@ impl<'a> TyLoweringContext<'a> { }) .nth(idx as usize) .map_or(TyKind::Error, |id| { - TyKind::BoundVar(BoundVar { debruijn: self.in_binders, index: id }) + TyKind::BoundVar(BoundVar { debruijn, index: id }) }); kind.intern(Interner) } - ImplTraitLoweringState::Disallowed => { + ImplTraitLoweringMode::Disallowed => { // FIXME: report error TyKind::Error.intern(Interner) } } } TypeRef::Macro(macro_call) => { - let (mut expander, recursion_start) = { - match RefMut::filter_map(self.expander.borrow_mut(), Option::as_mut) { + let (expander, recursion_start) = { + match &mut self.expander { // There already is an expander here, this means we are already recursing - Ok(expander) => (expander, false), + Some(expander) => (expander, false), // No expander was created yet, so we are at the start of the expansion recursion // and therefore have to create an expander. - Err(expander) => ( - RefMut::map(expander, |it| { - it.insert(Expander::new( - self.db.upcast(), - macro_call.file_id, - self.resolver.module(), - )) - }), - true, - ), + None => { + let expander = self.expander.insert(Expander::new( + self.db.upcast(), + macro_call.file_id, + self.resolver.module(), + )); + (expander, true) + } } }; let ty = { @@ -465,19 +442,16 @@ impl<'a> TyLoweringContext<'a> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = expander.ctx( + let mut ctx = expander.ctx( self.db.upcast(), &mut types_map, &mut types_source_map, ); // FIXME: Report syntax errors in expansion here - let type_ref = TypeRef::from_ast(&ctx, expanded.tree()); + let type_ref = TypeRef::from_ast(&mut ctx, expanded.tree()); - drop(expander); - - // FIXME: That may be better served by mutating `self` then restoring, but this requires - // making it `&mut self`. - let inner_ctx = TyLoweringContext { + // Can't mutate `self`, must create a new instance, because of the lifetimes. + let mut inner_ctx = TyLoweringContext { db: self.db, resolver: self.resolver, generics: self.generics.clone(), @@ -486,30 +460,27 @@ impl<'a> TyLoweringContext<'a> { in_binders: self.in_binders, owner: self.owner, type_param_mode: self.type_param_mode, - impl_trait_mode: self.impl_trait_mode.take(), - expander: RefCell::new(self.expander.take()), - unsized_types: RefCell::new(self.unsized_types.take()), + impl_trait_mode: mem::take(&mut self.impl_trait_mode), + expander: self.expander.take(), + unsized_types: mem::take(&mut self.unsized_types), }; let ty = inner_ctx.lower_ty(type_ref); - self.impl_trait_mode.swap(&inner_ctx.impl_trait_mode); - *self.expander.borrow_mut() = inner_ctx.expander.into_inner(); - *self.unsized_types.borrow_mut() = inner_ctx.unsized_types.into_inner(); + self.impl_trait_mode = inner_ctx.impl_trait_mode; + self.expander = inner_ctx.expander; + self.unsized_types = inner_ctx.unsized_types; - self.expander.borrow_mut().as_mut().unwrap().exit(mark); + self.expander.as_mut().unwrap().exit(mark); Some(ty) } - _ => { - drop(expander); - None - } + _ => None, } }; // drop the expander, resetting it to pre-recursion state if recursion_start { - *self.expander.borrow_mut() = None; + self.expander = None; } ty.unwrap_or_else(|| TyKind::Error.intern(Interner)) } @@ -544,7 +515,7 @@ impl<'a> TyLoweringContext<'a> { } pub(crate) fn lower_ty_relative_path( - &self, + &mut self, ty: Ty, // We need the original resolution to lower `Self::AssocTy` correctly res: Option<TypeNs>, @@ -565,7 +536,7 @@ impl<'a> TyLoweringContext<'a> { } pub(crate) fn lower_partly_resolved_path( - &self, + &mut self, resolution: TypeNs, resolved_segment: PathSegment<'_>, remaining_segments: PathSegments<'_>, @@ -706,7 +677,7 @@ impl<'a> TyLoweringContext<'a> { self.lower_ty_relative_path(ty, Some(resolution), remaining_segments) } - pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) { + pub(crate) fn lower_path(&mut self, path: &Path) -> (Ty, Option<TypeNs>) { // Resolve the path (in type namespace) if let Some(type_ref) = path.type_anchor() { let (ty, res) = self.lower_ty_ext(type_ref); @@ -736,7 +707,7 @@ impl<'a> TyLoweringContext<'a> { self.lower_partly_resolved_path(resolution, resolved_segment, remaining_segments, false) } - fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty { + fn select_associated_type(&mut self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty { let Some((generics, res)) = self.generics().zip(res) else { return TyKind::Error.intern(Interner); }; @@ -746,6 +717,8 @@ impl<'a> TyLoweringContext<'a> { res, Some(segment.name.clone()), move |name, t, associated_ty| { + let generics = self.generics().unwrap(); + if name != segment.name { return None; } @@ -797,7 +770,7 @@ impl<'a> TyLoweringContext<'a> { } fn lower_path_inner( - &self, + &mut self, segment: PathSegment<'_>, typeable: TyDefId, infer_args: bool, @@ -814,7 +787,7 @@ impl<'a> TyLoweringContext<'a> { /// Collect generic arguments from a path into a `Substs`. See also /// `create_substs_for_ast_path` and `def_to_ty` in rustc. pub(super) fn substs_from_path( - &self, + &mut self, path: &Path, // Note that we don't call `db.value_type(resolved)` here, // `ValueTyDefId` is just a convenient way to pass generics and @@ -855,7 +828,7 @@ impl<'a> TyLoweringContext<'a> { } pub(super) fn substs_from_path_segment( - &self, + &mut self, segment: PathSegment<'_>, def: Option<GenericDefId>, infer_args: bool, @@ -870,7 +843,7 @@ impl<'a> TyLoweringContext<'a> { } fn substs_from_args_and_bindings( - &self, + &mut self, args_and_bindings: Option<&GenericArgs>, def: Option<GenericDefId>, infer_args: bool, @@ -959,11 +932,11 @@ impl<'a> TyLoweringContext<'a> { self.db, id, arg, - &mut (), + self, self.types_map, - |_, type_ref| self.lower_ty(type_ref), - |_, const_ref, ty| self.lower_const(const_ref, ty), - |_, lifetime_ref| self.lower_lifetime(lifetime_ref), + |this, type_ref| this.lower_ty(type_ref), + |this, const_ref, ty| this.lower_const(const_ref, ty), + |this, lifetime_ref| this.lower_lifetime(lifetime_ref), ); substs.push(arg); } @@ -1016,7 +989,7 @@ impl<'a> TyLoweringContext<'a> { } pub(crate) fn lower_trait_ref_from_resolved_path( - &self, + &mut self, resolved: TraitId, segment: PathSegment<'_>, explicit_self_ty: Ty, @@ -1025,7 +998,7 @@ impl<'a> TyLoweringContext<'a> { TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } } - fn lower_trait_ref_from_path(&self, path: &Path, explicit_self_ty: Ty) -> Option<TraitRef> { + fn lower_trait_ref_from_path(&mut self, path: &Path, explicit_self_ty: Ty) -> Option<TraitRef> { let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { // FIXME(trait_alias): We need to handle trait alias here. TypeNs::TraitId(tr) => tr, @@ -1035,12 +1008,16 @@ impl<'a> TyLoweringContext<'a> { Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty)) } - fn lower_trait_ref(&self, trait_ref: &HirTraitRef, explicit_self_ty: Ty) -> Option<TraitRef> { + fn lower_trait_ref( + &mut self, + trait_ref: &HirTraitRef, + explicit_self_ty: Ty, + ) -> Option<TraitRef> { self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty) } fn trait_ref_substs_from_path( - &self, + &mut self, segment: PathSegment<'_>, resolved: TraitId, explicit_self_ty: Ty, @@ -1049,11 +1026,11 @@ impl<'a> TyLoweringContext<'a> { } pub(crate) fn lower_where_predicate<'b>( - &'b self, + &'b mut self, where_predicate: &'b WherePredicate, &def: &GenericDefId, ignore_bindings: bool, - ) -> impl Iterator<Item = QuantifiedWhereClause> + 'b { + ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'a, 'b> { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { @@ -1087,12 +1064,12 @@ impl<'a> TyLoweringContext<'a> { .into_iter() } - pub(crate) fn lower_type_bound( - &'a self, - bound: &'a TypeBound, + pub(crate) fn lower_type_bound<'b>( + &'b mut self, + bound: &'b TypeBound, self_ty: Ty, ignore_bindings: bool, - ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a { + ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> { let mut trait_ref = None; let clause = match bound { TypeBound::Path(path, TraitBoundModifier::None) => { @@ -1111,7 +1088,7 @@ impl<'a> TyLoweringContext<'a> { .lower_trait_ref_from_path(path, self_ty.clone()) .map(|trait_ref| trait_ref.hir_trait_id()); if trait_id == sized_trait { - self.unsized_types.borrow_mut().insert(self_ty); + self.unsized_types.insert(self_ty); } None } @@ -1131,17 +1108,18 @@ impl<'a> TyLoweringContext<'a> { }; clause.into_iter().chain( trait_ref - .into_iter() .filter(move |_| !ignore_bindings) - .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)), + .map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)) + .into_iter() + .flatten(), ) } - fn assoc_type_bindings_from_type_bound( - &'a self, - bound: &'a TypeBound, + fn assoc_type_bindings_from_type_bound<'b>( + &'b mut self, + bound: &'b TypeBound, trait_ref: TraitRef, - ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a { + ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> { let last_segment = match bound { TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => { path.segments().last() @@ -1192,22 +1170,16 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = binding.type_ref { - match (&self.types_map[type_ref], &self.impl_trait_mode) { - (TypeRef::ImplTrait(_), ImplTraitLoweringState::Disallowed) => (), - ( - _, - ImplTraitLoweringState::Disallowed | ImplTraitLoweringState::Opaque(_), - ) => { + match (&self.types_map[type_ref], self.impl_trait_mode.mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), + (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { let ty = self.lower_ty(type_ref); let alias_eq = AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; predicates .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); } - ( - _, - ImplTraitLoweringState::Param(_) | ImplTraitLoweringState::Variable(_), - ) => { + (_, ImplTraitLoweringMode::Param | ImplTraitLoweringMode::Variable) => { // Find the generic index for the target of our `bound` let target_param_idx = self .resolver @@ -1244,14 +1216,14 @@ impl<'a> TyLoweringContext<'a> { self.owner, ) .with_type_param_mode(self.type_param_mode); - match &self.impl_trait_mode { - ImplTraitLoweringState::Param(_) => { + match self.impl_trait_mode.mode { + ImplTraitLoweringMode::Param => { ext.impl_trait_mode = - ImplTraitLoweringState::Param(Cell::new(counter)); + ImplTraitLoweringState::param(counter); } - ImplTraitLoweringState::Variable(_) => { + ImplTraitLoweringMode::Variable => { ext.impl_trait_mode = - ImplTraitLoweringState::Variable(Cell::new(counter)); + ImplTraitLoweringState::variable(counter); } _ => unreachable!(), } @@ -1278,7 +1250,7 @@ impl<'a> TyLoweringContext<'a> { }) } - fn lower_dyn_trait(&self, bounds: &[TypeBound]) -> Ty { + fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty { let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); // INVARIANT: The principal trait bound, if present, must come first. Others may be in any // order but should be in the same order for the same set but possibly different order of @@ -1287,22 +1259,26 @@ impl<'a> TyLoweringContext<'a> { // These invariants are utilized by `TyExt::dyn_trait()` and chalk. let mut lifetime = None; let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - let mut bounds: Vec<_> = bounds - .iter() - .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)) - .filter(|b| match b.skip_binders() { - WhereClause::Implemented(_) | WhereClause::AliasEq(_) => true, - WhereClause::LifetimeOutlives(_) => false, - WhereClause::TypeOutlives(t) => { - lifetime = Some(t.lifetime.clone()); - false + let mut lowered_bounds = Vec::new(); + for b in bounds { + ctx.lower_type_bound(b, self_ty.clone(), false).for_each(|b| { + let filter = match b.skip_binders() { + WhereClause::Implemented(_) | WhereClause::AliasEq(_) => true, + WhereClause::LifetimeOutlives(_) => false, + WhereClause::TypeOutlives(t) => { + lifetime = Some(t.lifetime.clone()); + false + } + }; + if filter { + lowered_bounds.push(b); } - }) - .collect(); + }); + } let mut multiple_regular_traits = false; let mut multiple_same_projection = false; - bounds.sort_unstable_by(|lhs, rhs| { + lowered_bounds.sort_unstable_by(|lhs, rhs| { use std::cmp::Ordering; match (lhs.skip_binders(), rhs.skip_binders()) { (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => { @@ -1344,13 +1320,13 @@ impl<'a> TyLoweringContext<'a> { return None; } - bounds.first().and_then(|b| b.trait_id())?; + lowered_bounds.first().and_then(|b| b.trait_id())?; // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the // bounds. We shouldn't have repeated elements besides auto traits at this point. - bounds.dedup(); + lowered_bounds.dedup(); - Some(QuantifiedWhereClauses::from_iter(Interner, bounds)) + Some(QuantifiedWhereClauses::from_iter(Interner, lowered_bounds)) }); if let Some(bounds) = bounds { @@ -1376,16 +1352,16 @@ impl<'a> TyLoweringContext<'a> { } } - fn lower_impl_trait(&self, bounds: &[TypeBound], krate: CrateId) -> ImplTrait { + fn lower_impl_trait(&mut self, bounds: &[TypeBound], krate: CrateId) -> ImplTrait { cov_mark::hit!(lower_rpit); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - let mut predicates: Vec<_> = bounds - .iter() - .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)) - .collect(); + let mut predicates = Vec::new(); + for b in bounds { + predicates.extend(ctx.lower_type_bound(b, self_ty.clone(), false)); + } - if !ctx.unsized_types.borrow().contains(&self_ty) { + if !ctx.unsized_types.contains(&self_ty) { let sized_trait = ctx .db .lang_item(krate, LangItem::Sized) @@ -1562,7 +1538,7 @@ pub(crate) fn field_types_query( }; let generics = generics(db.upcast(), def); let mut res = ArenaMap::default(); - let ctx = TyLoweringContext::new(db, &resolver, var_data.types_map(), def.into()) + let mut ctx = TyLoweringContext::new(db, &resolver, var_data.types_map(), def.into()) .with_type_param_mode(ParamLoweringMode::Variable); for (field_id, field_data) in var_data.fields().iter() { res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref))); @@ -1596,7 +1572,7 @@ pub(crate) fn generic_predicates_for_param_query( let generics = generics(db.upcast(), def); // we have to filter out all other predicates *first*, before attempting to lower them - let predicate = |pred: &_, def: &_, ctx: &TyLoweringContext<'_>| match pred { + let predicate = |pred: &_, def: &_, ctx: &mut TyLoweringContext<'_>| match pred { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound, .. } => { let invalid_target = match target { @@ -1642,16 +1618,19 @@ pub(crate) fn generic_predicates_for_param_query( let mut predicates = Vec::new(); for (params, def) in resolver.all_generic_params() { ctx.types_map = ¶ms.types_map; - predicates.extend( - params.where_predicates().filter(|pred| predicate(pred, def, &ctx)).flat_map(|pred| { - ctx.lower_where_predicate(pred, def, true).map(|p| make_binders(db, &generics, p)) - }), - ); + for pred in params.where_predicates() { + if predicate(pred, def, &mut ctx) { + predicates.extend( + ctx.lower_where_predicate(pred, def, true) + .map(|p| make_binders(db, &generics, p)), + ); + } + } } let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); if !subst.is_empty(Interner) { - let explicitly_unsized_tys = ctx.unsized_types.into_inner(); + let explicitly_unsized_tys = ctx.unsized_types; if let Some(implicitly_sized_predicates) = implicitly_sized_clauses( db, param_id.parent, @@ -1731,7 +1710,7 @@ pub(crate) fn trait_environment_query( let subst = generics(db.upcast(), def).placeholder_subst(db); if !subst.is_empty(Interner) { - let explicitly_unsized_tys = ctx.unsized_types.into_inner(); + let explicitly_unsized_tys = ctx.unsized_types; if let Some(implicitly_sized_clauses) = implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) { @@ -1801,16 +1780,19 @@ where let mut predicates = Vec::new(); for (params, def) in resolver.all_generic_params() { ctx.types_map = ¶ms.types_map; - predicates.extend(params.where_predicates().filter(|pred| filter(pred, def)).flat_map( - |pred| { - ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p)) - }, - )); + for pred in params.where_predicates() { + if filter(pred, def) { + predicates.extend( + ctx.lower_where_predicate(pred, def, false) + .map(|p| make_binders(db, &generics, p)), + ); + } + } } if generics.len() > 0 { let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let explicitly_unsized_tys = ctx.unsized_types.into_inner(); + let explicitly_unsized_tys = ctx.unsized_types; if let Some(implicitly_sized_predicates) = implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) { @@ -1906,7 +1888,8 @@ pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> let mut val = p.default.as_ref().map_or_else( || unknown_const_as_generic(db.const_param_ty(id)), |c| { - let c = ctx.lower_const(c, ctx.lower_ty(p.ty)); + let param_ty = ctx.lower_ty(p.ty); + let c = ctx.lower_const(c, param_ty); c.cast(Interner) }, ); @@ -1946,11 +1929,11 @@ pub(crate) fn generic_defaults_recover( fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { let data = db.function_data(def); let resolver = def.resolver(db.upcast()); - let ctx_params = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) + let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) .with_impl_trait_mode(ImplTraitLoweringMode::Variable) .with_type_param_mode(ParamLoweringMode::Variable); let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); - let ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) + let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let ret = ctx_ret.lower_ty(data.ret_type); @@ -1982,7 +1965,7 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> { let data = db.const_data(def); let generics = generics(db.upcast(), def.into()); let resolver = def.resolver(db.upcast()); - let ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) + let mut ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) .with_type_param_mode(ParamLoweringMode::Variable); make_binders(db, &generics, ctx.lower_ty(data.type_ref)) @@ -1992,7 +1975,7 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> { fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> { let data = db.static_data(def); let resolver = def.resolver(db.upcast()); - let ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()); + let mut ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()); Binders::empty(Interner, ctx.lower_ty(data.type_ref)) } @@ -2001,7 +1984,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS let struct_data = db.struct_data(def); let fields = struct_data.variant_data.fields(); let resolver = def.resolver(db.upcast()); - let ctx = TyLoweringContext::new( + let mut ctx = TyLoweringContext::new( db, &resolver, struct_data.variant_data.types_map(), @@ -2038,7 +2021,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) let var_data = db.enum_variant_data(def); let fields = var_data.variant_data.fields(); let resolver = def.resolver(db.upcast()); - let ctx = TyLoweringContext::new( + let mut ctx = TyLoweringContext::new( db, &resolver, var_data.variant_data.types_map(), @@ -2087,7 +2070,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { let generics = generics(db.upcast(), t.into()); let resolver = t.resolver(db.upcast()); let type_alias_data = db.type_alias_data(t); - let ctx = TyLoweringContext::new(db, &resolver, &type_alias_data.types_map, t.into()) + let mut ctx = TyLoweringContext::new(db, &resolver, &type_alias_data.types_map, t.into()) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let inner = if type_alias_data.is_extern { @@ -2169,7 +2152,7 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); let generics = generics(db.upcast(), impl_id.into()); - let ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into()) + let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into()) .with_type_param_mode(ParamLoweringMode::Variable); make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)) } @@ -2179,7 +2162,8 @@ pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> T let parent_data = db.generic_params(def.parent()); let data = &parent_data[def.local_id()]; let resolver = def.parent().resolver(db.upcast()); - let ctx = TyLoweringContext::new(db, &resolver, &parent_data.types_map, def.parent().into()); + let mut ctx = + TyLoweringContext::new(db, &resolver, &parent_data.types_map, def.parent().into()); match data { TypeOrConstParamData::TypeParamData(_) => { never!(); @@ -2201,7 +2185,7 @@ pub(crate) fn impl_self_ty_recover( pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> { let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); - let ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into()) + let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into()) .with_type_param_mode(ParamLoweringMode::Variable); let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); let target_trait = impl_data.target_trait.as_ref()?; @@ -2215,17 +2199,13 @@ pub(crate) fn return_type_impl_traits( // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = db.function_data(def); let resolver = def.resolver(db.upcast()); - let ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) + let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let _ret = ctx_ret.lower_ty(data.ret_type); let generics = generics(db.upcast(), def.into()); - let return_type_impl_traits = ImplTraits { - impl_traits: match ctx_ret.impl_trait_mode { - ImplTraitLoweringState::Opaque(x) => x.into_inner(), - _ => unreachable!(), - }, - }; + let return_type_impl_traits = + ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; if return_type_impl_traits.impl_traits.is_empty() { None } else { @@ -2239,18 +2219,13 @@ pub(crate) fn type_alias_impl_traits( ) -> Option<Arc<Binders<ImplTraits>>> { let data = db.type_alias_data(def); let resolver = def.resolver(db.upcast()); - let ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) + let mut ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into()) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); if let Some(type_ref) = data.type_ref { let _ty = ctx.lower_ty(type_ref); } - let type_alias_impl_traits = ImplTraits { - impl_traits: match ctx.impl_trait_mode { - ImplTraitLoweringState::Opaque(x) => x.into_inner(), - _ => unreachable!(), - }, - }; + let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; if type_alias_impl_traits.impl_traits.is_empty() { None } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index e73b9dc27d1..d7029651fc1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -12,8 +12,8 @@ use hir_def::{ lang_item::LangItem, layout::{TagEncoding, Variants}, resolver::{HasResolver, TypeNs, ValueNs}, - AdtId, ConstId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, - StaticId, VariantId, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, + VariantId, }; use hir_expand::{mod_path::path, name::Name, HirFileIdExt, InFile}; use intern::sym; @@ -40,8 +40,8 @@ use crate::{ static_lifetime, traits::FnTrait, utils::{detect_variant_from_bytes, ClosureSubst}, - CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstScalar, FnDefId, Interner, MemoryMap, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, + CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, Interner, + MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -585,13 +585,9 @@ pub fn interpret_mir( // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, trait_env: Option<Arc<TraitEnvironment>>, -) -> (Result<Const>, MirOutput) { +) -> Result<(Result<Const>, MirOutput)> { let ty = body.locals[return_slot()].ty.clone(); - let mut evaluator = - match Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env) { - Ok(it) => it, - Err(e) => return (Err(e), MirOutput { stdout: vec![], stderr: vec![] }), - }; + let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?; let it: Result<Const> = (|| { if evaluator.ptr_size() != std::mem::size_of::<usize>() { not_supported!("targets with different pointer size from host"); @@ -613,7 +609,7 @@ pub fn interpret_mir( }; Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)) })(); - (it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr }) + Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })) } #[cfg(test)] @@ -1899,8 +1895,8 @@ impl Evaluator<'_> { #[allow(clippy::double_parens)] fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<Interval> { - let ty = &konst.data(Interner).ty; - let chalk_ir::ConstValue::Concrete(c) = &konst.data(Interner).value else { + let ConstData { ty, value: chalk_ir::ConstValue::Concrete(c) } = &konst.data(Interner) + else { not_supported!("evaluating non concrete constant"); }; let result_owner; @@ -2908,14 +2904,14 @@ impl Evaluator<'_> { pub fn render_const_using_debug_impl( db: &dyn HirDatabase, - owner: ConstId, + owner: DefWithBodyId, c: &Const, ) -> Result<String> { - let mut evaluator = Evaluator::new(db, owner.into(), false, None)?; + let mut evaluator = Evaluator::new(db, owner, false, None)?; let locals = &Locals { ptr: ArenaMap::new(), body: db - .mir_body(owner.into()) + .mir_body(owner) .map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?, drop_flags: DropFlags::default(), }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 595a78da10f..30d11373732 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -32,7 +32,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), ) .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; - let (result, output) = interpret_mir(db, body, false, None); + let (result, output) = interpret_mir(db, body, false, None)?; result?; Ok((output.stdout().into_owned(), output.stderr().into_owned())) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 88eb3b127e0..ebd84fd2be2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -34,7 +34,10 @@ pub mod term_search; mod display; -use std::{mem::discriminant, ops::ControlFlow}; +use std::{ + mem::discriminant, + ops::{ControlFlow, Not}, +}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin}; @@ -2303,22 +2306,15 @@ impl Function { self, db: &dyn HirDatabase, span_formatter: impl Fn(FileId, TextRange) -> String, - ) -> String { + ) -> Result<String, ConstEvalError> { let krate = HasModule::krate(&self.id, db.upcast()); let edition = db.crate_graph()[krate].edition; - let body = match db.monomorphized_mir_body( + let body = db.monomorphized_mir_body( self.id.into(), Substitution::empty(Interner), db.trait_environment(self.id.into()), - ) { - Ok(body) => body, - Err(e) => { - let mut r = String::new(); - _ = e.pretty_print(&mut r, db, &span_formatter, edition); - return r; - } - }; - let (result, output) = interpret_mir(db, body, false, None); + )?; + let (result, output) = interpret_mir(db, body, false, None)?; let mut text = match result { Ok(_) => "pass".to_owned(), Err(e) => { @@ -2337,7 +2333,7 @@ impl Function { text += "\n--------- stderr ---------\n"; text += &stderr; } - text + Ok(text) } } @@ -2560,9 +2556,9 @@ impl Const { /// Evaluate the constant and return the result as a string. /// /// This function is intended for IDE assistance, different from [`Const::render_eval`]. - pub fn eval(self, db: &dyn HirDatabase, edition: Edition) -> Result<String, ConstEvalError> { + pub fn eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> { let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?; - Ok(format!("{}", c.display(db, edition))) + Ok(format!("{}", c.display(db, self.krate(db).edition(db)))) } /// Evaluate the constant and return the result as a string, with more detailed information. @@ -2597,7 +2593,7 @@ impl Const { } } } - if let Ok(s) = mir::render_const_using_debug_impl(db, self.id, &c) { + if let Ok(s) = mir::render_const_using_debug_impl(db, self.id.into(), &c) { Ok(s) } else { Ok(format!("{}", c.display(db, edition))) @@ -2636,6 +2632,53 @@ impl Static { pub fn ty(self, db: &dyn HirDatabase) -> Type { Type::from_value_def(db, self.id) } + + /// Evaluate the static and return the result as a string. + /// + /// This function is intended for IDE assistance, different from [`Static::render_eval`]. + pub fn eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> { + let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?; + Ok(format!("{}", c.display(db, self.krate(db).edition(db)))) + } + + /// Evaluate the static and return the result as a string, with more detailed information. + /// + /// This function is intended for user-facing display. + pub fn render_eval( + self, + db: &dyn HirDatabase, + edition: Edition, + ) -> Result<String, ConstEvalError> { + let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?; + let data = &c.data(Interner); + if let TyKind::Scalar(s) = data.ty.kind(Interner) { + if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) { + if let hir_ty::ConstValue::Concrete(c) = &data.value { + if let hir_ty::ConstScalar::Bytes(b, _) = &c.interned { + let value = u128::from_le_bytes(mir::pad16(b, false)); + let value_signed = + i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_)))); + let mut result = if let Scalar::Int(_) = s { + value_signed.to_string() + } else { + value.to_string() + }; + if value >= 10 { + format_to!(result, " ({value:#X})"); + return Ok(result); + } else { + return Ok(result); + } + } + } + } + } + if let Ok(s) = mir::render_const_using_debug_impl(db, self.id.into(), &c) { + Ok(s) + } else { + Ok(format!("{}", c.display(db, edition))) + } + } } impl HasVisibility for Static { @@ -2697,6 +2740,18 @@ impl Trait { hir_ty::dyn_compatibility::dyn_compatibility(db, self.id) } + pub fn dyn_compatibility_all_violations( + &self, + db: &dyn HirDatabase, + ) -> Option<Vec<DynCompatibilityViolation>> { + let mut violations = vec![]; + hir_ty::dyn_compatibility::dyn_compatibility_with_callback(db, self.id, &mut |violation| { + violations.push(violation); + ControlFlow::Continue(()) + }); + violations.is_empty().not().then_some(violations) + } + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> { db.trait_data(self.id) .macro_calls diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index feb9a344d8a..9d3f8e5fba4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1,5 +1,6 @@ //! See `Semantics`. +mod child_by_source; mod source_to_def; use std::{ @@ -1271,9 +1272,9 @@ impl<'db> SemanticsImpl<'db> { let analyze = self.analyze(ty.syntax())?; let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = + let mut ctx = LowerCtx::new(self.db.upcast(), analyze.file_id, &mut types_map, &mut types_source_map); - let type_ref = crate::TypeRef::from_ast(&ctx, ty.clone()); + let type_ref = crate::TypeRef::from_ast(&mut ctx, ty.clone()); let ty = hir_ty::TyLoweringContext::new_maybe_unowned( self.db, &analyze.resolver, @@ -1289,9 +1290,9 @@ impl<'db> SemanticsImpl<'db> { let analyze = self.analyze(path.syntax())?; let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = + let mut ctx = LowerCtx::new(self.db.upcast(), analyze.file_id, &mut types_map, &mut types_source_map); - let hir_path = Path::from_src(&ctx, path.clone())?; + let hir_path = Path::from_src(&mut ctx, path.clone())?; match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? { TypeNs::TraitId(id) => Some(Trait { id }), _ => None, @@ -1974,9 +1975,9 @@ impl SemanticsScope<'_> { pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option<PathResolution> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = + let mut ctx = LowerCtx::new(self.db.upcast(), self.file_id, &mut types_map, &mut types_source_map); - let path = Path::from_src(&ctx, ast_path.clone())?; + let path = Path::from_src(&mut ctx, ast_path.clone())?; resolve_hir_path( self.db, &self.resolver, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index 0438278ca27..ec65ea9a9a8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -8,7 +8,7 @@ use either::Either; use hir_expand::{attrs::collect_attrs, HirFileId}; use syntax::{ast, AstPtr}; -use crate::{ +use hir_def::{ db::DefDatabase, dyn_map::{ keys::{self, Key}, @@ -23,7 +23,7 @@ use crate::{ VariantId, }; -pub trait ChildBySource { +pub(crate) trait ChildBySource { fn child_by_source(&self, db: &dyn DefDatabase, file_id: HirFileId) -> DynMap { let mut res = DynMap::default(); self.child_by_source_to(db, &mut res, file_id); diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 5357e824d09..08333c2d76c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -87,7 +87,6 @@ use either::Either; use hir_def::{ - child_by_source::ChildBySource, dyn_map::{ keys::{self, Key}, DynMap, @@ -111,7 +110,10 @@ use syntax::{ AstNode, AstPtr, SyntaxNode, }; -use crate::{db::HirDatabase, InFile, InlineAsmOperand, SemanticsImpl}; +use crate::{ + db::HirDatabase, semantics::child_by_source::ChildBySource, InFile, InlineAsmOperand, + SemanticsImpl, +}; #[derive(Default)] pub(super) struct SourceToDefCache { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 8d6e228e14c..c16454cff68 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -616,9 +616,9 @@ impl SourceAnalyzer { ) -> Option<Macro> { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = + let mut ctx = LowerCtx::new(db.upcast(), macro_call.file_id, &mut types_map, &mut types_source_map); - let path = macro_call.value.path().and_then(|ast| Path::from_src(&ctx, ast))?; + let path = macro_call.value.path().and_then(|ast| Path::from_src(&mut ctx, ast))?; self.resolver .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang)) .map(|(it, _)| it.into()) @@ -731,8 +731,9 @@ impl SourceAnalyzer { let (mut types_map, mut types_source_map) = (TypesMap::default(), TypesSourceMap::default()); - let ctx = LowerCtx::new(db.upcast(), self.file_id, &mut types_map, &mut types_source_map); - let hir_path = Path::from_src(&ctx, path.clone())?; + let mut ctx = + LowerCtx::new(db.upcast(), self.file_id, &mut types_map, &mut types_source_map); + let hir_path = Path::from_src(&mut ctx, path.clone())?; // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are // trying to resolve foo::bar. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index f01b4ea0fd4..83f4a6b123c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -104,7 +104,7 @@ fn edit_struct_def( ast::make::tokens::single_newline().text(), ); edit.insert(tuple_fields_text_range.start(), w.syntax().text()); - if !w.syntax().last_token().is_some_and(|t| t.kind() == SyntaxKind::COMMA) { + if w.syntax().last_token().is_none_or(|t| t.kind() != SyntaxKind::COMMA) { edit.insert(tuple_fields_text_range.start(), ","); } edit.insert( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs new file mode 100644 index 00000000000..f4b4c22d98d --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -0,0 +1,430 @@ +use either::Either; +use ide_db::assists::{AssistId, AssistKind, GroupLabel}; +use syntax::{ + ast::{self, edit::IndentLevel, make, HasGenericParams, HasName}, + syntax_editor, AstNode, +}; + +use crate::{AssistContext, Assists}; + +// Assist: generate_fn_type_alias_named +// +// Generate a type alias for the function with named parameters. +// +// ``` +// unsafe fn fo$0o(n: i32) -> i32 { 42i32 } +// ``` +// -> +// ``` +// type ${0:FooFn} = unsafe fn(n: i32) -> i32; +// +// unsafe fn foo(n: i32) -> i32 { 42i32 } +// ``` + +// Assist: generate_fn_type_alias_unnamed +// +// Generate a type alias for the function with unnamed parameters. +// +// ``` +// unsafe fn fo$0o(n: i32) -> i32 { 42i32 } +// ``` +// -> +// ``` +// type ${0:FooFn} = unsafe fn(i32) -> i32; +// +// unsafe fn foo(n: i32) -> i32 { 42i32 } +// ``` + +pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let name = ctx.find_node_at_offset::<ast::Name>()?; + let func = &name.syntax().parent()?; + let func_node = ast::Fn::cast(func.clone())?; + let param_list = func_node.param_list()?; + + let assoc_owner = func.ancestors().nth(2).and_then(Either::<ast::Trait, ast::Impl>::cast); + // This is where we'll insert the type alias, since type aliases in `impl`s or `trait`s are not supported + let insertion_node = assoc_owner + .as_ref() + .map_or_else(|| func, |impl_| impl_.as_ref().either(AstNode::syntax, AstNode::syntax)); + + for style in ParamStyle::ALL { + acc.add_group( + &GroupLabel("Generate a type alias for function...".into()), + style.assist_id(), + style.label(), + func_node.syntax().text_range(), + |builder| { + let mut edit = builder.make_editor(func); + + let alias_name = format!("{}Fn", stdx::to_camel_case(&name.to_string())); + + let mut fn_params_vec = Vec::new(); + + if let Some(self_ty) = + param_list.self_param().and_then(|p| ctx.sema.type_of_self(&p)) + { + let is_ref = self_ty.is_reference(); + let is_mut = self_ty.is_mutable_reference(); + + if let Some(adt) = self_ty.strip_references().as_adt() { + let inner_type = make::ty(adt.name(ctx.db()).as_str()); + + let ast_self_ty = + if is_ref { make::ty_ref(inner_type, is_mut) } else { inner_type }; + + fn_params_vec.push(make::unnamed_param(ast_self_ty)); + } + } + + fn_params_vec.extend(param_list.params().filter_map(|p| match style { + ParamStyle::Named => Some(p), + ParamStyle::Unnamed => p.ty().map(make::unnamed_param), + })); + + let generic_params = func_node.generic_param_list(); + + let is_unsafe = func_node.unsafe_token().is_some(); + let ty = make::ty_fn_ptr( + None, + is_unsafe, + func_node.abi(), + fn_params_vec.into_iter(), + func_node.ret_type(), + ); + + // Insert new alias + let ty_alias = make::ty_alias( + &alias_name, + generic_params, + None, + None, + Some((ast::Type::FnPtrType(ty), None)), + ) + .clone_for_update(); + + let indent = IndentLevel::from_node(insertion_node); + edit.insert_all( + syntax_editor::Position::before(insertion_node), + vec![ + ty_alias.syntax().clone().into(), + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + ], + ); + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(name) = ty_alias.name() { + edit.add_annotation(name.syntax(), builder.make_placeholder_snippet(cap)); + } + } + + builder.add_file_edits(ctx.file_id(), edit); + }, + ); + } + + Some(()) +} + +enum ParamStyle { + Named, + Unnamed, +} + +impl ParamStyle { + const ALL: &'static [ParamStyle] = &[ParamStyle::Named, ParamStyle::Unnamed]; + + fn assist_id(&self) -> AssistId { + let s = match self { + ParamStyle::Named => "generate_fn_type_alias_named", + ParamStyle::Unnamed => "generate_fn_type_alias_unnamed", + }; + + AssistId(s, AssistKind::Generate) + } + + fn label(&self) -> &'static str { + match self { + ParamStyle::Named => "Generate a type alias for function with named params", + ParamStyle::Unnamed => "Generate a type alias for function with unnamed params", + } + } +} + +#[cfg(test)] +mod tests { + use crate::tests::check_assist_by_label; + + use super::*; + + #[test] + fn generate_fn_alias_unnamed_simple() { + check_assist_by_label( + generate_fn_type_alias, + r#" +fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = fn(u32) -> i32; + +fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_alias_unnamed_unsafe() { + check_assist_by_label( + generate_fn_type_alias, + r#" +unsafe fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = unsafe fn(u32) -> i32; + +unsafe fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_alias_unnamed_extern() { + check_assist_by_label( + generate_fn_type_alias, + r#" +extern fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = extern fn(u32) -> i32; + +extern fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_type_unnamed_extern_abi() { + check_assist_by_label( + generate_fn_type_alias, + r#" +extern "FooABI" fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = extern "FooABI" fn(u32) -> i32; + +extern "FooABI" fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_alias_unnamed_unsafe_extern_abi() { + check_assist_by_label( + generate_fn_type_alias, + r#" +unsafe extern "FooABI" fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = unsafe extern "FooABI" fn(u32) -> i32; + +unsafe extern "FooABI" fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_alias_unnamed_generics() { + check_assist_by_label( + generate_fn_type_alias, + r#" +fn fo$0o<A, B>(a: A, b: B) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn}<A, B> = fn(A, B) -> i32; + +fn foo<A, B>(a: A, b: B) -> i32 { return 42; } +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_alias_unnamed_generics_bounds() { + check_assist_by_label( + generate_fn_type_alias, + r#" +fn fo$0o<A: Trait, B: Trait>(a: A, b: B) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn}<A: Trait, B: Trait> = fn(A, B) -> i32; + +fn foo<A: Trait, B: Trait>(a: A, b: B) -> i32 { return 42; } +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_alias_unnamed_self() { + check_assist_by_label( + generate_fn_type_alias, + r#" +struct S; + +impl S { + fn fo$0o(&mut self, param: u32) -> i32 { return 42; } +} +"#, + r#" +struct S; + +type ${0:FooFn} = fn(&mut S, u32) -> i32; + +impl S { + fn foo(&mut self, param: u32) -> i32 { return 42; } +} +"#, + ParamStyle::Unnamed.label(), + ); + } + + #[test] + fn generate_fn_alias_named_simple() { + check_assist_by_label( + generate_fn_type_alias, + r#" +fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = fn(param: u32) -> i32; + +fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Named.label(), + ); + } + + #[test] + fn generate_fn_alias_named_unsafe() { + check_assist_by_label( + generate_fn_type_alias, + r#" +unsafe fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = unsafe fn(param: u32) -> i32; + +unsafe fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Named.label(), + ); + } + + #[test] + fn generate_fn_alias_named_extern() { + check_assist_by_label( + generate_fn_type_alias, + r#" +extern fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = extern fn(param: u32) -> i32; + +extern fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Named.label(), + ); + } + + #[test] + fn generate_fn_type_named_extern_abi() { + check_assist_by_label( + generate_fn_type_alias, + r#" +extern "FooABI" fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = extern "FooABI" fn(param: u32) -> i32; + +extern "FooABI" fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Named.label(), + ); + } + + #[test] + fn generate_fn_alias_named_unsafe_extern_abi() { + check_assist_by_label( + generate_fn_type_alias, + r#" +unsafe extern "FooABI" fn fo$0o(param: u32) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn} = unsafe extern "FooABI" fn(param: u32) -> i32; + +unsafe extern "FooABI" fn foo(param: u32) -> i32 { return 42; } +"#, + ParamStyle::Named.label(), + ); + } + + #[test] + fn generate_fn_alias_named_generics() { + check_assist_by_label( + generate_fn_type_alias, + r#" +fn fo$0o<A, B>(a: A, b: B) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn}<A, B> = fn(a: A, b: B) -> i32; + +fn foo<A, B>(a: A, b: B) -> i32 { return 42; } +"#, + ParamStyle::Named.label(), + ); + } + + #[test] + fn generate_fn_alias_named_generics_bounds() { + check_assist_by_label( + generate_fn_type_alias, + r#" +fn fo$0o<A: Trait, B: Trait>(a: A, b: B) -> i32 { return 42; } +"#, + r#" +type ${0:FooFn}<A: Trait, B: Trait> = fn(a: A, b: B) -> i32; + +fn foo<A: Trait, B: Trait>(a: A, b: B) -> i32 { return 42; } +"#, + ParamStyle::Named.label(), + ); + } + + #[test] + fn generate_fn_alias_named_self() { + check_assist_by_label( + generate_fn_type_alias, + r#" +struct S; + +impl S { + fn fo$0o(&mut self, param: u32) -> i32 { return 42; } +} +"#, + r#" +struct S; + +type ${0:FooFn} = fn(&mut S, param: u32) -> i32; + +impl S { + fn foo(&mut self, param: u32) -> i32 { return 42; } +} +"#, + ParamStyle::Named.label(), + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs index 6b504a918b4..2bd4c4da1e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs @@ -51,10 +51,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_> | ast::Expr::MatchExpr(_) | ast::Expr::MacroExpr(_) | ast::Expr::BinExpr(_) - | ast::Expr::CallExpr(_) => { - let edition = ctx.sema.scope(variable.syntax())?.krate().edition(ctx.db()); - konst.eval(ctx.sema.db, edition).ok()? - } + | ast::Expr::CallExpr(_) => konst.eval(ctx.sema.db).ok()?, _ => return None, }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 8aaf5d6fff2..5c95b25f28d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -161,6 +161,7 @@ mod handlers { mod generate_enum_is_method; mod generate_enum_projection_method; mod generate_enum_variant; + mod generate_fn_type_alias; mod generate_from_impl_for_enum; mod generate_function; mod generate_getter_or_setter; @@ -289,6 +290,7 @@ mod handlers { generate_enum_projection_method::generate_enum_as_method, generate_enum_projection_method::generate_enum_try_into_method, generate_enum_variant::generate_enum_variant, + generate_fn_type_alias::generate_fn_type_alias, generate_from_impl_for_enum::generate_from_impl_for_enum, generate_function::generate_function, generate_impl::generate_impl, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 933d45d7508..64b7ab1a123 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1549,6 +1549,36 @@ fn main() { } #[test] +fn doctest_generate_fn_type_alias_named() { + check_doc_test( + "generate_fn_type_alias_named", + r#####" +unsafe fn fo$0o(n: i32) -> i32 { 42i32 } +"#####, + r#####" +type ${0:FooFn} = unsafe fn(n: i32) -> i32; + +unsafe fn foo(n: i32) -> i32 { 42i32 } +"#####, + ) +} + +#[test] +fn doctest_generate_fn_type_alias_unnamed() { + check_doc_test( + "generate_fn_type_alias_unnamed", + r#####" +unsafe fn fo$0o(n: i32) -> i32 { 42i32 } +"#####, + r#####" +type ${0:FooFn} = unsafe fn(i32) -> i32; + +unsafe fn foo(n: i32) -> i32 { 42i32 } +"#####, + ) +} + +#[test] fn doctest_generate_from_impl_for_enum() { check_doc_test( "generate_from_impl_for_enum", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index ec3c2fe3556..0f00ad45f98 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -281,8 +281,8 @@ pub(crate) fn render_resolution_with_import( import_edit: LocatedImport, ) -> Option<Builder> { let resolution = ScopeDef::from(import_edit.original_item); - let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?; - //this now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead + let local_name = get_import_name(resolution, &ctx, &import_edit)?; + // This now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead. let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution); let ctx = ctx.doc_aliases(doc_aliases); Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution)) @@ -294,7 +294,7 @@ pub(crate) fn render_resolution_with_import_pat( import_edit: LocatedImport, ) -> Option<Builder> { let resolution = ScopeDef::from(import_edit.original_item); - let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?; + let local_name = get_import_name(resolution, &ctx, &import_edit)?; Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution)) } @@ -357,6 +357,24 @@ pub(crate) fn render_expr( Some(item) } +fn get_import_name( + resolution: ScopeDef, + ctx: &RenderContext<'_>, + import_edit: &LocatedImport, +) -> Option<hir::Name> { + // FIXME: Temporary workaround for handling aliased import. + // This should be removed after we have proper support for importing alias. + // <https://github.com/rust-lang/rust-analyzer/issues/14079> + + // If `item_to_import` matches `original_item`, we are importing the item itself (not its parent module). + // In this case, we can use the last segment of `import_path`, as it accounts for the aliased name. + if import_edit.item_to_import == import_edit.original_item { + import_edit.import_path.segments().last().cloned() + } else { + scope_def_to_name(resolution, ctx, import_edit) + } +} + fn scope_def_to_name( resolution: ScopeDef, ctx: &RenderContext<'_>, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 0b532064fb2..4b949e0d657 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1669,3 +1669,54 @@ mod module { "#]], ); } + +#[test] +fn re_export_aliased() { + check( + r#" +mod outer { + mod inner { + pub struct BarStruct; + pub fn bar_fun() {} + pub mod bar {} + } + pub use inner::bar as foo; + pub use inner::bar_fun as foo_fun; + pub use inner::BarStruct as FooStruct; +} +fn function() { + foo$0 +} +"#, + expect![[r#" + st FooStruct (use outer::FooStruct) BarStruct + md foo (use outer::foo) + fn foo_fun() (use outer::foo_fun) fn() + "#]], + ); +} + +#[test] +fn re_export_aliased_pattern() { + check( + r#" +mod outer { + mod inner { + pub struct BarStruct; + pub fn bar_fun() {} + pub mod bar {} + } + pub use inner::bar as foo; + pub use inner::bar_fun as foo_fun; + pub use inner::BarStruct as FooStruct; +} +fn function() { + let foo$0 +} +"#, + expect![[r#" + st FooStruct (use outer::FooStruct) + md foo (use outer::foo) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 45c723d09d4..1f1b6478d36 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -382,7 +382,7 @@ pub fn semantic_diagnostics( // A bunch of parse errors in a file indicate some bigger structural parse changes in the // file, so we skip semantic diagnostics so we can show these faster. Some(m) => { - if !db.parse_errors(file_id).as_deref().is_some_and(|es| es.len() >= 16) { + if db.parse_errors(file_id).as_deref().is_none_or(|es| es.len() < 16) { m.diagnostics(db, &mut diags, config.style_lints); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 6cac4f1ee48..332dfacbb43 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -158,7 +158,7 @@ fn hover_offset( if let Some(doc_comment) = token_as_doc_comment(&original_token) { cov_mark::hit!(no_highlight_on_comment_hover); return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| { - let res = hover_for_definition(sema, file_id, def, &node, None, config, edition); + let res = hover_for_definition(sema, file_id, def, &node, None, false, config, edition); Some(RangeInfo::new(range, res)) }); } @@ -172,6 +172,7 @@ fn hover_offset( Definition::from(resolution?), &original_token.parent()?, None, + false, config, edition, ); @@ -218,6 +219,7 @@ fn hover_offset( break 'a vec![( Definition::Macro(macro_), sema.resolve_macro_call_arm(¯o_call), + false, node, )]; } @@ -234,19 +236,34 @@ fn hover_offset( decl, .. }) => { - vec![(Definition::ExternCrateDecl(decl), None, node)] + vec![(Definition::ExternCrateDecl(decl), None, false, node)] } class => { - multizip((class.definitions(), iter::repeat(None), iter::repeat(node))) - .collect::<Vec<_>>() + let is_def = matches!(class, IdentClass::NameClass(_)); + multizip(( + class.definitions(), + iter::repeat(None), + iter::repeat(is_def), + iter::repeat(node), + )) + .collect::<Vec<_>>() } } } .into_iter() - .unique_by(|&(def, _, _)| def) - .map(|(def, macro_arm, node)| { - hover_for_definition(sema, file_id, def, &node, macro_arm, config, edition) + .unique_by(|&(def, _, _, _)| def) + .map(|(def, macro_arm, hovered_definition, node)| { + hover_for_definition( + sema, + file_id, + def, + &node, + macro_arm, + hovered_definition, + config, + edition, + ) }) .collect::<Vec<_>>(), ) @@ -366,6 +383,7 @@ pub(crate) fn hover_for_definition( def: Definition, scope_node: &SyntaxNode, macro_arm: Option<u32>, + hovered_definition: bool, config: &HoverConfig, edition: Edition, ) -> HoverResult { @@ -397,6 +415,7 @@ pub(crate) fn hover_for_definition( famous_defs.as_ref(), ¬able_traits, macro_arm, + hovered_definition, config, edition, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index a31b14dbd3e..d9ddc2d015b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -273,7 +273,7 @@ pub(super) fn keyword( let markup = process_markup( sema.db, Definition::Module(doc_owner), - &markup(Some(docs.into()), description, None), + &markup(Some(docs.into()), description, None, None), config, ); Some(HoverResult { markup, actions }) @@ -419,6 +419,7 @@ pub(super) fn definition( famous_defs: Option<&FamousDefs<'_, '_>>, notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)], macro_arm: Option<u32>, + hovered_definition: bool, config: &HoverConfig, edition: Edition, ) -> Markup { @@ -456,7 +457,7 @@ pub(super) fn definition( _ => def.label(db, edition), }; let docs = def.docs(db, famous_defs, edition); - let value = (|| match def { + let value = || match def { Definition::Variant(it) => { if !it.parent_enum(db).is_data_carrying(db) { match it.eval(db) { @@ -485,18 +486,24 @@ pub(super) fn definition( } } Definition::Static(it) => { - let source = it.source(db)?; - let mut body = source.value.body()?.syntax().clone(); - if let Some(macro_file) = source.file_id.macro_file() { - let span_map = db.expansion_span_map(macro_file); - body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); + let body = it.render_eval(db, edition); + match body { + Ok(it) => Some(it), + Err(_) => { + let source = it.source(db)?; + let mut body = source.value.body()?.syntax().clone(); + if let Some(macro_file) = source.file_id.macro_file() { + let span_map = db.expansion_span_map(macro_file); + body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); + } + Some(body.to_string()) + } } - Some(body.to_string()) } _ => None, - })(); + }; - let layout_info = match def { + let layout_info = || match def { Definition::Field(it) => render_memory_layout( config.memory_layout, || it.layout(db), @@ -529,34 +536,38 @@ pub(super) fn definition( _ => None, }; - let dyn_compatibility_info = if let Definition::Trait(it) = def { - let mut dyn_compatibility_info = String::new(); - render_dyn_compatibility(db, &mut dyn_compatibility_info, it.dyn_compatibility(db)); - Some(dyn_compatibility_info) - } else { - None + let dyn_compatibility_info = || match def { + Definition::Trait(it) => { + let mut dyn_compatibility_info = String::new(); + render_dyn_compatibility(db, &mut dyn_compatibility_info, it.dyn_compatibility(db)); + Some(dyn_compatibility_info) + } + _ => None, }; - let mut desc = String::new(); - if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits, edition) { - desc.push_str(¬able_traits); - desc.push('\n'); - } - if let Some(layout_info) = layout_info { - desc.push_str(&layout_info); - desc.push('\n'); - } - if let Some(dyn_compatibility_info) = dyn_compatibility_info { - desc.push_str(&dyn_compatibility_info); - desc.push('\n'); + let mut extra = String::new(); + if hovered_definition { + if let Some(notable_traits) = render_notable_trait(db, notable_traits, edition) { + extra.push_str("\n___\n"); + extra.push_str(¬able_traits); + } + if let Some(layout_info) = layout_info() { + extra.push_str("\n___\n"); + extra.push_str(&layout_info); + } + if let Some(dyn_compatibility_info) = dyn_compatibility_info() { + extra.push_str("\n___\n"); + extra.push_str(&dyn_compatibility_info); + } } + let mut desc = String::new(); desc.push_str(&label); - if let Some(value) = value { + if let Some(value) = value() { desc.push_str(" = "); desc.push_str(&value); } - markup(docs.map(Into::into), desc, mod_path) + markup(docs.map(Into::into), desc, extra.is_empty().not().then_some(extra), mod_path) } pub(super) fn literal( @@ -626,7 +637,7 @@ pub(super) fn literal( Some(s.into()) } -fn render_notable_trait_comment( +fn render_notable_trait( db: &RootDatabase, notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)], edition: Edition, @@ -635,7 +646,7 @@ fn render_notable_trait_comment( let mut needs_impl_header = true; for (trait_, assoc_types) in notable_traits { desc.push_str(if mem::take(&mut needs_impl_header) { - "// Implements notable traits: " + "Implements notable traits: " } else { ", " }); @@ -728,13 +739,12 @@ fn type_info( ) .into() } else { - let mut desc = - match render_notable_trait_comment(db, ¬able_traits(db, &original), edition) { - Some(desc) => desc + "\n", - None => String::new(), - }; - format_to!(desc, "{}", original.display(db, edition)); - Markup::fenced_block(&desc) + let mut desc = format!("```rust\n{}\n```", original.display(db, edition)); + if let Some(extra) = render_notable_trait(db, ¬able_traits(db, &original), edition) { + desc.push_str("\n___\n"); + desc.push_str(&extra); + }; + desc.into() }; if let Some(actions) = HoverAction::goto_type_from_targets(db, targets, edition) { res.actions.push(actions); @@ -786,20 +796,16 @@ fn closure_ty( }; let mut markup = format!("```rust\n{}", c.display_with_id(sema.db, edition)); + if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) { + push_new_def(hir::Trait::from(trait_).into()) + } + format_to!(markup, "\n{}\n```", c.display_with_impl(sema.db, edition),); if let Some(layout) = render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) { - format_to!(markup, " {layout}"); + format_to!(markup, "\n___\n{layout}"); } - if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) { - push_new_def(hir::Trait::from(trait_).into()) - } - format_to!( - markup, - "\n{}\n```{adjusted}\n\n## Captures\n{}", - c.display_with_impl(sema.db, edition), - captures_rendered, - ); + format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,); let mut res = HoverResult::default(); if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { @@ -824,7 +830,12 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition, edition: Edition) -> .map(|module| path(db, module, definition_owner_name(db, def, edition), edition)) } -fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Markup { +fn markup( + docs: Option<String>, + rust: String, + extra: Option<String>, + mod_path: Option<String>, +) -> Markup { let mut buf = String::new(); if let Some(mod_path) = mod_path { @@ -832,7 +843,11 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Marku format_to!(buf, "```rust\n{}\n```\n\n", mod_path); } } - format_to!(buf, "```rust\n{}\n```", desc); + format_to!(buf, "```rust\n{}\n```", rust); + + if let Some(extra) = extra { + buf.push_str(&extra); + } if let Some(doc) = docs { format_to!(buf, "\n___\n\n{}", doc); @@ -862,7 +877,7 @@ fn render_memory_layout( let config = config?; let layout = layout().ok()?; - let mut label = String::from("// "); + let mut label = String::new(); if let Some(render) = config.size { let size = match tag(&layout) { @@ -994,55 +1009,53 @@ fn render_dyn_compatibility( safety: Option<DynCompatibilityViolation>, ) { let Some(osv) = safety else { - buf.push_str("// Dyn Compatible: Yes"); + buf.push_str("Is Dyn compatible"); return; }; - buf.push_str("// Dyn Compatible: No\n// - Reason: "); + buf.push_str("Is not Dyn compatible due to "); match osv { DynCompatibilityViolation::SizedSelf => { - buf.push_str("has a `Self: Sized` bound"); + buf.push_str("having a `Self: Sized` bound"); } DynCompatibilityViolation::SelfReferential => { - buf.push_str("has a bound that references `Self`"); + buf.push_str("having a bound that references `Self`"); } DynCompatibilityViolation::Method(func, mvc) => { let name = hir::Function::from(func).name(db); - format_to!( - buf, - "has a method `{}` that is non dispatchable because of:\n// - ", - name.as_str() - ); + format_to!(buf, "having a method `{}` that is not dispatchable due to ", name.as_str()); let desc = match mvc { MethodViolationCode::StaticMethod => "missing a receiver", - MethodViolationCode::ReferencesSelfInput => "a parameter references `Self`", - MethodViolationCode::ReferencesSelfOutput => "the return type references `Self`", + MethodViolationCode::ReferencesSelfInput => "having a parameter referencing `Self`", + MethodViolationCode::ReferencesSelfOutput => "the return type referencing `Self`", MethodViolationCode::ReferencesImplTraitInTrait => { - "the return type contains `impl Trait`" + "the return type containing `impl Trait`" } MethodViolationCode::AsyncFn => "being async", MethodViolationCode::WhereClauseReferencesSelf => { - "a where clause references `Self`" + "a where clause referencing `Self`" + } + MethodViolationCode::Generic => "having a const or type generic parameter", + MethodViolationCode::UndispatchableReceiver => { + "having a non-dispatchable receiver type" } - MethodViolationCode::Generic => "a non-lifetime generic parameter", - MethodViolationCode::UndispatchableReceiver => "a non-dispatchable receiver type", }; buf.push_str(desc); } DynCompatibilityViolation::AssocConst(const_) => { let name = hir::Const::from(const_).name(db); if let Some(name) = name { - format_to!(buf, "has an associated constant `{}`", name.as_str()); + format_to!(buf, "having an associated constant `{}`", name.as_str()); } else { - buf.push_str("has an associated constant"); + buf.push_str("having an associated constant"); } } DynCompatibilityViolation::GAT(alias) => { let name = hir::TypeAlias::from(alias).name(db); - format_to!(buf, "has a generic associated type `{}`", name.as_str()); + format_to!(buf, "having a generic associated type `{}`", name.as_str()); } DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait) => { let name = hir::Trait::from(super_trait).name(db); - format_to!(buf, "has a dyn incompatible supertrait `{}`", name.as_str()); + format_to!(buf, "having a dyn incompatible supertrait `{}`", name.as_str()); } } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 3e402630419..0ffbf988c35 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -260,7 +260,6 @@ fn foo() { *local* ```rust - // size = 4, align = 4 let local: i32 ``` "#]], @@ -348,9 +347,11 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 8, align = 8, niches = 1 + {closure#0} impl Fn(i32) -> i32 ``` + ___ + size = 8, align = 8, niches = 1 ## Captures * `x` by immutable borrow @@ -370,9 +371,11 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 0, align = 1 + {closure#0} impl Fn(i32) -> i32 ``` + ___ + size = 0, align = 1 ## Captures This closure captures nothing @@ -403,9 +406,11 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 16 (0x10), align = 8, niches = 1 + {closure#0} impl FnOnce() ``` + ___ + size = 16 (0x10), align = 8, niches = 1 ## Captures * `x.f1` by move @@ -431,9 +436,11 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 8, align = 8, niches = 1 + {closure#0} impl FnMut() ``` + ___ + size = 8, align = 8, niches = 1 ## Captures * `x` by mutable borrow @@ -455,9 +462,11 @@ fn main() { "#, expect![[r#" ```rust - {closure#0} // size = 8, align = 8, niches = 1 + {closure#0} impl FnOnce() -> S2 ``` + ___ + size = 8, align = 8, niches = 1 Coerced to: &impl FnOnce() -> S2 ## Captures @@ -551,9 +560,12 @@ fn main() { *iter* ```rust - // size = 8, align = 4 let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>> ``` + + --- + + size = 8, align = 4 "#]], ); } @@ -793,9 +805,12 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - // size = 1, align = 1, offset = 6 field_a: u8 ``` + + --- + + size = 1, align = 1, offset = 6 "#]], ); } @@ -819,7 +834,6 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 pub field_a: u32 ``` "#]], @@ -842,9 +856,12 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 pub field_a: u32 ``` + + --- + + size = 4, align = 4, offset = 0 "#]], ); } @@ -867,7 +884,6 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 pub 0: u32 ``` "#]], @@ -888,7 +904,6 @@ fn foo(foo: Foo) { ``` ```rust - // size = 4, align = 4, offset = 0 pub 0: u32 ``` "#]], @@ -909,11 +924,14 @@ struct Foo$0(pub u32) where u32: Copy; ``` ```rust - // size = 4, align = 4 struct Foo(pub u32) where u32: Copy, ``` + + --- + + size = 4, align = 4 "#]], ); } @@ -932,11 +950,14 @@ struct Foo$0 { field: u32 } ``` ```rust - // size = 4, align = 4 struct Foo { field: u32, } ``` + + --- + + size = 4, align = 4 "#]], ); check( @@ -951,7 +972,6 @@ struct Foo$0 where u32: Copy { field: u32 } ``` ```rust - // size = 4, align = 4 struct Foo where u32: Copy, @@ -959,6 +979,10 @@ struct Foo$0 where u32: Copy { field: u32 } field: u32, } ``` + + --- + + size = 4, align = 4 "#]], ); } @@ -978,13 +1002,16 @@ fn hover_record_struct_limit() { ``` ```rust - // size = 12 (0xC), align = 4 struct Foo { a: u32, b: i32, c: i32, } ``` + + --- + + size = 12 (0xC), align = 4 "#]], ); check_hover_fields_limit( @@ -1000,11 +1027,14 @@ fn hover_record_struct_limit() { ``` ```rust - // size = 4, align = 4 struct Foo { a: u32, } ``` + + --- + + size = 4, align = 4 "#]], ); check_hover_fields_limit( @@ -1020,7 +1050,6 @@ fn hover_record_struct_limit() { ``` ```rust - // size = 16 (0x10), align = 4 struct Foo { a: u32, b: i32, @@ -1028,6 +1057,10 @@ fn hover_record_struct_limit() { /* … */ } ``` + + --- + + size = 16 (0x10), align = 4 "#]], ); check_hover_fields_limit( @@ -1043,9 +1076,12 @@ fn hover_record_struct_limit() { ``` ```rust - // size = 12 (0xC), align = 4 struct Foo ``` + + --- + + size = 12 (0xC), align = 4 "#]], ); check_hover_fields_limit( @@ -1061,9 +1097,12 @@ fn hover_record_struct_limit() { ``` ```rust - // size = 12 (0xC), align = 4 struct Foo { /* … */ } ``` + + --- + + size = 12 (0xC), align = 4 "#]], ); @@ -1081,9 +1120,12 @@ fn hover_record_struct_limit() { ``` ```rust - // size = 0, align = 1 struct Foo {} ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -1103,9 +1145,12 @@ fn hover_record_variant_limit() { ``` ```rust - // size = 12 (0xC), align = 4 A { a: u32, b: i32, c: i32, } ``` + + --- + + size = 12 (0xC), align = 4 "#]], ); check_hover_fields_limit( @@ -1121,9 +1166,12 @@ fn hover_record_variant_limit() { ``` ```rust - // size = 4, align = 4 A { a: u32, } ``` + + --- + + size = 4, align = 4 "#]], ); check_hover_fields_limit( @@ -1139,9 +1187,12 @@ fn hover_record_variant_limit() { ``` ```rust - // size = 16 (0x10), align = 4 A { a: u32, b: i32, c: i32, /* … */ } ``` + + --- + + size = 16 (0x10), align = 4 "#]], ); check_hover_fields_limit( @@ -1157,9 +1208,12 @@ fn hover_record_variant_limit() { ``` ```rust - // size = 12 (0xC), align = 4 A ``` + + --- + + size = 12 (0xC), align = 4 "#]], ); check_hover_fields_limit( @@ -1175,9 +1229,12 @@ fn hover_record_variant_limit() { ``` ```rust - // size = 12 (0xC), align = 4 A { /* … */ } ``` + + --- + + size = 12 (0xC), align = 4 "#]], ); } @@ -1195,12 +1252,15 @@ fn hover_enum_limit() { ``` ```rust - // size = 1, align = 1, niches = 254 enum Foo { A, B, } ``` + + --- + + size = 1, align = 1, niches = 254 "#]], ); check_hover_enum_variants_limit( @@ -1214,12 +1274,15 @@ fn hover_enum_limit() { ``` ```rust - // size = 1, align = 1, niches = 254 enum Foo { A, /* … */ } ``` + + --- + + size = 1, align = 1, niches = 254 "#]], ); check_hover_enum_variants_limit( @@ -1233,9 +1296,12 @@ fn hover_enum_limit() { ``` ```rust - // size = 1, align = 1, niches = 254 enum Foo { /* … */ } ``` + + --- + + size = 1, align = 1, niches = 254 "#]], ); check_hover_enum_variants_limit( @@ -1249,9 +1315,12 @@ fn hover_enum_limit() { ``` ```rust - // size = 1, align = 1, niches = 254 enum Foo ``` + + --- + + size = 1, align = 1, niches = 254 "#]], ); check_hover_enum_variants_limit( @@ -1274,7 +1343,6 @@ fn hover_enum_limit() { ``` ```rust - // size = 12 (0xC), align = 4, niches = 4294967288 enum Enum { Variant {}, Variant2 { /* … */ }, @@ -1286,6 +1354,10 @@ fn hover_enum_limit() { /* … */ } ``` + + --- + + size = 12 (0xC), align = 4, niches = 4294967288 "#]], ); } @@ -1303,12 +1375,15 @@ fn hover_union_limit() { ``` ```rust - // size = 4, align = 4 union Foo { a: u32, b: i32, } ``` + + --- + + size = 4, align = 4 "#]], ); check_hover_fields_limit( @@ -1322,12 +1397,15 @@ fn hover_union_limit() { ``` ```rust - // size = 4, align = 4 union Foo { a: u32, /* … */ } ``` + + --- + + size = 4, align = 4 "#]], ); check_hover_fields_limit( @@ -1341,9 +1419,12 @@ fn hover_union_limit() { ``` ```rust - // size = 4, align = 4 union Foo { /* … */ } ``` + + --- + + size = 4, align = 4 "#]], ); check_hover_fields_limit( @@ -1357,9 +1438,12 @@ fn hover_union_limit() { ``` ```rust - // size = 4, align = 4 union Foo ``` + + --- + + size = 4, align = 4 "#]], ); } @@ -1378,11 +1462,14 @@ struct Foo$0 where u32: Copy; ``` ```rust - // size = 0, align = 1 struct Foo where u32: Copy, ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -1457,7 +1544,7 @@ const foo$0: u32 = { ``` ```rust - static foo: u32 = 456 + static foo: u32 = 456 (0x1C8) ``` "#]], ); @@ -1548,9 +1635,12 @@ fn main() { *zz* ```rust - // size = 8, align = 4 let zz: Test<i32> ``` + + --- + + size = 8, align = 4 "#]], ); check_hover_range( @@ -1600,9 +1690,12 @@ fn main() { let b$0ar = Some(12); } *bar* ```rust - // size = 4, align = 4 let bar: Option<i32> ``` + + --- + + size = 4, align = 4 "#]], ); } @@ -1670,7 +1763,6 @@ fn hover_for_local_variable() { *foo* ```rust - // size = 4, align = 4 foo: i32 ``` "#]], @@ -1685,9 +1777,12 @@ fn hover_for_local_variable_pat() { *foo* ```rust - // size = 4, align = 4 foo: i32 ``` + + --- + + size = 4, align = 4 "#]], ) } @@ -1700,7 +1795,6 @@ fn hover_local_var_edge() { *foo* ```rust - // size = 4, align = 4 foo: i32 ``` "#]], @@ -1715,9 +1809,12 @@ fn hover_for_param_edge() { *foo* ```rust - // size = 4, align = 4 foo: i32 ``` + + --- + + size = 4, align = 4 "#]], ) } @@ -1760,9 +1857,12 @@ fn main() { let foo_$0test = Thing::new(); } *foo_test* ```rust - // size = 4, align = 4 let foo_test: Thing ``` + + --- + + size = 4, align = 4 "#]], ) } @@ -1985,7 +2085,6 @@ fn y() { *x* ```rust - // size = 4, align = 4 let x: i32 ``` "#]], @@ -2116,7 +2215,6 @@ fn foo(bar:u32) { let a = id!(ba$0r); } *bar* ```rust - // size = 4, align = 4 bar: u32 ``` "#]], @@ -2135,7 +2233,6 @@ fn foo(bar:u32) { let a = id!(ba$0r); } *bar* ```rust - // size = 4, align = 4 bar: u32 ``` "#]], @@ -2371,9 +2468,12 @@ fn test_hover_function_pointer_show_identifiers() { ``` ```rust - // size = 8, align = 8, niches = 1 type foo = fn(a: i32, b: i32) -> i32 ``` + + --- + + size = 8, align = 8, niches = 1 "#]], ); } @@ -2390,9 +2490,12 @@ fn test_hover_function_pointer_no_identifier() { ``` ```rust - // size = 8, align = 8, niches = 1 type foo = fn(i32, i32) -> i32 ``` + + --- + + size = 8, align = 8, niches = 1 "#]], ); } @@ -2537,7 +2640,6 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 struct Bar ``` @@ -2574,7 +2676,6 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 struct Bar ``` @@ -2604,7 +2705,6 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 struct Bar ``` @@ -2633,12 +2733,15 @@ pub struct B$0ar ``` ```rust - // size = 0, align = 1 pub struct Bar ``` --- + size = 0, align = 1 + + --- + [external](https://www.google.com) "#]], ); @@ -2661,12 +2764,15 @@ pub struct B$0ar ``` ```rust - // size = 0, align = 1 pub struct Bar ``` --- + size = 0, align = 1 + + --- + [baz](Baz) "#]], ); @@ -2751,9 +2857,12 @@ fn test_hover_layout_of_variant() { ``` ```rust - // size = 4, align = 2 Variant1(u8, u16) ``` + + --- + + size = 4, align = 2 "#]], ); } @@ -2794,9 +2903,12 @@ struct S$0<T>(core::marker::PhantomData<T>); ``` ```rust - // size = 0, align = 1 struct S<T>(PhantomData<T>) ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -2816,12 +2928,15 @@ fn test_hover_layout_of_enum() { ``` ```rust - // size = 16 (0x10), align = 8, niches = 254 enum Foo { Variant1( /* … */ ), Variant2( /* … */ ), } ``` + + --- + + size = 16 (0x10), align = 8, niches = 254 "#]], ); } @@ -4133,9 +4248,12 @@ fn main() { *f* ```rust - // size = 8, align = 8, niches = 1 let f: &i32 ``` + + --- + + size = 8, align = 8, niches = 1 --- ```rust @@ -4143,9 +4261,12 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 f: i32 ``` + + --- + + size = 4, align = 4, offset = 0 "#]], ); } @@ -4165,9 +4286,12 @@ struct S$0T<const C: usize = 1, T = Foo>(T); ``` ```rust - // size = 0, align = 1 struct ST<const C: usize = 1, T = Foo>(T) ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4187,9 +4311,12 @@ struct S$0T<const C: usize = {40 + 2}, T = Foo>(T); ``` ```rust - // size = 0, align = 1 struct ST<const C: usize = {const}, T = Foo>(T) ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4210,9 +4337,12 @@ struct S$0T<const C: usize = VAL, T = Foo>(T); ``` ```rust - // size = 0, align = 1 struct ST<const C: usize = VAL, T = Foo>(T) ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4231,9 +4361,12 @@ fn main() { *value* ```rust - // size = 0, align = 1 let value: Const<1> ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4252,9 +4385,12 @@ fn main() { *value* ```rust - // size = 0, align = 1 let value: Const<0> ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4273,9 +4409,12 @@ fn main() { *value* ```rust - // size = 0, align = 1 let value: Const<-1> ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4294,9 +4433,12 @@ fn main() { *value* ```rust - // size = 0, align = 1 let value: Const<true> ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4315,9 +4457,12 @@ fn main() { *value* ```rust - // size = 0, align = 1 let value: Const<'🦀'> ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4335,9 +4480,12 @@ impl Foo { *self* ```rust - // size = 8, align = 8, niches = 1 self: &Foo ``` + + --- + + size = 8, align = 8, niches = 1 "#]], ); } @@ -4356,9 +4504,12 @@ impl Foo { *self* ```rust - // size = 0, align = 1 self: Arc<Foo> ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4755,9 +4906,12 @@ type Fo$0o2 = Foo<2>; ``` ```rust - // size = 0, align = 1 type Foo2 = Foo<2> ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -4798,12 +4952,15 @@ enum E { ``` ```rust - // size = 1, align = 1 A = 8 ``` --- + size = 1, align = 1 + + --- + This is a doc "#]], ); @@ -4824,12 +4981,15 @@ enum E { ``` ```rust - // size = 1, align = 1 A = 12 (0xC) ``` --- + size = 1, align = 1 + + --- + This is a doc "#]], ); @@ -4851,12 +5011,15 @@ enum E { ``` ```rust - // size = 1, align = 1 B = 2 ``` --- + size = 1, align = 1 + + --- + This is a doc "#]], ); @@ -4878,12 +5041,15 @@ enum E { ``` ```rust - // size = 1, align = 1 B = 5 ``` --- + size = 1, align = 1 + + --- + This is a doc "#]], ); @@ -5750,7 +5916,6 @@ fn foo(e: E) { ``` ```rust - // size = 0, align = 1 A = 3 ``` @@ -5799,9 +5964,12 @@ fn main() { *tile4* ```rust - // size = 32 (0x20), align = 4 let tile4: [u32; 8] ``` + + --- + + size = 32 (0x20), align = 4 "#]], ); } @@ -6036,7 +6204,6 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - // size = 0, align = 1 pub struct TheItem ``` @@ -6185,7 +6352,6 @@ mod string { ``` ```rust - // size = 0, align = 1 struct String ``` @@ -6948,7 +7114,6 @@ foo_macro!( ``` ```rust - // size = 0, align = 1 pub struct Foo ``` @@ -6974,7 +7139,6 @@ pub struct Foo(i32); ``` ```rust - // size = 4, align = 4 pub struct Foo(i32) ``` @@ -7099,9 +7263,12 @@ enum Enum { ``` ```rust - // size = 4, align = 4 RecordV { field: u32, } ``` + + --- + + size = 4, align = 4 "#]], ); } @@ -7122,9 +7289,12 @@ enum Enum { ``` ```rust - // size = 4, align = 4 field: u32 ``` + + --- + + size = 4, align = 4 "#]], ); } @@ -7175,7 +7345,6 @@ impl T$0 for () {} ``` ```rust - // Dyn Compatible: Yes trait T {} ``` "#]], @@ -7195,7 +7364,6 @@ impl T$0 for () {} ``` ```rust - // Dyn Compatible: Yes trait T {} ``` "#]], @@ -7219,9 +7387,6 @@ impl T$0 for () {} ``` ```rust - // Dyn Compatible: No - // - Reason: has a method `func` that is non dispatchable because of: - // - missing a receiver trait T { /* … */ } ``` "#]], @@ -7245,9 +7410,6 @@ impl T$0 for () {} ``` ```rust - // Dyn Compatible: No - // - Reason: has a method `func` that is non dispatchable because of: - // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7275,9 +7437,6 @@ impl T$0 for () {} ``` ```rust - // Dyn Compatible: No - // - Reason: has a method `func` that is non dispatchable because of: - // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7305,9 +7464,6 @@ impl T$0 for () {} ``` ```rust - // Dyn Compatible: No - // - Reason: has a method `func` that is non dispatchable because of: - // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7784,7 +7940,6 @@ fn test() { ``` ```rust - // size = 4, align = 4, offset = 0 f: u32 ``` "#]], @@ -7804,9 +7959,12 @@ fn test() { *s* ```rust - // size = 0, align = 1 let s: S ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -7825,7 +7983,6 @@ fn test() { *foo* ```rust - // size = 4, align = 4 let foo: i32 ``` "#]], @@ -7846,7 +8003,6 @@ format_args!("{aaaaa$0}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7867,7 +8023,6 @@ format_args!("{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7888,7 +8043,6 @@ format_args!(r"{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7914,7 +8068,6 @@ foo!(r"{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -8386,10 +8539,16 @@ fn main(notable$0: u32) {} *notable* ```rust - // Implements notable traits: Notable<Assoc = &str, Assoc2 = char> - // size = 4, align = 4 notable: u32 ``` + + --- + + Implements notable traits: Notable\<Assoc = &str, Assoc2 = char> + + --- + + size = 4, align = 4 "#]], ); } @@ -8418,8 +8577,6 @@ impl Iterator for S { ``` ```rust - // Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S> - // size = 0, align = 1 struct S ``` "#]], @@ -8478,9 +8635,12 @@ extern "C" { ``` ```rust - // size = 0, align = 1 type Ty ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -8506,9 +8666,10 @@ fn main() { "#, expect![[r#" ```rust - // Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S> S - ```"#]], + ``` + ___ + Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S>"#]], ); } @@ -8618,11 +8779,14 @@ struct Pedro$0<'a> { ``` ```rust - // size = 16 (0x10), align = 8, niches = 1 struct Pedro<'a> { hola: &str, } ``` + + --- + + size = 16 (0x10), align = 8, niches = 1 "#]], ) } @@ -8655,9 +8819,12 @@ fn main(a$0: T) {} *a* ```rust - // size = 0, align = 1 a: T ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -8678,7 +8845,6 @@ fn test() { *f* ```rust - // size = 0, align = 1 let f: fn bar<3>(bool) ``` "#]], @@ -8706,9 +8872,12 @@ fn main() { *x* ```rust - // size = 0, align = 1 let x: fn f<S, i32>() ``` + + --- + + size = 0, align = 1 "#]], ); } @@ -9037,12 +9206,15 @@ type A$0 = B; ``` ```rust - // size = 0, align = 1 type A = B ``` --- + size = 0, align = 1 + + --- + *This is the documentation for* `struct B` Docs for B @@ -9067,12 +9239,15 @@ type A$0 = B; ``` ```rust - // size = 0, align = 1 type A = B ``` --- + size = 0, align = 1 + + --- + *This is the documentation for* `struct C` Docs for C @@ -9098,12 +9273,15 @@ type A$0 = B; ``` ```rust - // size = 0, align = 1 type A = B ``` --- + size = 0, align = 1 + + --- + *This is the documentation for* `struct C` Docs for C @@ -9127,9 +9305,12 @@ type A$0 = B; ``` ```rust - // size = 0, align = 1 type A = B ``` + + --- + + size = 0, align = 1 "#]], ); @@ -9159,7 +9340,6 @@ use a::A$0; ``` ```rust - // size = 0, align = 1 pub type A = B ``` @@ -9171,3 +9351,68 @@ use a::A$0; "#]], ); } + +#[test] +fn dyn_compat() { + check( + r#" +trait Compat$0 {} +"#, + expect![[r#" + *Compat* + + ```rust + test + ``` + + ```rust + trait Compat + ``` + + --- + + Is Dyn compatible + "#]], + ); + check( + r#" +trait UnCompat$0 { + fn f<T>() {} +} +"#, + expect![[r#" + *UnCompat* + + ```rust + test + ``` + + ```rust + trait UnCompat + ``` + + --- + + Is not Dyn compatible due to having a method `f` that is not dispatchable due to missing a receiver + "#]], + ); + check( + r#" +trait UnCompat { + fn f<T>() {} +} +fn f<T: UnCompat$0> +"#, + expect![[r#" + *UnCompat* + + ```rust + test + ``` + + ```rust + trait UnCompat + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs new file mode 100644 index 00000000000..5fa6f4e4842 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs @@ -0,0 +1,64 @@ +use hir::{DefWithBody, Semantics}; +use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase}; +use std::time::{Duration, Instant}; +use stdx::format_to; +use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; + +// Feature: Interpret A Function, Static Or Const. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **rust-analyzer: Interpret** +// |=== +pub(crate) fn interpret(db: &RootDatabase, position: FilePosition) -> String { + match find_and_interpret(db, position) { + Some((duration, mut result)) => { + result.push('\n'); + format_to!(result, "----------------------\n"); + format_to!(result, " Finished in {}s\n", duration.as_secs_f32()); + result + } + _ => "Not inside a function, const or static".to_owned(), + } +} + +fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Duration, String)> { + let sema = Semantics::new(db); + let source_file = sema.parse_guess_edition(position.file_id); + + let item = ancestors_at_offset(source_file.syntax(), position.offset) + .filter(|it| !ast::MacroCall::can_cast(it.kind())) + .find_map(ast::Item::cast)?; + let def: DefWithBody = match item { + ast::Item::Fn(it) => sema.to_def(&it)?.into(), + ast::Item::Const(it) => sema.to_def(&it)?.into(), + ast::Item::Static(it) => sema.to_def(&it)?.into(), + _ => return None, + }; + let span_formatter = |file_id, text_range: TextRange| { + let path = &db + .source_root(db.file_source_root(file_id)) + .path_for_file(&file_id) + .map(|x| x.to_string()); + let path = path.as_deref().unwrap_or("<unknown file>"); + match db.line_index(file_id).try_line_col(text_range.start()) { + Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col), + None => format!("file://{path} range {text_range:?}"), + } + }; + let start_time = Instant::now(); + let res = match def { + DefWithBody::Function(it) => it.eval(db, span_formatter), + DefWithBody::Static(it) => it.eval(db), + DefWithBody::Const(it) => it.eval(db), + _ => unreachable!(), + }; + let res = res.unwrap_or_else(|e| { + let mut r = String::new(); + _ = e.pretty_print(&mut r, db, span_formatter, def.module(db).krate().edition(db)); + r + }); + let duration = Instant::now() - start_time; + Some((duration, res)) +} diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs deleted file mode 100644 index ff1317d135c..00000000000 --- a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs +++ /dev/null @@ -1,47 +0,0 @@ -use hir::Semantics; -use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase}; -use std::{fmt::Write, time::Instant}; -use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; - -// Feature: Interpret Function -// -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Interpret Function** -// |=== -pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> String { - let start_time = Instant::now(); - let mut result = - find_and_interpret(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()); - let duration = Instant::now() - start_time; - writeln!(result).unwrap(); - writeln!(result, "----------------------").unwrap(); - writeln!(result, " Finished in {}s", duration.as_secs_f32()).unwrap(); - result -} - -fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<String> { - let sema = Semantics::new(db); - let source_file = sema.parse_guess_edition(position.file_id); - - let item = ancestors_at_offset(source_file.syntax(), position.offset) - .filter(|it| !ast::MacroCall::can_cast(it.kind())) - .find_map(ast::Item::cast)?; - let def = match item { - ast::Item::Fn(it) => sema.to_def(&it)?, - _ => return None, - }; - let span_formatter = |file_id, text_range: TextRange| { - let path = &db - .source_root(db.file_source_root(file_id)) - .path_for_file(&file_id) - .map(|x| x.to_string()); - let path = path.as_deref().unwrap_or("<unknown file>"); - match db.line_index(file_id).try_line_col(text_range.start()) { - Some(line_col) => format!("file://{path}#{}:{}", line_col.line + 1, line_col.col), - None => format!("file://{path} range {text_range:?}"), - } - }; - Some(def.eval(db, span_formatter)) -} diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index d053c4b3c93..d4ef9570e1a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -33,7 +33,7 @@ mod goto_type_definition; mod highlight_related; mod hover; mod inlay_hints; -mod interpret_function; +mod interpret; mod join_lines; mod markdown_remove; mod matching_brace; @@ -350,7 +350,7 @@ impl Analysis { } pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> { - self.with_db(|db| interpret_function::interpret_function(db, position)) + self.with_db(|db| interpret::interpret(db, position)) } pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 1cbe8c62a81..0f4b5e7d87a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -212,6 +212,7 @@ impl StaticIndex<'_> { def, &node, None, + false, &hover_config, edition, )), diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index a62cbc7fb29..aecafb444c3 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -398,6 +398,8 @@ define_symbols! { rustc_const_panic_str, rustc_deprecated_safe_2024, rustc_has_incoherent_inherent_impls, + rustc_intrinsic, + rustc_intrinsic_must_be_overridden, rustc_layout_scalar_valid_range_end, rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index 7ae1e5f82e5..a50a2182a7b 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -67,7 +67,7 @@ pub(crate) mod entry { } pub(crate) fn pat_top(p: &mut Parser<'_>) { - patterns::pattern_top(p); + patterns::pattern(p); } pub(crate) fn ty(p: &mut Parser<'_>) { @@ -80,7 +80,8 @@ pub(crate) mod entry { paths::type_path(p); } pub(crate) fn item(p: &mut Parser<'_>) { - items::item_or_macro(p, true); + // We can set `is_in_extern=true`, because it only allows `safe fn`, and there is no ambiguity here. + items::item_or_macro(p, true, true); } // Parse a meta item , which excluded [], e.g : #[ MetaItem ] pub(crate) fn meta_item(p: &mut Parser<'_>) { @@ -116,7 +117,7 @@ pub(crate) mod entry { pub(crate) fn pattern(p: &mut Parser<'_>) { let m = p.start(); - patterns::pattern_top(p); + patterns::pattern(p); if p.at(EOF) { m.abandon(p); return; diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 861fcedda2a..e565874a421 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -66,7 +66,7 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { // test block_items // fn a() { fn b() {} } - let m = match items::opt_item(p, m) { + let m = match items::opt_item(p, m, false) { Ok(()) => return, Err(m) => m, }; diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 2333e6c862b..97e0392ce15 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -660,7 +660,7 @@ fn for_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { fn let_expr(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); p.bump(T![let]); - patterns::pattern_top(p); + patterns::pattern(p); p.expect(T![=]); expr_let(p); m.complete(p, LET_EXPR) diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index c4dce0daa5b..7d98499008d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -20,7 +20,8 @@ use super::*; pub(super) fn mod_contents(p: &mut Parser<'_>, stop_on_r_curly: bool) { attributes::inner_attrs(p); while !(p.at(EOF) || (p.at(T!['}']) && stop_on_r_curly)) { - item_or_macro(p, stop_on_r_curly); + // We can set `is_in_extern=true`, because it only allows `safe fn`, and there is no ambiguity here. + item_or_macro(p, stop_on_r_curly, true); } } @@ -41,11 +42,11 @@ pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[ T![;], ]); -pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) { +pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool, is_in_extern: bool) { let m = p.start(); attributes::outer_attrs(p); - let m = match opt_item(p, m) { + let m = match opt_item(p, m, is_in_extern) { Ok(()) => { if p.at(T![;]) { p.err_and_bump( @@ -91,7 +92,7 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) { } /// Try to parse an item, completing `m` in case of success. -pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { +pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Result<(), Marker> { // test_err pub_expr // fn foo() { pub 92; } let has_visibility = opt_visibility(p, false); @@ -135,7 +136,9 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { has_mods = true; } - if p.at_contextual_kw(T![safe]) { + // test safe_outside_of_extern + // fn foo() { safe = true; } + if is_in_extern && p.at_contextual_kw(T![safe]) { p.eat_contextual_kw(T![safe]); has_mods = true; } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs index c215185d632..47f86ce8c6c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs @@ -94,7 +94,7 @@ pub(crate) fn assoc_item_list(p: &mut Parser<'_>) { error_block(p, "expected an item"); continue; } - item_or_macro(p, true); + item_or_macro(p, true, false); } p.expect(T!['}']); m.complete(p, ASSOC_ITEM_LIST); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index ed01fca2acd..460051a0f4a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -20,14 +20,9 @@ const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]])); const RANGE_PAT_END_FIRST: TokenSet = expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]])); -pub(crate) fn pattern(p: &mut Parser<'_>) { - let m = p.start(); - pattern_r(p, m, false, PAT_RECOVERY_SET); -} - /// Parses a pattern list separated by pipes `|`. -pub(super) fn pattern_top(p: &mut Parser<'_>) { - pattern_top_r(p, PAT_RECOVERY_SET); +pub(crate) fn pattern(p: &mut Parser<'_>) { + pattern_r(p, PAT_RECOVERY_SET); } pub(crate) fn pattern_single(p: &mut Parser<'_>) { @@ -37,9 +32,7 @@ pub(crate) fn pattern_single(p: &mut Parser<'_>) { /// Parses a pattern list separated by pipes `|` /// using the given `recovery_set`. pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) { - let m = p.start(); - let has_leading_pipe = p.eat(T![|]); - pattern_r(p, m, has_leading_pipe, recovery_set); + pattern_r(p, recovery_set); } // test or_pattern @@ -53,7 +46,10 @@ pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) { // } /// Parses a pattern list separated by pipes `|`, with no leading `|`,using the /// given `recovery_set`. -fn pattern_r(p: &mut Parser<'_>, m: Marker, has_leading_pipe: bool, recovery_set: TokenSet) { +fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) { + let m = p.start(); + let has_leading_pipe = p.eat(T![|]); + pattern_single_r(p, recovery_set); if !p.at(T![|]) && !has_leading_pipe { @@ -319,6 +315,8 @@ fn record_pat_field(p: &mut Parser<'_>) { IDENT | INT_NUMBER if p.nth(1) == T![:] => { name_ref_or_index(p); p.bump(T![:]); + // test record_field_pat_leading_or + // fn foo() { let R { a: | 1 | 2 } = 0; } pattern(p); } // test_err record_pat_field_eq_recovery @@ -438,7 +436,7 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker { } has_rest |= p.at(T![..]); - pattern_top(p); + pattern(p); if !p.at(T![')']) { has_comma = true; p.expect(T![,]); @@ -465,7 +463,7 @@ fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker { fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) { while !p.at(EOF) && !p.at(ket) { - pattern_top(p); + pattern(p); if !p.eat(T![,]) { if p.at_ts(PAT_TOP_FIRST) { p.error(format!("expected {:?}, got {:?}", T![,], p.current())); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 164d0f36f1b..62b381b6688 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -493,6 +493,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs"); } #[test] + fn record_field_pat_leading_or() { + run_and_expect_no_errors("test_data/parser/inline/ok/record_field_pat_leading_or.rs"); + } + #[test] fn record_lit() { run_and_expect_no_errors("test_data/parser/inline/ok/record_lit.rs"); } #[test] fn record_literal_field_with_attr() { @@ -527,6 +531,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/return_type_syntax_in_path.rs"); } #[test] + fn safe_outside_of_extern() { + run_and_expect_no_errors("test_data/parser/inline/ok/safe_outside_of_extern.rs"); + } + #[test] fn self_param() { run_and_expect_no_errors("test_data/parser/inline/ok/self_param.rs"); } #[test] fn self_param_outer_attr() { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_pat_leading_or.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_pat_leading_or.rast new file mode 100644 index 00000000000..4ef6c1c45f5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_pat_leading_or.rast @@ -0,0 +1,54 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LET_STMT + LET_KW "let" + WHITESPACE " " + RECORD_PAT + PATH + PATH_SEGMENT + NAME_REF + IDENT "R" + WHITESPACE " " + RECORD_PAT_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_PAT_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + OR_PAT + PIPE "|" + WHITESPACE " " + LITERAL_PAT + LITERAL + INT_NUMBER "1" + WHITESPACE " " + PIPE "|" + WHITESPACE " " + LITERAL_PAT + LITERAL + INT_NUMBER "2" + WHITESPACE " " + R_CURLY "}" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_pat_leading_or.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_pat_leading_or.rs new file mode 100644 index 00000000000..a5e7510c140 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_pat_leading_or.rs @@ -0,0 +1 @@ +fn foo() { let R { a: | 1 | 2 } = 0; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rast new file mode 100644 index 00000000000..c9398eaa993 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rast @@ -0,0 +1,30 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "safe" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + TRUE_KW "true" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rs new file mode 100644 index 00000000000..a4d75e63ea7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rs @@ -0,0 +1 @@ +fn foo() { safe = true; } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index c965257a5c5..4045e25fdf1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -56,8 +56,8 @@ impl ProcMacroProcessSrv { match srv.version_check() { Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new( io::ErrorKind::Other, - format!( - "proc-macro server's api version ({v}) is newer than rust-analyzer's ({CURRENT_API_VERSION})" + format!( "The version of the proc-macro server ({v}) in your Rust toolchain is newer than the version supported by your rust-analyzer ({CURRENT_API_VERSION}). + This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain." ), )), Ok(v) => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 51c81a0d1a6..155fb6f7c88 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -172,7 +172,6 @@ impl flags::AnalysisStats { let mut num_decls = 0; let mut bodies = Vec::new(); let mut adts = Vec::new(); - let mut consts = Vec::new(); let mut file_ids = Vec::new(); while let Some(module) = visit_queue.pop() { if visited_modules.insert(module) { @@ -193,7 +192,6 @@ impl flags::AnalysisStats { } ModuleDef::Const(c) => { bodies.push(DefWithBody::from(c)); - consts.push(c) } ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)), _ => (), @@ -207,7 +205,6 @@ impl flags::AnalysisStats { AssocItem::Function(f) => bodies.push(DefWithBody::from(f)), AssocItem::Const(c) => { bodies.push(DefWithBody::from(c)); - consts.push(c); } _ => (), } @@ -220,7 +217,10 @@ impl flags::AnalysisStats { visited_modules.len(), bodies.len(), adts.len(), - consts.len(), + bodies + .iter() + .filter(|it| matches!(it, DefWithBody::Const(_) | DefWithBody::Static(_))) + .count(), ); let crate_def_map_time = crate_def_map_sw.elapsed(); eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time); @@ -247,7 +247,7 @@ impl flags::AnalysisStats { } if !self.skip_const_eval { - self.run_const_eval(db, &consts, verbosity); + self.run_const_eval(db, &bodies, verbosity); } if self.run_all_ide_things { @@ -320,18 +320,23 @@ impl flags::AnalysisStats { report_metric("data layout time", data_layout_time.time.as_millis() as u64, "ms"); } - fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) { + fn run_const_eval(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) { let mut sw = self.stop_watch(); let mut all = 0; let mut fail = 0; - for &c in consts { + for &b in bodies { + let res = match b { + DefWithBody::Const(c) => c.render_eval(db, Edition::LATEST), + DefWithBody::Static(s) => s.render_eval(db, Edition::LATEST), + _ => continue, + }; all += 1; - let Err(error) = c.render_eval(db, Edition::LATEST) else { + let Err(error) = res else { continue; }; if verbosity.is_spammy() { let full_name = - full_name_of_item(db, c.module(db), c.name(db).unwrap_or(Name::missing())); + full_name_of_item(db, b.module(db), b.name(db).unwrap_or(Name::missing())); println!("Const eval for {full_name} failed due {error:?}"); } fail += 1; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs index 11534bbeba9..7398b9a9ef0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs @@ -61,12 +61,11 @@ impl flags::RunTests { } let mut sw_one = StopWatch::start(); let result = test.eval(db, span_formatter); - if result.trim() == "pass" { - pass_count += 1; - } else { - fail_count += 1; + match &result { + Ok(result) if result.trim() == "pass" => pass_count += 1, + _ => fail_count += 1, } - println!("{result}"); + println!("{result:?}"); eprintln!("{:<20} {}", format!("test {}", full_name), sw_one.elapsed()); } println!("{pass_count} passed, {fail_count} failed, {ignore_count} ignored"); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs index 271a9c0f3d1..1db616898e8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs @@ -463,6 +463,11 @@ impl ClientCapabilities { .unwrap_or_default() } + pub fn diagnostics_refresh(&self) -> bool { + (|| -> _ { self.0.workspace.as_ref()?.diagnostic.as_ref()?.refresh_support })() + .unwrap_or_default() + } + pub fn inlay_hint_resolve_support_properties(&self) -> FxHashSet<&str> { self.0 .text_document diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 9a51df80fe1..73fce42437f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -417,8 +417,6 @@ impl GlobalState { } } - let supports_diagnostic_pull_model = self.config.text_document_diagnostic(); - let client_refresh = became_quiescent || state_changed; if client_refresh { // Refresh semantic tokens if the client supports it. @@ -437,7 +435,7 @@ impl GlobalState { self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ()); } - if supports_diagnostic_pull_model { + if self.config.diagnostics_refresh() { self.send_request::<lsp_types::request::WorkspaceDiagnosticRefresh>( (), |_, _| (), @@ -448,7 +446,7 @@ impl GlobalState { let project_or_mem_docs_changed = became_quiescent || state_changed || memdocs_added_or_removed; if project_or_mem_docs_changed - && !supports_diagnostic_pull_model + && !self.config.text_document_diagnostic() && self.config.publish_diagnostics(None) { self.update_diagnostics(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index fcdc97ce327..2ec83d23b27 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -15,7 +15,11 @@ use parser::{Edition, T}; use rowan::NodeOrToken; use stdx::{format_to, format_to_acc, never}; -use crate::{ast, utils::is_raw_identifier, AstNode, SourceFile, SyntaxKind, SyntaxToken}; +use crate::{ + ast::{self, Param}, + utils::is_raw_identifier, + AstNode, SourceFile, SyntaxKind, SyntaxToken, +}; /// While the parent module defines basic atomic "constructors", the `ext` /// module defines shortcuts for common things. @@ -198,6 +202,38 @@ pub fn ty_alias( ast_from_text(&s) } +pub fn ty_fn_ptr<I: Iterator<Item = Param>>( + for_lifetime_list: Option<ast::GenericParamList>, + is_unsafe: bool, + abi: Option<ast::Abi>, + params: I, + ret_type: Option<ast::RetType>, +) -> ast::FnPtrType { + let mut s = String::from("type __ = "); + + if let Some(list) = for_lifetime_list { + format_to!(s, "for{} ", list); + } + + if is_unsafe { + s.push_str("unsafe "); + } + + if let Some(abi) = abi { + format_to!(s, "{} ", abi) + } + + s.push_str("fn"); + + format_to!(s, "({})", params.map(|p| p.to_string()).join(", ")); + + if let Some(ret_type) = ret_type { + format_to!(s, " {}", ret_type); + } + + ast_from_text(&s) +} + pub fn assoc_item_list() -> ast::AssocItemList { ast_from_text("impl C for D {}") } @@ -862,6 +898,10 @@ pub fn item_const( ast_from_text(&format!("{visibility} const {name}: {ty} = {expr};")) } +pub fn unnamed_param(ty: ast::Type) -> ast::Param { + ast_from_text(&format!("fn f({ty}) {{ }}")) +} + pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { ast_from_text(&format!("fn f({pat}: {ty}) {{ }}")) } diff --git a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml index f9565721dd5..c860e7b1183 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml @@ -5,7 +5,6 @@ rust-version.workspace = true edition.workspace = true license.workspace = true authors.workspace = true -publish = false [dependencies] hir-expand.workspace = true diff --git a/src/tools/rust-analyzer/docs/dev/setup.md b/src/tools/rust-analyzer/docs/dev/setup.md new file mode 100644 index 00000000000..d8a7840d376 --- /dev/null +++ b/src/tools/rust-analyzer/docs/dev/setup.md @@ -0,0 +1,57 @@ +# Setup Guide + +This guide gives a simplified opinionated setup for developers contributing to rust-analyzer using Visual Studio Code to make changes and Visual Studio Code Insiders to test those changes. This guide will assume you have Visual Studio Code and Visual Studio Code Insiders installed. + +## Prerequisites + +Since rust-analyzer is a Rust project, you will need to install Rust. You can download and install the latest stable version of Rust [here](https://www.rust-lang.org/tools/install). + +## Step-by-Step Setup + +**Step 01**: Fork the rust-analyzer repository and clone the fork to your local machine. + +**Step 02**: Open the project in Visual Studio Code. + +**Step 03**: Open a terminal and run `cargo build` to build the project. + +**Step 04**: Install the language server locally by running the following command: + +```sh +cargo xtask install --server --code-bin code-insiders --dev-rel +``` + +In the output of this command, there should be a file path provided to the installed binary on your local machine. +It should look something like the following output below: + +``` +Installing <path-to-rust-analyzer-binary> +Installed package `rust-analyzer v0.0.0 (<path-to-rust-analyzer-binary>)` (executable `rust-analyzer.exe`) +``` + +In Visual Studio Code Insiders, you will want to open your User Settings (JSON) from the Command Palette. From there you should ensure that the `rust-analyzer.server.path` key is set to the `<path-to-rust-analyzer-binary>`. This will tell Visual Studio Code Insiders to use the locally installed version that you can debug. + +The User Settings (JSON) file should contain the following: + +```json +{ + "rust-analyzer.server.path": "<path-to-rust-analyzer-binary>" +} +``` + +Now you should be able to make changes to rust-analyzer in Visual Studio Code and then view the changes in Visual Studio Code Insiders. + +## Debugging rust-analyzer +The simplest way to debug rust-analyzer is to use the `eprintln!` macro. The reason why we use `eprintln!` instead of `println!` is because the language server uses `stdout` to send messages. So instead we will debug using `stderr`. + +An example debugging statement could go into the `main_loop.rs` file which can be found at `crates/rust-analyzer/src/main_loop.rs`. Inside the `main_loop` we will add the following `eprintln!` to test debugging rust-analyzer: + +```rs +eprintln!("Hello, world!"); +``` + +Now we run `cargo build` and `sh +cargo xtask install --server --code-bin code-insiders --dev-rel` to reinstall the server. + +Now on Visual Studio Code Insiders, we should be able to open the Output tab on our terminal and switch to Rust Analyzer Language Server to see the `eprintln!` statement we just wrote. + +If you are able to see your output, you now have a complete workflow for debugging rust-analyzer. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 6eebdf9f016..ccc8c0e3842 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -125,7 +125,7 @@ }, { "command": "rust-analyzer.interpretFunction", - "title": "Interpret Function", + "title": "Interpret", "category": "rust-analyzer (debug command)" }, { diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index ffb312d06e6..00f4f743a7c 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -a9d17627d241645a54c1134a20f1596127fedb60 +145f9cf95de1fbde3fa11e98461310e0373253e6 diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs index c3d531344a1..35412be8764 100644 --- a/src/tools/rust-analyzer/xtask/src/tidy.rs +++ b/src/tools/rust-analyzer/xtask/src/tidy.rs @@ -135,7 +135,6 @@ Apache-2.0 WITH LLVM-exception Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT Apache-2.0/MIT BSD-2-Clause OR Apache-2.0 OR MIT -BSD-3-Clause CC0-1.0 ISC MIT diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index fc043a697e0..327a547b295 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -2009,6 +2009,7 @@ pub(crate) struct StaticParts<'a> { safety: ast::Safety, vis: &'a ast::Visibility, ident: symbol::Ident, + generics: Option<&'a ast::Generics>, ty: &'a ast::Ty, mutability: ast::Mutability, expr_opt: Option<&'a ptr::P<ast::Expr>>, @@ -2018,8 +2019,10 @@ pub(crate) struct StaticParts<'a> { impl<'a> StaticParts<'a> { pub(crate) fn from_item(item: &'a ast::Item) -> Self { - let (defaultness, prefix, safety, ty, mutability, expr) = match &item.kind { - ast::ItemKind::Static(s) => (None, "static", s.safety, &s.ty, s.mutability, &s.expr), + let (defaultness, prefix, safety, ty, mutability, expr, generics) = match &item.kind { + ast::ItemKind::Static(s) => { + (None, "static", s.safety, &s.ty, s.mutability, &s.expr, None) + } ast::ItemKind::Const(c) => ( Some(c.defaultness), "const", @@ -2027,6 +2030,7 @@ impl<'a> StaticParts<'a> { &c.ty, ast::Mutability::Not, &c.expr, + Some(&c.generics), ), _ => unreachable!(), }; @@ -2035,6 +2039,7 @@ impl<'a> StaticParts<'a> { safety, vis: &item.vis, ident: item.ident, + generics, ty, mutability, expr_opt: expr.as_ref(), @@ -2044,8 +2049,8 @@ impl<'a> StaticParts<'a> { } pub(crate) fn from_trait_item(ti: &'a ast::AssocItem) -> Self { - let (defaultness, ty, expr_opt) = match &ti.kind { - ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr), + let (defaultness, ty, expr_opt, generics) = match &ti.kind { + ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr, Some(&c.generics)), _ => unreachable!(), }; StaticParts { @@ -2053,6 +2058,7 @@ impl<'a> StaticParts<'a> { safety: ast::Safety::Default, vis: &ti.vis, ident: ti.ident, + generics, ty, mutability: ast::Mutability::Not, expr_opt: expr_opt.as_ref(), @@ -2062,8 +2068,8 @@ impl<'a> StaticParts<'a> { } pub(crate) fn from_impl_item(ii: &'a ast::AssocItem) -> Self { - let (defaultness, ty, expr) = match &ii.kind { - ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr), + let (defaultness, ty, expr, generics) = match &ii.kind { + ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr, Some(&c.generics)), _ => unreachable!(), }; StaticParts { @@ -2071,6 +2077,7 @@ impl<'a> StaticParts<'a> { safety: ast::Safety::Default, vis: &ii.vis, ident: ii.ident, + generics, ty, mutability: ast::Mutability::Not, expr_opt: expr.as_ref(), @@ -2085,6 +2092,14 @@ fn rewrite_static( static_parts: &StaticParts<'_>, offset: Indent, ) -> Option<String> { + // For now, if this static (or const) has generics, then bail. + if static_parts + .generics + .is_some_and(|g| !g.params.is_empty() || !g.where_clause.is_empty()) + { + return None; + } + let colon = colon_spaces(context.config); let mut prefix = format!( "{}{}{}{} {}{}{}", diff --git a/src/tools/rustfmt/tests/target/const-generics.rs b/src/tools/rustfmt/tests/target/const-generics.rs new file mode 100644 index 00000000000..94f76643664 --- /dev/null +++ b/src/tools/rustfmt/tests/target/const-generics.rs @@ -0,0 +1,25 @@ +// Make sure we don't mess up the formatting of generic consts + +#![feature(generic_const_items)] + +const GENERIC<N, const M: usize>: i32 = 0; + +const WHERECLAUSE: i32 = 0 +where + i32:; + +trait Foo { + const GENERIC<N, const M: usize>: i32; + + const WHERECLAUSE: i32 + where + i32:; +} + +impl Foo for () { + const GENERIC<N, const M: usize>: i32 = 0; + + const WHERECLAUSE: i32 = 0 + where + i32:; +} diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index f3bd72fe647..8e12db409ec 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -65,9 +65,6 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>, ), // tidy-alphabetical-start ("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]), - //("library/backtrace", &[], None), // FIXME uncomment once rust-lang/backtrace#562 has been synced back to the rust repo - //("library/portable-simd", &[], None), // FIXME uncomment once rust-lang/portable-simd#363 has been synced back to the rust repo - //("library/stdarch", EXCEPTIONS_STDARCH, None), // FIXME uncomment once rust-lang/stdarch#1462 has been synced back to the rust repo ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]), ("src/ci/docker/host-x86_64/test-various/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None, &[]), ("src/etc/test-float-parse", EXCEPTIONS, None, &[]), @@ -116,17 +113,6 @@ const EXCEPTIONS_STDLIB: ExceptionList = &[ // tidy-alphabetical-end ]; -// FIXME uncomment once rust-lang/stdarch#1462 lands -/* -const EXCEPTIONS_STDARCH: ExceptionList = &[ - // tidy-alphabetical-start - ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 - ("wasmparser", "Apache-2.0 WITH LLVM-exception"), - ("wasmprinter", "Apache-2.0 WITH LLVM-exception"), - // tidy-alphabetical-end -]; -*/ - const EXCEPTIONS_CARGO: ExceptionList = &[ // tidy-alphabetical-start ("arrayref", "BSD-2-Clause"), @@ -203,7 +189,7 @@ const EXCEPTIONS_CRANELIFT: ExceptionList = &[ ("cranelift-module", "Apache-2.0 WITH LLVM-exception"), ("cranelift-native", "Apache-2.0 WITH LLVM-exception"), ("cranelift-object", "Apache-2.0 WITH LLVM-exception"), - ("mach", "BSD-2-Clause"), + ("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"), ("regalloc2", "Apache-2.0 WITH LLVM-exception"), ("target-lexicon", "Apache-2.0 WITH LLVM-exception"), ("wasmtime-jit-icache-coherence", "Apache-2.0 WITH LLVM-exception"), @@ -537,7 +523,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "libc", "libloading", "log", - "mach", + "mach2", "memchr", "object", "once_cell", @@ -554,9 +540,6 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "unicode-ident", "version_check", "wasmtime-jit-icache-coherence", - "winapi", - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", "windows-sys", "windows-targets", "windows_aarch64_gnullvm", diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs index 8cbc263689b..edf7658d25f 100644 --- a/src/tools/tidy/src/walk.rs +++ b/src/tools/tidy/src/walk.rs @@ -7,7 +7,6 @@ use ignore::DirEntry; /// The default directory filter. pub fn filter_dirs(path: &Path) -> bool { - // FIXME: sync submodule exclusion list with rustfmt.toml // bootstrap/etc let skip = [ "tidy-test-file", diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs index 036f0bd7eac..1eb35e819c0 100644 --- a/src/tools/unicode-table-generator/src/cascading_map.rs +++ b/src/tools/unicode-table-generator/src/cascading_map.rs @@ -65,7 +65,7 @@ impl RawEmitter { self.bytes_used += 256; writeln!(&mut self.file, "#[inline]").unwrap(); - writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap(); writeln!(&mut self.file, " match c as u32 >> 8 {{").unwrap(); for arm in arms { writeln!(&mut self.file, " {},", arm).unwrap(); diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index e1832091d70..415db2c4dbc 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -268,7 +268,7 @@ fn main() { let mut table_file = String::new(); table_file.push_str( - "///! This file is generated by src/tools/unicode-table-generator; do not edit manually!\n", + "///! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually!\n", ); // Include the range search function |
