diff options
650 files changed, 15771 insertions, 10046 deletions
diff --git a/.github/workflows/clippy_changelog.yml b/.github/workflows/clippy_changelog.yml index 1e97154bf8a..4d84d6b6dae 100644 --- a/.github/workflows/clippy_changelog.yml +++ b/.github/workflows/clippy_changelog.yml @@ -15,27 +15,18 @@ jobs: changelog: runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: # Run - name: Check Changelog if: ${{ github.event_name == 'pull_request' }} run: | - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") - if [ -z "$output" ]; then - echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it." + if [[ -z $(grep -oP 'changelog: *\K\S+' <<< "$PR_BODY") ]]; then + echo "::error::Pull request message must contain 'changelog: ...' with your changelog. Please add it." exit 1 - else - echo "changelog: $output" fi env: - PYTHONIOENCODING: 'utf-8' - PR_NUMBER: '${{ github.event.number }}' + PR_BODY: ${{ github.event.pull_request.body }}) + # We need to have the "conclusion" job also on PR CI, to make it possible # to add PRs to a merge queue. diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index 741e7457331..07d5a08304e 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -66,7 +66,7 @@ jobs: run: cargo test --features internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features internal + run: cargo test working-directory: clippy_lints - name: Test clippy_utils diff --git a/.github/workflows/clippy_pr.yml b/.github/workflows/clippy_pr.yml index 80523d91f4f..880ebd6e5d5 100644 --- a/.github/workflows/clippy_pr.yml +++ b/.github/workflows/clippy_pr.yml @@ -42,7 +42,7 @@ jobs: run: cargo test --features internal - name: Test clippy_lints - run: cargo test --features internal + run: cargo test working-directory: clippy_lints - name: Test clippy_utils diff --git a/CHANGELOG.md b/CHANGELOG.md index a8b703028f2..28147dfbea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,105 @@ document. ## Unreleased / Beta / In Rust Nightly -[3e3715c3...master](https://github.com/rust-lang/rust-clippy/compare/3e3715c3...master) +[1e5237f4...master](https://github.com/rust-lang/rust-clippy/compare/1e5237f4...master) + +## Rust 1.87 + +Current stable, released 2025-05-15 + +[View all 127 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-02-06T14%3A54%3A28Z..2025-03-20T20%3A07%3A53Z+base%3Amaster) + +### New Lints + +* Added [`doc_comment_double_space_linebreaks`] to `pedantic` [#12876](https://github.com/rust-lang/rust-clippy/pull/12876) +* Added [`manual_midpoint`] to `pedantic` [#13851](https://github.com/rust-lang/rust-clippy/pull/13851) +* Added [`io_other_error`] to `style` [#14022](https://github.com/rust-lang/rust-clippy/pull/14022) +* Added [`owned_cow`] to `pedantic` [#13948](https://github.com/rust-lang/rust-clippy/pull/13948) +* Added [`manual_contains`] to `perf` [#13817](https://github.com/rust-lang/rust-clippy/pull/13817) +* Added [`unnecessary_debug_formatting`] to `pedantic` [#13893](https://github.com/rust-lang/rust-clippy/pull/13893) +* Added [`elidable_lifetime_names`] to `pedantic` [#13960](https://github.com/rust-lang/rust-clippy/pull/13960) +* Added [`mem_replace_option_with_some`] to `style` [#14197](https://github.com/rust-lang/rust-clippy/pull/14197) +* Added [`unbuffered_bytes`] to `perf` [#14089](https://github.com/rust-lang/rust-clippy/pull/14089) +* Added [`single_option_map`] to `nursery` [#14033](https://github.com/rust-lang/rust-clippy/pull/14033) + +### Moves and Deprecations + +* Moved [`comparison_chain`] to `pedantic` (from `style`) + [#14219](https://github.com/rust-lang/rust-clippy/pull/14219) +* Moved [`manual_ok_or`] to `style` (from `pedantic`) + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) +* Deprecated [`option_map_or_err_ok`] in favor of [`manual_ok_or`] + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) + +### Enhancements + +* Add `allow_expect_in_consts` and `allow_unwrap_in_consts` configuration options to [`unwrap_used`], [`expect_used`] + [#14200](https://github.com/rust-lang/rust-clippy/pull/14200) +* Add `check-incompatible-msrv-in-tests` configuration option to [`incompatible_msrv`] + [#14279](https://github.com/rust-lang/rust-clippy/pull/14279) +* [`len_zero`] now also triggers if deref target implements `is_empty()` + [#13871](https://github.com/rust-lang/rust-clippy/pull/13871) +* [`ptr_eq`] now handles more cases, including `!=` in addition to `==` + [#14339](https://github.com/rust-lang/rust-clippy/pull/14339) +* [`struct_field_names`] now also checks private fields of public structs + [#14076](https://github.com/rust-lang/rust-clippy/pull/14076) +* [`needless_pass_by_value`] suggests using a reference on the innermost `Option` content + [#14392](https://github.com/rust-lang/rust-clippy/pull/14392) +* [`obfuscated_if_else`] now supports `then().unwrap_or_else()` and `then_some().unwrap_or_else()` + [#14165](https://github.com/rust-lang/rust-clippy/pull/14165) +* Format macros: all format-handling lints now validate `todo!` and `unimplemented!` macros + [#14266](https://github.com/rust-lang/rust-clippy/pull/14266) +* [`disallowed_methods`] now supports replacements + [#13669](https://github.com/rust-lang/rust-clippy/pull/13669) +* Added MSRV checks for several lints: + * [`question_mark`] [#14436](https://github.com/rust-lang/rust-clippy/pull/14436) + * [`repeat_vec_with_capacity`] [#14126](https://github.com/rust-lang/rust-clippy/pull/14126) + * [`manual_flatten`] [#14086](https://github.com/rust-lang/rust-clippy/pull/14086) + * [`lines_filter_map_ok`] [#14130](https://github.com/rust-lang/rust-clippy/pull/14130) + +### False Positive Fixes + +* [`missing_const_for_fn`] no longer triggers on unstable const traits [#14294](https://github.com/rust-lang/rust-clippy/pull/14294) +* [`unnecessary_to_owned`] now avoids suggesting to call `iter()` on a temporary object [#14243](https://github.com/rust-lang/rust-clippy/pull/14243) +* [`unnecessary_debug_formatting`] no longer triggers in tests [#14347](https://github.com/rust-lang/rust-clippy/pull/14347) +* [`option_if_let_else`] now handles cases when value is partially moved [#14209](https://github.com/rust-lang/rust-clippy/pull/14209) +* [`blocks_in_conditions`] no longer triggers when the condition contains a `return` [#14338](https://github.com/rust-lang/rust-clippy/pull/14338) +* [`undocumented_unsafe_blocks`] no longer triggers on trait/impl items [#13888](https://github.com/rust-lang/rust-clippy/pull/13888) +* [`manual_slice_fill`] no longer triggers due to missing index checks [#14193](https://github.com/rust-lang/rust-clippy/pull/14193) +* [`useless_asref`] no longer suggests using `.clone()` if the target type doesn't implement `Clone` [#14174](https://github.com/rust-lang/rust-clippy/pull/14174) +* [`unnecessary_safety_comment`] no longer triggers on desugared assign [#14371](https://github.com/rust-lang/rust-clippy/pull/14371) +* [`unnecessary_map_or`] no longer consumes the comparison value if it does not implement `Copy` [#14207](https://github.com/rust-lang/rust-clippy/pull/14207) +* [`let_and_return`] no longer triggers involving short-lived block temporary variables [#14180](https://github.com/rust-lang/rust-clippy/pull/14180) +* [`manual_async_fn`] no longer emits suggestions inside macros [#14142](https://github.com/rust-lang/rust-clippy/pull/14142) +* [`use_self`] skips analysis inside macro expansions of a `impl Self` block [#13128](https://github.com/rust-lang/rust-clippy/pull/13128) +* [`double_ended_iterator_last`] no longer triggers on non-reference immutable receiver [#14140](https://github.com/rust-lang/rust-clippy/pull/14140) + +### ICE Fixes + +* [`macro_use_imports`] Fix ICE when checking attributes + [#14317](https://github.com/rust-lang/rust-clippy/pull/14317) +* [`doc_nested_refdefs`] Fix ICE by avoiding invalid ranges + [#14308](https://github.com/rust-lang/rust-clippy/pull/14308) +* [`just_underscores_and_digits`] Fix ICE in error recovery scenario + [#14168](https://github.com/rust-lang/rust-clippy/pull/14168) +* [`declare_interior_mutable_const`], [`borrow_interior_mutable_const`] Fix ICE by properly resolving `<T as Trait>::AssocT` projections + [#14125](https://github.com/rust-lang/rust-clippy/pull/14125) + +### Documentation Improvements + +* [`struct_excessive_bools`] Documentation improved with rationale + [#14351](https://github.com/rust-lang/rust-clippy/pull/14351) + +### Others + +* Use edition=2021 in `rustc_tools_util` + [#14211](https://github.com/rust-lang/rust-clippy/pull/14211) +* Fix rustc_tools_util's `version.host_compiler` release channel, expose the rustc version, and add tests + [#14123](https://github.com/rust-lang/rust-clippy/pull/14123) +* Make UI test annotations mandatory + [#11421](https://github.com/rust-lang/rust-clippy/pull/11421) + [#14388](https://github.com/rust-lang/rust-clippy/pull/14388) + [#14393](https://github.com/rust-lang/rust-clippy/pull/14393) ## Rust 1.86 @@ -5583,6 +5681,7 @@ Released 2018-09-13 [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr [`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied +[`cloned_ref_to_slice_refs`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_ref_to_slice_refs [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned @@ -5594,6 +5693,7 @@ Released 2018-09-13 [`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`confusing_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#confusing_method_to_numeric_cast [`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator @@ -6116,6 +6216,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes +[`redundant_test_prefix`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_test_prefix [`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations [`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference @@ -6217,6 +6318,7 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref +[`swap_with_temporary`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_with_temporary [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr @@ -6381,6 +6483,7 @@ Released 2018-09-13 [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero [`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests +[`allow-exact-repetitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-exact-repetitions [`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests @@ -6433,6 +6536,7 @@ Released 2018-09-13 [`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length [`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-allow-unused`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-allow-unused [`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 [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b33f719063..45ba2f078be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,7 +77,7 @@ debugging to find the actual problem behind the issue. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of -an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful. +an AST expression). [`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed diff --git a/Cargo.toml b/Cargo.toml index 5a6904d63be..b6a1b9314c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" @@ -25,8 +25,10 @@ path = "src/driver.rs" [dependencies] clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } +clippy_utils = { path = "clippy_utils" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } -tempfile = { version = "3.3", optional = true } +clippy_lints_internal = { path = "clippy_lints_internal", optional = true } +tempfile = { version = "3.20", optional = true } termize = "0.1" color-print = "0.3.4" anstream = "0.6.18" @@ -45,7 +47,6 @@ pulldown-cmark = { version = "0.11", default-features = false, features = ["html askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } # UI test dependencies -clippy_utils = { path = "clippy_utils" } if_chain = "1.0" quote = "1.0.25" syn = { version = "2.0", features = ["full"] } @@ -57,8 +58,8 @@ tokio = { version = "1", features = ["io-util"] } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } [features] -integration = ["tempfile"] -internal = ["clippy_lints/internal", "tempfile"] +integration = ["dep:tempfile"] +internal = ["dep:clippy_lints_internal", "dep:tempfile"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index e5e82ede4fd..2b89e94cf8f 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -416,7 +416,7 @@ In our example, `is_foo_fn` looks like: fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { match fn_kind { - FnKind::Fn(_, ident, ..) => { + FnKind::Fn(_, _, Fn { ident, .. }) => { // check if `fn` name is `foo` ident.name.as_str() == "foo" } diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 4219724ed5d..cdbbe76bdb0 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -145,42 +145,32 @@ unclear to you. If you are hacking on Clippy and want to install it from source, do the following: -First, take note of the toolchain -[override](https://rust-lang.github.io/rustup/overrides.html) in -`/rust-toolchain.toml`. We will use this override to install Clippy into the right -toolchain. - -> Tip: You can view the active toolchain for the current directory with `rustup -> show active-toolchain`. - From the Clippy project root, run the following command to build the Clippy -binaries and copy them into the toolchain directory. This will override the -currently installed Clippy component. +binaries and copy them into the toolchain directory. This will create a new +toolchain called `clippy` by default, see `cargo dev setup toolchain --help` +for other options. ```terminal -cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" +cargo dev setup toolcahin ``` -Now you may run `cargo clippy` in any project, using the toolchain where you -just installed Clippy. +Now you may run `cargo +clippy clippy` in any project using the new toolchain. ```terminal cd my-project -cargo +nightly-2021-07-01 clippy +cargo +clippy clippy ``` ...or `clippy-driver` ```terminal -clippy-driver +nightly-2021-07-01 <filename> +clippy-driver +clippy <filename> ``` -If you need to restore the default Clippy installation, run the following (from -the Clippy project root). +If you no longer need the toolchain it can be uninstalled using `rustup`: ```terminal -rustup component remove clippy -rustup component add clippy +rustup toolchain uninstall clippy ``` > **DO NOT** install using `cargo install --path . --force` since this will diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 051febc2ca5..e23b32039c9 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -86,7 +86,7 @@ arguments have to be checked separately. ```rust use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{paths, match_def_path}; +use clippy_utils::paths; use rustc_span::symbol::sym; use rustc_hir::LangItem; @@ -108,7 +108,7 @@ impl LateLintPass<'_> for MyStructLint { // 3. Using the type path // This method should be avoided if possible - if match_def_path(cx, def_id, &paths::RESULT) { + if paths::RESULT.matches_ty(cx, ty) { // The type is a `core::result::Result` } } @@ -159,19 +159,21 @@ paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::return_ty; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{sym, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { // 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.as_str() == "some_method" + // + // Add `some_method` to `clippy_utils::sym` if it's not already there + && impl_item.ident.name == sym::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` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) + && is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String) { // ... } diff --git a/book/src/development/defining_lints.md b/book/src/development/defining_lints.md index 169cecd7d11..cb6d7b740db 100644 --- a/book/src/development/defining_lints.md +++ b/book/src/development/defining_lints.md @@ -9,7 +9,7 @@ lint involves some boilerplate code. A lint type is the category of items and expressions in which your lint focuses on. -As of the writing of this documentation update, there are 12 _types_ of lints +As of the writing of this documentation update, there are 11 _types_ of lints besides the numerous standalone lints living under `clippy_lints/src/`: - `cargo` @@ -23,7 +23,6 @@ besides the numerous standalone lints living under `clippy_lints/src/`: - `transmute` - `types` - `unit_types` -- `utils / internal` (Clippy internal lints) These types group together lints that share some common behaviors. For instance, `functions` groups together lints that deal with some aspects of functions in diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index 2b2c096b049..eede6b78d92 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -51,7 +51,9 @@ Once you've got the correct commit range, run util/fetch_prs_between.sh commit1 commit2 > changes.txt ``` -and open that file in your editor of choice. +where `commit2` is the commit hash from the previous command and `commit1` +is the commit hash from the current CHANGELOG file. +Open `changes.txt` file in your editor of choice. When updating the changelog it's also a good idea to make sure that `commit1` is already correct in the current changelog. @@ -60,8 +62,8 @@ already correct in the current changelog. The above script should have dumped all the relevant PRs to the file you specified. It should have filtered out most of the irrelevant PRs already, but -it's a good idea to do a manual cleanup pass where you look for more irrelevant -PRs. If you're not sure about some PRs, just leave them in for the review and +it's a good idea to do a manual cleanup pass and choose valuable PRs. +If you're not sure about some PRs, just leave them in for the review and ask for feedback. With the PRs filtered, you can start to take each PR and move the `changelog: ` @@ -74,10 +76,9 @@ The order should roughly be: 2. Moves or deprecations of lints 3. Changes that expand what code existing lints cover 4. False positive fixes -5. Suggestion fixes/improvements -6. ICE fixes -7. Documentation improvements -8. Others +5. ICE fixes +6. Documentation improvements +7. Others As section headers, we use: @@ -91,7 +92,6 @@ As section headers, we use: ### Enhancements ### False Positive Fixes -### Suggestion Fixes/Improvements ### ICE Fixes ### Documentation Improvements ### Others @@ -112,7 +112,16 @@ that label in the changelog. If you can, remove the `beta-accepted` labels ### 4. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added -lints contain the correct version. +lints contain the correct version. +In order to find lints that need a version update, go through the lints in the +"New Lints" section and run the following command for each lint name: + +``` +grep -rB1 "pub $LINT_NAME" . +``` + +The version shown should match the version of the release the changelog is +written for. If not, update the version to the changelog version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index b7d229ccc19..cc4eb966f59 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -73,22 +73,24 @@ impl LateLintPass<'_> for CheckDropTraitLint { ## Using Type Path If neither diagnostic item nor a language item is available, we can use -[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait -implementation. +[`clippy_utils::paths`][paths] to determine get a trait's `DefId`. > **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item. -Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` : +Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html): ```rust -use clippy_utils::{match_trait_method, paths}; +use clippy_utils::{implements_trait, paths}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { +impl LateLintPass<'_> for CheckIterStep { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) { - println!("`expr` implements `CORE_ITER_CLONED` trait!"); + let ty = cx.typeck_results().expr_ty(expr); + if let Some(trait_def_id) = paths::ITER_STEP.first(cx) + && implements_trait(cx, ty, trait_def_id, &[]) + { + println!("`expr` implements the `core::iter::Step` trait!"); } } } diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 93506b81025..0db4182dbcd 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` * [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) +## `allow-exact-repetitions` +Whether an item should be allowed to have the same name as its containing module + +**Default Value:** `true` + +--- +**Affected lints:** +* [`module_name_repetitions`](https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions) + + ## `allow-expect-in-consts` Whether `expect` should be allowed in code always evaluated at compile time @@ -734,6 +744,16 @@ Minimum chars an ident can have, anything below or equal to this will be linted. * [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) +## `missing-docs-allow-unused` +Whether to allow fields starting with an underscore to skip documentation requirements + +**Default Value:** `false` + +--- +**Affected lints:** +* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) + + ## `missing-docs-in-crate-items` Whether to **only** check for missing documentation in items visible within the current crate. For example, `pub(crate)` items. @@ -805,6 +825,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten) * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) * [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [`manual_is_power_of_two`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two) * [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [`manual_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint) * [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) @@ -838,6 +859,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) diff --git a/clippy.toml b/clippy.toml index 0a7724bbe4e..77573105d86 100644 --- a/clippy.toml +++ b/clippy.toml @@ -7,14 +7,11 @@ lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" -allow-invalid = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" -allow-invalid = true [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" -allow-invalid = true diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 93fd2e35d1b..1134b0e97af 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version edition = "2024" publish = false diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 8d72c26b8b5..ad0aea39d41 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -360,6 +360,9 @@ define_Conf! { /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` #[lints(dbg_macro)] allow_dbg_in_tests: bool = false, + /// Whether an item should be allowed to have the same name as its containing module + #[lints(module_name_repetitions)] + allow_exact_repetitions: bool = true, /// Whether `expect` should be allowed in code always evaluated at compile time #[lints(expect_used)] allow_expect_in_consts: bool = true, @@ -675,6 +678,9 @@ define_Conf! { /// Minimum chars an ident can have, anything below or equal to this will be linted. #[lints(min_ident_chars)] min_ident_chars_threshold: u64 = 1, + /// Whether to allow fields starting with an underscore to skip documentation requirements + #[lints(missing_docs_in_private_items)] + missing_docs_allow_unused: bool = false, /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. #[lints(missing_docs_in_private_items)] @@ -722,6 +728,7 @@ define_Conf! { manual_flatten, manual_hash_one, manual_is_ascii_check, + manual_is_power_of_two, manual_let_else, manual_midpoint, manual_non_exhaustive, @@ -755,6 +762,7 @@ define_Conf! { same_item_push, seek_from_current, seek_rewind, + to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index c227b8900b7..67904b4fcdc 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, array_windows, let_chains)] +#![feature(rustc_private)] #![warn( trivial_casts, trivial_numeric_casts, @@ -12,6 +12,7 @@ rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] +#![deny(clippy::derive_deserialize_allowing_unknown)] extern crate rustc_data_structures; extern crate rustc_errors; diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 5949eaca7bc..f64eefa0c23 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,16 +1,18 @@ +use clippy_utils::paths::{PathNS, find_crates, lookup_path}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; use rustc_hir::PrimTy; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use serde::de::{self, Deserializer, Visitor}; use serde::{Deserialize, Serialize, ser}; use std::collections::HashMap; use std::fmt; #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Rename { pub path: String, pub rename: String, @@ -58,7 +60,7 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<R // `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just // above. `DisallowedPathEnum` is not meant to be used outside of this file. #[derive(Debug, Deserialize, Serialize)] -#[serde(untagged)] +#[serde(untagged, deny_unknown_fields)] enum DisallowedPathEnum { Simple(String), WithReason { @@ -133,6 +135,7 @@ impl DisallowedPathEnum { pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>( tcx: TyCtxt<'_>, disallowed_paths: &'static [DisallowedPath<REPLACEMENT_ALLOWED>], + ns: PathNS, def_kind_predicate: impl Fn(DefKind) -> bool, predicate_description: &str, allow_prim_tys: bool, @@ -145,57 +148,47 @@ pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>( FxHashMap::default(); for disallowed_path in disallowed_paths { let path = disallowed_path.path(); - let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::<Vec<_>>()); - - let mut found_def_id = None; - let mut found_prim_ty = false; - resolutions.retain(|res| match res { - Res::Def(def_kind, def_id) => { - found_def_id = Some(*def_id); - def_kind_predicate(*def_kind) - }, - Res::PrimTy(_) => { - found_prim_ty = true; - allow_prim_tys - }, - _ => false, - }); - - if resolutions.is_empty() { - let span = disallowed_path.span(); - - if let Some(def_id) = found_def_id { - tcx.sess.dcx().span_warn( - span, - format!( - "expected a {predicate_description}, found {} {}", - tcx.def_descr_article(def_id), - tcx.def_descr(def_id) - ), - ); + let sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect(); + let mut resolutions = lookup_path(tcx, ns, &sym_path); + resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id))); + + let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice() + && let Some(prim) = PrimTy::from_name(name) + { + (allow_prim_tys.then_some(prim), true) + } else { + (None, false) + }; + + if resolutions.is_empty() + && prim_ty.is_none() + && !disallowed_path.allow_invalid + // Don't warn about unloaded crates: + // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 + && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty()) + { + // Relookup the path in an arbitrary namespace to get a good `expected, found` message + let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path); + let message = if let Some(&def_id) = found_def_ids.first() { + let (article, description) = tcx.article_and_description(def_id); + format!("expected a {predicate_description}, found {article} {description}") } else if found_prim_ty { - tcx.sess.dcx().span_warn( - span, - format!("expected a {predicate_description}, found a primitive type",), - ); - } else if !disallowed_path.allow_invalid { - tcx.sess.dcx().span_warn( - span, - format!("`{path}` does not refer to an existing {predicate_description}"), - ); - } + format!("expected a {predicate_description}, found a primitive type") + } else { + format!("`{path}` does not refer to a reachable {predicate_description}") + }; + tcx.sess + .dcx() + .struct_span_warn(disallowed_path.span(), message) + .with_help("add `allow-invalid = true` to the entry to suppress this warning") + .emit(); } - for res in resolutions { - match res { - Res::Def(_, def_id) => { - def_ids.insert(def_id, (path, disallowed_path)); - }, - Res::PrimTy(ty) => { - prim_tys.insert(ty, (path, disallowed_path)); - }, - _ => unreachable!(), - } + for def_id in resolutions { + def_ids.insert(def_id, (path, disallowed_path)); + } + if let Some(ty) = prim_ty { + prim_tys.insert(ty, (path, disallowed_path)); } } diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 47b7b375861..a963fba7d98 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -5,7 +5,6 @@ version = "0.0.1" edition = "2024" [dependencies] -aho-corasick = "1.0" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } clap = { version = "4.4", features = ["derive"] } indoc = "1.0" diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs new file mode 100644 index 00000000000..3bdc5b27723 --- /dev/null +++ b/clippy_dev/src/deprecate_lint.rs @@ -0,0 +1,161 @@ +use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::utils::{UpdateMode, Version}; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +/// Runs the `deprecate` command +/// +/// This does the following: +/// * Adds an entry to `deprecated_lints.rs`. +/// * Removes the lint declaration (and the entire file if applicable) +/// +/// # Panics +/// +/// If a file path could not read from or written to +pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { + if let Some((prefix, _)) = name.split_once("::") { + panic!("`{name}` should not contain the `{prefix}` prefix"); + } + + let mut lints = find_lint_decls(); + let (mut deprecated_lints, renamed_lints) = read_deprecated_lints(); + + let Some(lint) = lints.iter().find(|l| l.name == name) else { + eprintln!("error: failed to find lint `{name}`"); + return; + }; + + let prefixed_name = String::from_iter(["clippy::", name]); + match deprecated_lints.binary_search_by(|x| x.name.cmp(&prefixed_name)) { + Ok(_) => { + println!("`{name}` is already deprecated"); + return; + }, + Err(idx) => deprecated_lints.insert( + idx, + DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + version: clippy_version.rust_display().to_string(), + }, + ), + } + + let mod_path = { + let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); + if mod_path.is_dir() { + mod_path = mod_path.join("mod"); + } + + mod_path.set_extension("rs"); + mod_path + }; + + if remove_lint_declaration(name, &mod_path, &mut lints).unwrap_or(false) { + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } +} + +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> { + fn remove_lint(name: &str, lints: &mut Vec<Lint>) { + lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); + } + + fn remove_test_assets(name: &str) { + let test_file_stem = format!("tests/ui/{name}"); + let path = Path::new(&test_file_stem); + + // Some lints have their own directories, delete them + if path.is_dir() { + let _ = fs::remove_dir_all(path); + return; + } + + // Remove all related test files + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); + } + + fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { + let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { + content + .find("declare_lint_pass!") + .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) + }); + let mut impl_lint_pass_end = content[impl_lint_pass_start..] + .find(']') + .expect("failed to find `impl_lint_pass` terminator"); + + impl_lint_pass_end += impl_lint_pass_start; + if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { + let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); + for c in content[lint_name_end..impl_lint_pass_end].chars() { + // Remove trailing whitespace + if c == ',' || c.is_whitespace() { + lint_name_end += 1; + } else { + break; + } + } + + content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); + } + } + + if path.exists() + && let Some(lint) = lints.iter().find(|l| l.name == name) + { + if lint.module == name { + // The lint name is the same as the file, we can just delete the entire file + fs::remove_file(path)?; + } else { + // We can't delete the entire file, just remove the declaration + + if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { + // Remove clippy_lints/src/some_mod/some_lint.rs + let mut lint_mod_path = path.to_path_buf(); + lint_mod_path.set_file_name(name); + lint_mod_path.set_extension("rs"); + + let _ = fs::remove_file(lint_mod_path); + } + + let mut content = + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + + eprintln!( + "warn: you will have to manually remove any code related to `{name}` from `{}`", + path.display() + ); + + assert!( + content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + "error: `{}` does not contain lint `{}`'s declaration", + path.display(), + lint.name + ); + + // Remove lint declaration (declare_clippy_lint!) + content.replace_range(lint.declaration_range.clone(), ""); + + // Remove the module declaration (mod xyz;) + let mod_decl = format!("\nmod {name};"); + content = content.replacen(&mod_decl, "", 1); + + remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); + fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); + } + + remove_test_assets(name); + remove_lint(name, lints); + return Ok(true); + } + + Ok(false) +} diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index 05fa24d8d4e..7e9d92458d0 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, exit_if_err}; +use crate::utils::exit_if_err; use std::process::Command; /// # Panics @@ -8,8 +8,7 @@ use std::process::Command; pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { let mut cmd = Command::new("cargo"); - cmd.current_dir(clippy_project_root()) - .args(["test", "--test", "dogfood"]) + cmd.args(["test", "--test", "dogfood"]) .args(["--features", "internal"]) .args(["--", "dogfood_clippy", "--nocapture"]); diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index bdddf46a2cb..b4c13213f55 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -1,4 +1,3 @@ -use crate::utils::clippy_project_root; use itertools::Itertools; use rustc_lexer::{TokenKind, tokenize}; use shell_escape::escape; @@ -104,15 +103,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { Field, } - let path: PathBuf = [ - clippy_project_root().as_path(), - "clippy_config".as_ref(), - "src".as_ref(), - "conf.rs".as_ref(), - ] - .into_iter() - .collect(); - let text = fs::read_to_string(&path)?; + let path = "clippy_config/src/conf.rs"; + let text = fs::read_to_string(path)?; let (pre, conf) = text .split_once("define_Conf! {\n") @@ -203,7 +195,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {}, _ => { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + i), format!("unexpected token `{}`", &conf[i..i + t.len as usize]), )); @@ -213,7 +205,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if !matches!(state, State::Field) { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + conf.len()), "incomplete field".into(), )); @@ -260,18 +252,16 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if check { return Err(Error::CheckFailed); } - fs::write(&path, new_text.as_bytes())?; + fs::write(path, new_text.as_bytes())?; } Ok(()) } fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { - let project_root = clippy_project_root(); - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to // format because rustfmt would also format the entire rustc repo as it is a local // dependency - if fs::read_to_string(project_root.join("Cargo.toml")) + if fs::read_to_string("Cargo.toml") .expect("Failed to read clippy Cargo.toml") .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { @@ -280,12 +270,12 @@ fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { check_for_rustfmt(context)?; - cargo_fmt(context, project_root.as_path())?; - cargo_fmt(context, &project_root.join("clippy_dev"))?; - cargo_fmt(context, &project_root.join("rustc_tools_util"))?; - cargo_fmt(context, &project_root.join("lintcheck"))?; + cargo_fmt(context, ".".as_ref())?; + cargo_fmt(context, "clippy_dev".as_ref())?; + cargo_fmt(context, "rustc_tools_util".as_ref())?; + cargo_fmt(context, "lintcheck".as_ref())?; - let chunks = WalkDir::new(project_root.join("tests")) + let chunks = WalkDir::new("tests") .into_iter() .filter_map(|entry| { let entry = entry.expect("failed to find tests"); diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 9280369c23b..1cfcbdfe855 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,5 +1,11 @@ -#![feature(let_chains)] -#![feature(rustc_private)] +#![feature( + rustc_private, + if_let_guard, + let_chains, + os_str_slice, + os_string_truncate, + slice_split_once +)] #![warn( trivial_casts, trivial_numeric_casts, @@ -13,12 +19,15 @@ #[allow(unused_extern_crates)] extern crate rustc_driver; extern crate rustc_lexer; +extern crate rustc_literal_escaper; +pub mod deprecate_lint; pub mod dogfood; pub mod fmt; pub mod lint; pub mod new_lint; pub mod release; +pub mod rename_lint; pub mod serve; pub mod setup; pub mod sync; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 0380b7e6a6d..5dce0be742b 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -3,11 +3,18 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use clap::{Args, Parser, Subcommand}; -use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils}; +use clippy_dev::{ + deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, utils, +}; use std::convert::Infallible; +use std::env; fn main() { let dev = Dev::parse(); + let clippy = utils::ClippyInfo::search_for_manifest(); + if let Err(e) = env::set_current_dir(&clippy.path) { + panic!("error setting current directory to `{}`: {e}", clippy.path.display()); + } match dev.command { DevCommand::Bless => { @@ -20,22 +27,14 @@ fn main() { allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), - DevCommand::UpdateLints { print_only, check } => { - if print_only { - update_lints::print_lints(); - } else if check { - update_lints::update(utils::UpdateMode::Check); - } else { - update_lints::update(utils::UpdateMode::Change); - } - }, + DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), DevCommand::NewLint { pass, name, category, r#type, msrv, - } => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) { + } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { Ok(()) => update_lints::update(utils::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), }, @@ -79,13 +78,18 @@ fn main() { old_name, new_name, uplift, - } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason), + } => rename_lint::rename( + clippy.version, + &old_name, + new_name.as_ref().unwrap_or(&old_name), + uplift, + ), + DevCommand::Deprecate { name, reason } => deprecate_lint::deprecate(clippy.version, &name, &reason), DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), }, DevCommand::Release(ReleaseCommand { subcommand }) => match subcommand { - ReleaseSubcommand::BumpVersion => release::bump_version(), + ReleaseSubcommand::BumpVersion => release::bump_version(clippy.version), }, } } @@ -136,11 +140,6 @@ enum DevCommand { /// * all lints are registered in the lint store UpdateLints { #[arg(long)] - /// Print a table of lints to STDOUT - /// - /// This does not include deprecated and internal lints. (Does not modify any files) - print_only: bool, - #[arg(long)] /// Checks that `cargo dev update_lints` has been run. Used on CI. check: bool, }, @@ -170,7 +169,6 @@ enum DevCommand { "restriction", "cargo", "nursery", - "internal", ], default_value = "nursery", )] diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 96e12706c9e..4121daa85e6 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, clippy_version}; +use crate::utils::{RustSearcher, Token, Version}; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; use std::fmt::{self, Write as _}; @@ -22,11 +22,11 @@ impl fmt::Display for Pass { } struct LintData<'a> { + clippy_version: Version, pass: Pass, name: &'a str, category: &'a str, ty: Option<&'a str>, - project_root: PathBuf, } trait Context { @@ -50,18 +50,25 @@ impl<T> Context for io::Result<T> { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create(pass: Pass, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { +pub fn create( + clippy_version: Version, + pass: Pass, + name: &str, + category: &str, + mut ty: Option<&str>, + msrv: bool, +) -> io::Result<()> { if category == "cargo" && ty.is_none() { // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` ty = Some("cargo"); } let lint = LintData { + clippy_version, pass, name, category, ty, - project_root: clippy_project_root(), }; create_lint(&lint, msrv).context("Unable to create lint implementation")?; @@ -88,7 +95,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { } else { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); - write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; + write_file(&lint_path, lint_contents.as_bytes())?; println!("Generated lint file: `{lint_path}`"); Ok(()) @@ -115,8 +122,7 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { } if lint.category == "cargo" { - let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); - let test_dir = lint.project_root.join(&relative_test_dir); + let test_dir = format!("tests/ui-cargo/{}", lint.name); fs::create_dir(&test_dir)?; create_project_layout( @@ -134,11 +140,11 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { false, )?; - println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); + println!("Generated test directories: `{test_dir}/pass`, `{test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); let test_contents = get_test_file_contents(lint.name, msrv); - write_file(lint.project_root.join(&test_path), test_contents)?; + write_file(&test_path, test_contents)?; println!("Generated test file: `{test_path}`"); } @@ -193,11 +199,6 @@ fn to_camel_case(name: &str) -> String { .collect() } -pub(crate) fn get_stabilization_version() -> String { - let (minor, patch) = clippy_version(); - format!("{minor}.{patch}.0") -} - fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { let mut test = formatdoc!( r" @@ -292,7 +293,11 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ); } - let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); + let _: fmt::Result = writeln!( + result, + "{}", + get_lint_declaration(lint.clippy_version, &name_upper, category) + ); if enable_msrv { let _: fmt::Result = writedoc!( @@ -330,7 +335,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result } -fn get_lint_declaration(name_upper: &str, category: &str) -> String { +fn get_lint_declaration(version: Version, name_upper: &str, category: &str) -> String { let justification_heading = if category == "restriction" { "Why restrict this?" } else { @@ -355,9 +360,8 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String { pub {name_upper}, {category}, "default lint description" - }} - "#, - get_stabilization_version(), + }}"#, + version.rust_display(), ) } @@ -371,7 +375,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R _ => {}, } - let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}")); + let ty_dir = PathBuf::from(format!("clippy_lints/src/{ty}")); assert!( ty_dir.exists() && ty_dir.is_dir(), "Directory `{}` does not exist!", @@ -441,95 +445,25 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R #[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { - use super::update_lints::{LintDeclSearchResult, match_tokens}; - use rustc_lexer::TokenKind; - let lint_name_upper = lint.name.to_uppercase(); let mut file_contents = fs::read_to_string(path)?; assert!( - !file_contents.contains(&lint_name_upper), + !file_contents.contains(&format!("pub {lint_name_upper},")), "Lint `{}` already defined in `{}`", lint.name, path.display() ); - let mut offset = 0usize; - let mut last_decl_curly_offset = None; - let mut lint_context = None; - - let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &file_contents[range.clone()], - range, - } - }); - - // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl - while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) { - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - - match content { - "declare_clippy_lint" => { - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - if let Some(LintDeclSearchResult { range, .. }) = - iter.find(|result| result.token_kind == TokenKind::CloseBrace) - { - last_decl_curly_offset = Some(range.end); - } - }, - "impl" => { - let mut token = iter.next(); - match token { - // matches <'foo> - Some(LintDeclSearchResult { - token_kind: TokenKind::Lt, - .. - }) => { - match_tokens!(iter, Lifetime { .. } Gt); - token = iter.next(); - }, - None => break, - _ => {}, - } - - if let Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - content, - .. - }) = token - { - // Get the appropriate lint context struct - lint_context = match content { - "LateLintPass" => Some("LateContext"), - "EarlyLintPass" => Some("EarlyContext"), - _ => continue, - }; - } - }, - _ => {}, - } - } - - drop(iter); - - let last_decl_curly_offset = - last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())); - let lint_context = - lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())); + let (lint_context, lint_decl_end) = parse_mod_file(path, &file_contents); // Add the lint declaration to `mod.rs` - file_contents.replace_range( - // Remove the trailing newline, which should always be present - last_decl_curly_offset..=last_decl_curly_offset, - &format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)), + file_contents.insert_str( + lint_decl_end, + &format!( + "\n\n{}", + get_lint_declaration(lint.clippy_version, &lint_name_upper, lint.category) + ), ); // Add the lint to `impl_lint_pass`/`declare_lint_pass` @@ -580,6 +514,41 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> Ok(lint_context) } +// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl +fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { + #[allow(clippy::enum_glob_use)] + use Token::*; + + let mut context = None; + let mut decl_end = None; + let mut searcher = RustSearcher::new(contents); + while let Some(name) = searcher.find_capture_token(CaptureIdent) { + match name { + "declare_clippy_lint" => { + if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) { + decl_end = Some(searcher.pos()); + } + }, + "impl" => { + let mut capture = ""; + if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { + match capture { + "LateLintPass" => context = Some("LateContext"), + "EarlyLintPass" => context = Some("EarlyContext"), + _ => {}, + } + } + }, + _ => {}, + } + } + + ( + context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())), + decl_end.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())) as usize, + ) +} + #[test] fn test_camel_case() { let s = "a_lint"; diff --git a/clippy_dev/src/release.rs b/clippy_dev/src/release.rs index ac755168701..d3b1a7ff320 100644 --- a/clippy_dev/src/release.rs +++ b/clippy_dev/src/release.rs @@ -1,27 +1,27 @@ +use crate::utils::{FileUpdater, Version, update_text_region_fn}; use std::fmt::Write; -use std::path::Path; -use crate::utils::{UpdateMode, clippy_version, replace_region_in_file}; - -const CARGO_TOML_FILES: [&str; 4] = [ +static CARGO_TOML_FILES: &[&str] = &[ "clippy_config/Cargo.toml", "clippy_lints/Cargo.toml", "clippy_utils/Cargo.toml", "Cargo.toml", ]; -pub fn bump_version() { - let (minor, mut patch) = clippy_version(); - patch += 1; - for file in &CARGO_TOML_FILES { - replace_region_in_file( - UpdateMode::Change, - Path::new(file), - "# begin autogenerated version\n", - "# end autogenerated version", - |res| { - writeln!(res, "version = \"0.{minor}.{patch}\"").unwrap(); - }, +pub fn bump_version(mut version: Version) { + version.minor += 1; + + let mut updater = FileUpdater::default(); + for file in CARGO_TOML_FILES { + updater.update_file( + file, + &mut update_text_region_fn( + "# begin autogenerated version\n", + "# end autogenerated version", + |dst| { + writeln!(dst, "version = \"{}\"", version.toml_display()).unwrap(); + }, + ), ); } } diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs new file mode 100644 index 00000000000..be8b27c7a9e --- /dev/null +++ b/clippy_dev/src/rename_lint.rs @@ -0,0 +1,403 @@ +use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::utils::{ + FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, + try_rename_dir, try_rename_file, +}; +use rustc_lexer::TokenKind; +use std::ffi::OsString; +use std::fs; +use std::path::Path; +use walkdir::WalkDir; + +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[expect(clippy::too_many_lines)] +pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{old_name}` should not contain the `{prefix}` prefix"); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{new_name}` should not contain the `{prefix}` prefix"); + } + + let mut updater = FileUpdater::default(); + let mut lints = find_lint_decls(); + let (deprecated_lints, mut renamed_lints) = read_deprecated_lints(); + + let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else { + panic!("could not find lint `{old_name}`"); + }; + let lint = &lints[lint_idx]; + + let old_name_prefixed = String::from_iter(["clippy::", old_name]); + let new_name_prefixed = if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) + }; + + for lint in &mut renamed_lints { + if lint.new_name == old_name_prefixed { + lint.new_name.clone_from(&new_name_prefixed); + } + } + match renamed_lints.binary_search_by(|x| x.old_name.cmp(&old_name_prefixed)) { + Ok(_) => { + println!("`{old_name}` already has a rename registered"); + return; + }, + Err(idx) => { + renamed_lints.insert( + idx, + RenamedLint { + old_name: old_name_prefixed, + new_name: if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) + }, + version: clippy_version.rust_display().to_string(), + }, + ); + }, + } + + // Some tests are named `lint_name_suffix` which should also be renamed, + // but we can't do that if the renamed lint's name overlaps with another + // lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists. + let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); + + let mut mod_edit = ModEdit::None; + if uplift { + let is_unique_mod = lints[..lint_idx].iter().any(|l| l.module == lint.module) + || lints[lint_idx + 1..].iter().any(|l| l.module == lint.module); + if is_unique_mod { + if delete_file_if_exists(lint.path.as_ref()) { + mod_edit = ModEdit::Delete; + } + } else { + updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { + let mut start = &src[..lint.declaration_range.start]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + let mut end = &src[lint.declaration_range.end..]; + if end.starts_with("\n\n") { + end = &end[1..]; + } + dst.push_str(start); + dst.push_str(end); + UpdateStatus::Changed + }); + } + delete_test_files(old_name, change_prefixed_tests); + lints.remove(lint_idx); + } else if lints.binary_search_by(|x| x.name.as_str().cmp(new_name)).is_err() { + let lint = &mut lints[lint_idx]; + if lint.module.ends_with(old_name) + && lint + .path + .file_stem() + .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) + { + let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + new_path.push(".rs"); + if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + mod_edit = ModEdit::Rename; + } + + let mod_len = lint.module.len(); + lint.module.truncate(mod_len - old_name.len()); + lint.module.push_str(new_name); + } + rename_test_files(old_name, new_name, change_prefixed_tests); + new_name.clone_into(&mut lints[lint_idx].name); + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Since `{new_name}` already exists the existing code has not been changed"); + return; + } + + let mut update_fn = file_update_fn(old_name, new_name, mod_edit); + for file in WalkDir::new(".").into_iter().filter_entry(|e| { + // Skip traversing some of the larger directories. + e.path() + .as_os_str() + .as_encoded_bytes() + .get(2..) + .is_none_or(|x| x != "target".as_bytes() && x != ".git".as_bytes()) + }) { + let file = file.expect("error reading clippy directory"); + if file.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { + updater.update_file(file.path(), &mut update_fn); + } + } + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + + if uplift { + println!("Uplifted `clippy::{old_name}` as `{new_name}`"); + if matches!(mod_edit, ModEdit::None) { + println!("Only the rename has been registered, the code will need to be edited manually"); + } else { + println!("All the lint's code has been deleted"); + println!("Make sure to inspect the results as some things may have been missed"); + } + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("All code referencing the old name has been updated"); + println!("Make sure to inspect the results as some things may have been missed"); + } + println!("note: `cargo uibless` still needs to be run to update the test results"); +} + +#[derive(Clone, Copy)] +enum ModEdit { + None, + Delete, + Rename, +} + +fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { + let e = e.expect("error reading `tests/ui`"); + let name = e.file_name(); + if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') { + if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) { + dst.push((name, true)); + } + } else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len()) + { + dst.push((name, false)); + } + } +} + +fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + if rename_prefixed { + for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { + let e = e.expect("error reading `tests/ui-toml`"); + let name = e.file_name(); + if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) { + dst.push((name, false)); + } + } + } else { + dst.push((lint.into(), false)); + } +} + +/// Renames all test files for the given lint. +/// +/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix. +fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut old_buf = OsString::from("tests/ui/"); + let mut new_buf = OsString::from("tests/ui/"); + collect_ui_test_names(old_name, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + if is_file { + try_rename_file(old_buf.as_ref(), new_buf.as_ref()); + } else { + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + } + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } + + tests.clear(); + old_buf.truncate("tests/ui".len()); + new_buf.truncate("tests/ui".len()); + old_buf.push("-toml/"); + new_buf.push("-toml/"); + collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests); + for (name, _) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } +} + +fn delete_test_files(lint: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut buf = OsString::from("tests/ui/"); + collect_ui_test_names(lint, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + buf.push(name); + if is_file { + delete_file_if_exists(buf.as_ref()); + } else { + delete_dir_if_exists(buf.as_ref()); + } + buf.truncate("tests/ui/".len()); + } + + buf.truncate("tests/ui".len()); + buf.push("-toml/"); + + tests.clear(); + collect_ui_toml_test_names(lint, rename_prefixed, &mut tests); + for (name, _) in &tests { + buf.push(name); + delete_dir_if_exists(buf.as_ref()); + buf.truncate("tests/ui/".len()); + } +} + +fn snake_to_pascal(s: &str) -> String { + let mut dst = Vec::with_capacity(s.len()); + let mut iter = s.bytes(); + || -> Option<()> { + dst.push(iter.next()?.to_ascii_uppercase()); + while let Some(c) = iter.next() { + if c == b'_' { + dst.push(iter.next()?.to_ascii_uppercase()); + } else { + dst.push(c); + } + } + Some(()) + }(); + String::from_utf8(dst).unwrap() +} + +#[expect(clippy::too_many_lines)] +fn file_update_fn<'a, 'b>( + old_name: &'a str, + new_name: &'b str, + mod_edit: ModEdit, +) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus { + let old_name_pascal = snake_to_pascal(old_name); + let new_name_pascal = snake_to_pascal(new_name); + let old_name_upper = old_name.to_ascii_uppercase(); + let new_name_upper = new_name.to_ascii_uppercase(); + move |_, src, dst| { + let mut copy_pos = 0u32; + let mut changed = false; + let mut searcher = RustSearcher::new(src); + let mut capture = ""; + loop { + match searcher.peek() { + TokenKind::Eof => break, + TokenKind::Ident => { + let match_start = searcher.pos(); + let text = searcher.peek_text(); + searcher.step(); + match text { + // clippy::line_name or clippy::lint-name + "clippy" => { + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + } + }, + // mod lint_name + "mod" => { + if !matches!(mod_edit, ModEdit::None) + && searcher.match_tokens(&[Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + match mod_edit { + ModEdit::Rename => { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + ModEdit::Delete if searcher.match_tokens(&[Token::Semi], &mut []) => { + let mut start = &src[copy_pos as usize..match_start as usize]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + dst.push_str(start); + copy_pos = searcher.pos(); + if src[copy_pos as usize..].starts_with("\n\n") { + copy_pos += 1; + } + changed = true; + }, + ModEdit::Delete | ModEdit::None => {}, + } + } + }, + // lint_name:: + name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { + let name_end = searcher.pos(); + if searcher.match_tokens(&[Token::DoubleColon], &mut []) { + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(new_name); + copy_pos = name_end; + changed = true; + } + }, + // LINT_NAME or LintName + name => { + let replacement = if name == old_name_upper { + &new_name_upper + } else if name == old_name_pascal { + &new_name_pascal + } else { + continue; + }; + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(replacement); + copy_pos = searcher.pos(); + changed = true; + }, + } + }, + // //~ lint_name + TokenKind::LineComment { doc_style: None } => { + let text = searcher.peek_text(); + if text.starts_with("//~") + && let Some(text) = text.strip_suffix(old_name) + && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize + text.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos() + searcher.peek_len(); + changed = true; + } + searcher.step(); + }, + // ::lint_name + TokenKind::Colon + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name => + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + _ => searcher.step(), + } + } + + dst.push_str(&src[copy_pos as usize..]); + UpdateStatus::from_changed(changed) + } +} diff --git a/clippy_dev/src/sync.rs b/clippy_dev/src/sync.rs index a6b65e561c2..c699b0d7b95 100644 --- a/clippy_dev/src/sync.rs +++ b/clippy_dev/src/sync.rs @@ -1,33 +1,18 @@ -use std::fmt::Write; -use std::path::Path; - +use crate::utils::{FileUpdater, update_text_region_fn}; use chrono::offset::Utc; - -use crate::utils::{UpdateMode, replace_region_in_file}; +use std::fmt::Write; pub fn update_nightly() { - // Update rust-toolchain nightly version let date = Utc::now().format("%Y-%m-%d").to_string(); - replace_region_in_file( - UpdateMode::Change, - Path::new("rust-toolchain.toml"), + let update = &mut update_text_region_fn( "# begin autogenerated nightly\n", "# end autogenerated nightly", - |res| { - writeln!(res, "channel = \"nightly-{date}\"").unwrap(); + |dst| { + writeln!(dst, "channel = \"nightly-{date}\"").unwrap(); }, ); - // Update clippy_utils nightly version - replace_region_in_file( - UpdateMode::Change, - Path::new("clippy_utils/README.md"), - "<!-- begin autogenerated nightly -->\n", - "<!-- end autogenerated nightly -->", - |res| { - writeln!(res, "```").unwrap(); - writeln!(res, "nightly-{date}").unwrap(); - writeln!(res, "```").unwrap(); - }, - ); + let mut updater = FileUpdater::default(); + updater.update_file("rust-toolchain.toml", update); + updater.update_file("clippy_utils/README.md", update); } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 0be24f322d2..8e203ae5142 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,12 +1,9 @@ -use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file}; -use aho_corasick::AhoCorasickBuilder; +use crate::utils::{ + File, FileAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_file, update_text_region_fn, +}; use itertools::Itertools; -use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape}; -use std::collections::{HashMap, HashSet}; -use std::ffi::OsStr; -use std::fmt::{self, Write}; -use std::fs::{self, OpenOptions}; -use std::io::{self, Read, Seek, Write as _}; +use std::collections::HashSet; +use std::fmt::Write; use std::ops::Range; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; @@ -27,866 +24,343 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// /// Panics if a file path could not read from or then written to pub fn update(update_mode: UpdateMode) { - let (lints, deprecated_lints, renamed_lints) = gather_all(); - generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); + let lints = find_lint_decls(); + let (deprecated, renamed) = read_deprecated_lints(); + generate_lint_files(update_mode, &lints, &deprecated, &renamed); } -fn generate_lint_files( +#[expect(clippy::too_many_lines)] +pub fn generate_lint_files( update_mode: UpdateMode, lints: &[Lint], - deprecated_lints: &[DeprecatedLint], - renamed_lints: &[RenamedLint], + deprecated: &[DeprecatedLint], + renamed: &[RenamedLint], ) { - let internal_lints = Lint::internal_lints(lints); - let mut usable_lints = Lint::usable_lints(lints); - usable_lints.sort_by_key(|lint| lint.name.clone()); - - replace_region_in_file( - update_mode, - Path::new("README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); - }, - ); - - replace_region_in_file( - update_mode, - Path::new("book/src/README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); - }, - ); - - replace_region_in_file( - update_mode, - Path::new("CHANGELOG.md"), - "<!-- begin autogenerated links to lint list -->\n", - "<!-- end autogenerated links to lint list -->", - |res| { - for lint in usable_lints - .iter() - .map(|l| &*l.name) - .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ); - - // This has to be in lib.rs, otherwise rustfmt doesn't work - replace_region_in_file( - update_mode, - Path::new("clippy_lints/src/lib.rs"), - "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", - "// end lints modules, do not remove this comment, it’s used in `update_lints`", - |res| { - for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { - writeln!(res, "mod {lint_mod};").unwrap(); - } - }, - ); - - process_file( - "clippy_lints/src/declared_lints.rs", + FileUpdater::default().update_files_checked( + "cargo dev update_lints", update_mode, - &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), - ); - - let content = gen_deprecated_lints_test(deprecated_lints); - process_file("tests/ui/deprecated.rs", update_mode, &content); - - let content = gen_renamed_lints_test(renamed_lints); - process_file("tests/ui/rename.rs", update_mode, &content); -} - -pub fn print_lints() { - let (lint_list, _, _) = gather_all(); - let usable_lints = Lint::usable_lints(&lint_list); - let usable_lint_count = usable_lints.len(); - let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); - - for (lint_group, mut lints) in grouped_by_lint_group { - println!("\n## {lint_group}"); - - lints.sort_by_key(|l| l.name.clone()); - - for lint in lints { - println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc); - } - } - - println!("there are {usable_lint_count} lints"); -} - -/// Runs the `rename_lint` command. -/// -/// This does the following: -/// * Adds an entry to `renamed_lints.rs`. -/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). -/// * Renames the lint struct to the new name. -/// * Renames the module containing the lint struct to the new name if it shares a name with the -/// lint. -/// -/// # Panics -/// Panics for the following conditions: -/// * If a file path could not read from or then written to -/// * If either lint name has a prefix -/// * If `old_name` doesn't name an existing lint. -/// * If `old_name` names a deprecated or renamed lint. -#[allow(clippy::too_many_lines)] -pub fn rename(old_name: &str, new_name: &str, uplift: bool) { - if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{old_name}` should not contain the `{prefix}` prefix"); - } - if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{new_name}` should not contain the `{prefix}` prefix"); - } - - let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); - let mut old_lint_index = None; - let mut found_new_name = false; - for (i, lint) in lints.iter().enumerate() { - if lint.name == old_name { - old_lint_index = Some(i); - } else if lint.name == new_name { - found_new_name = true; - } - } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); - - let lint = RenamedLint { - old_name: format!("clippy::{old_name}"), - new_name: if uplift { - new_name.into() - } else { - format!("clippy::{new_name}") - }, - }; - - // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in - // case. - assert!( - !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{old_name}` has already been renamed" - ); - assert!( - !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{old_name}` has already been deprecated" - ); - - // Update all lint level attributes. (`clippy::lint_name`) - for file in WalkDir::new(clippy_project_root()) - .into_iter() - .map(Result::unwrap) - .filter(|f| { - let name = f.path().file_name(); - let ext = f.path().extension(); - (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) - && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("deprecated_lints.rs")) - }) - { - rewrite_file(file.path(), |s| { - replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) - }); - } - - let version = crate::new_lint::get_stabilization_version(); - rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { - insert_at_marker( - s, - "// end renamed lints. used by `cargo dev rename_lint`", - &format!( - "#[clippy::version = \"{version}\"]\n \ - (\"{}\", \"{}\"),\n ", - lint.old_name, lint.new_name, + &mut [ + ( + "README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), ), - ) - }); - - renamed_lints.push(lint); - renamed_lints.sort_by(|lhs, rhs| { - lhs.new_name - .starts_with("clippy::") - .cmp(&rhs.new_name.starts_with("clippy::")) - .reverse() - .then_with(|| lhs.old_name.cmp(&rhs.old_name)) - }); - - if uplift { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." - ); - } else if found_new_name { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." - ); - } else { - // Rename the lint struct and source files sharing a name with the lint. - let lint = &mut lints[old_lint_index]; - let old_name_upper = old_name.to_uppercase(); - let new_name_upper = new_name.to_uppercase(); - lint.name = new_name.into(); - - // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. - if try_rename_file( - Path::new(&format!("tests/ui/{old_name}.rs")), - Path::new(&format!("tests/ui/{new_name}.rs")), - ) { - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.stderr")), - Path::new(&format!("tests/ui/{new_name}.stderr")), - ); - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.fixed")), - Path::new(&format!("tests/ui/{new_name}.fixed")), - ); - } - - // Try to rename the file containing the lint if the file name matches the lint's name. - let replacements; - let replacements = if lint.module == old_name - && try_rename_file( - Path::new(&format!("clippy_lints/src/{old_name}.rs")), - Path::new(&format!("clippy_lints/src/{new_name}.rs")), - ) { - // Edit the module name in the lint list. Note there could be multiple lints. - for lint in lints.iter_mut().filter(|l| l.module == old_name) { - lint.module = new_name.into(); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else if !lint.module.contains("::") - // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` - && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), - Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), - ) - { - // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{old_name}", lint.module); - for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{new_name}", lint.module); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else { - replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; - &replacements[0..1] - }; - - // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being - // renamed. - for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs")) - { - rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); - } - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{old_name} has been successfully renamed"); - } - - println!("note: `cargo uitest` still needs to be run to update the test results"); -} - -/// Runs the `deprecate` command -/// -/// This does the following: -/// * Adds an entry to `deprecated_lints.rs`. -/// * Removes the lint declaration (and the entire file if applicable) -/// -/// # Panics -/// -/// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: &str) { - let prefixed_name = if name.starts_with("clippy::") { - name.to_owned() - } else { - format!("clippy::{name}") - }; - let stripped_name = &prefixed_name[8..]; - - let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { - eprintln!("error: failed to find lint `{name}`"); - return; - }; - - let mod_path = { - let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); - if mod_path.is_dir() { - mod_path = mod_path.join("mod"); - } - - mod_path.set_extension("rs"); - mod_path - }; - - let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs"); - - if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - let version = crate::new_lint::get_stabilization_version(); - rewrite_file(deprecated_lints_path, |s| { - insert_at_marker( - s, - "// end deprecated lints. used by `cargo dev deprecate_lint`", - &format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",), - ) - }); - - deprecated_lints.push(DeprecatedLint { - name: prefixed_name, - reason: reason.into(), - }); - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - println!("note: you must run `cargo uitest` to update the test results"); - } else { - eprintln!("error: lint not found"); - } -} - -fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> { - fn remove_lint(name: &str, lints: &mut Vec<Lint>) { - lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); - } - - fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{name}"); - let path = Path::new(&test_file_stem); - - // Some lints have their own directories, delete them - if path.is_dir() { - let _ = fs::remove_dir_all(path); - return; - } - - // Remove all related test files - let _ = fs::remove_file(path.with_extension("rs")); - let _ = fs::remove_file(path.with_extension("stderr")); - let _ = fs::remove_file(path.with_extension("fixed")); - } - - fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { - let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { - content - .find("declare_lint_pass!") - .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) - }); - let mut impl_lint_pass_end = content[impl_lint_pass_start..] - .find(']') - .expect("failed to find `impl_lint_pass` terminator"); - - impl_lint_pass_end += impl_lint_pass_start; - if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { - let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); - for c in content[lint_name_end..impl_lint_pass_end].chars() { - // Remove trailing whitespace - if c == ',' || c.is_whitespace() { - lint_name_end += 1; - } else { - break; + ( + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), + ), + ( + "CHANGELOG.md", + &mut update_text_region_fn( + "<!-- begin autogenerated links to lint list -->\n", + "<!-- end autogenerated links to lint list -->", + |dst| { + for lint in lints + .iter() + .map(|l| &*l.name) + .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) + .sorted() + { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); + } + }, + ), + ), + ( + "clippy_lints/src/lib.rs", + &mut update_text_region_fn( + "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", + "// end lints modules, do not remove this comment, it’s used in `update_lints`", + |dst| { + for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() { + writeln!(dst, "mod {lint_mod};").unwrap(); + } + }, + ), + ), + ("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); + for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { + writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap(); } - } - - content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); - } - } - - if path.exists() - && let Some(lint) = lints.iter().find(|l| l.name == name) - { - if lint.module == name { - // The lint name is the same as the file, we can just delete the entire file - fs::remove_file(path)?; - } else { - // We can't delete the entire file, just remove the declaration - - if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { - // Remove clippy_lints/src/some_mod/some_lint.rs - let mut lint_mod_path = path.to_path_buf(); - lint_mod_path.set_file_name(name); - lint_mod_path.set_extension("rs"); - - let _ = fs::remove_file(lint_mod_path); - } - - let mut content = - fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); - - eprintln!( - "warn: you will have to manually remove any code related to `{name}` from `{}`", - path.display() - ); - - assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), - "error: `{}` does not contain lint `{}`'s declaration", - path.display(), - lint.name - ); - - // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range.clone(), ""); - - // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {name};"); - content = content.replacen(&mod_decl, "", 1); - - remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); - fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); - } - - remove_test_assets(name); - remove_lint(name, lints); - return Ok(true); - } - - Ok(false) -} - -/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there -/// were no replacements. -fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> { - fn is_ident_char(c: u8) -> bool { - matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') - } - - let searcher = AhoCorasickBuilder::new() - .match_kind(aho_corasick::MatchKind::LeftmostLongest) - .build(replacements.iter().map(|&(x, _)| x.as_bytes())) - .unwrap(); - - let mut result = String::with_capacity(contents.len() + 1024); - let mut pos = 0; - let mut edited = false; - for m in searcher.find_iter(contents) { - let (old, new) = replacements[m.pattern()]; - result.push_str(&contents[pos..m.start()]); - result.push_str( - if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - edited = true; - new - } else { - old - }, - ); - pos = m.end(); - } - result.push_str(&contents[pos..]); - edited.then_some(result) + dst.push_str("];\n"); + UpdateStatus::from_changed(src != dst) + }), + ("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| { + let mut searcher = RustSearcher::new(src); + assert!( + searcher.find_token(Token::Ident("declare_with_version")) + && searcher.find_token(Token::Ident("declare_with_version")), + "error reading deprecated lints" + ); + dst.push_str(&src[..searcher.pos() as usize]); + dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); + for lint in deprecated { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.name, lint.reason, + ) + .unwrap(); + } + dst.push_str( + "]}\n\n\ + #[rustfmt::skip]\n\ + declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ + ", + ); + for lint in renamed { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.old_name, lint.new_name, + ) + .unwrap(); + } + dst.push_str("]}\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/deprecated.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + for lint in deprecated { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/rename.rs", &mut move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for lint in renamed { + if seen_lints.insert(&lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in renamed { + if seen_lints.insert(&lint.old_name) { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + } + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), + ], + ); } fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } -fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) { - if update_mode == UpdateMode::Check { - let old_content = - fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display())); - if content != old_content { - exit_with_failure(); - } - } else { - fs::write(&path, content.as_bytes()) - .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display())); - } -} - /// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Eq, Debug)] -struct Lint { - name: String, - group: String, - desc: String, - module: String, - declaration_range: Range<usize>, -} - -impl Lint { - #[must_use] - fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self { - Self { - name: name.to_lowercase(), - group: group.into(), - desc: remove_line_splices(desc), - module: module.into(), - declaration_range, - } - } - - /// Returns all non-deprecated lints and non-internal lints - #[must_use] - fn usable_lints(lints: &[Self]) -> Vec<Self> { - lints - .iter() - .filter(|l| !l.group.starts_with("internal")) - .cloned() - .collect() - } - - /// Returns all internal lints - #[must_use] - fn internal_lints(lints: &[Self]) -> Vec<Self> { - lints.iter().filter(|l| l.group == "internal").cloned().collect() - } - - /// Returns the lints in a `HashMap`, grouped by the different lint groups - #[must_use] - fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> { - lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() - } +#[derive(PartialEq, Eq, Debug)] +pub struct Lint { + pub name: String, + pub group: String, + pub module: String, + pub path: PathBuf, + pub declaration_range: Range<usize>, } -#[derive(Clone, PartialEq, Eq, Debug)] -struct DeprecatedLint { - name: String, - reason: String, -} -impl DeprecatedLint { - fn new(name: &str, reason: &str) -> Self { - Self { - name: remove_line_splices(name), - reason: remove_line_splices(reason), - } - } +pub struct DeprecatedLint { + pub name: String, + pub reason: String, + pub version: String, } -struct RenamedLint { - old_name: String, - new_name: String, -} -impl RenamedLint { - fn new(old_name: &str, new_name: &str) -> Self { - Self { - old_name: remove_line_splices(old_name), - new_name: remove_line_splices(new_name), - } - } +pub struct RenamedLint { + pub old_name: String, + pub new_name: String, + pub version: String, } -/// Generates the code for registering lints +/// Finds all lint declarations (`declare_clippy_lint!`) #[must_use] -fn gen_declared_lints<'a>( - internal_lints: impl Iterator<Item = &'a Lint>, - usable_lints: impl Iterator<Item = &'a Lint>, -) -> String { - let mut details: Vec<_> = internal_lints - .map(|l| (false, &l.module, l.name.to_uppercase())) - .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase()))) - .collect(); - details.sort_unstable(); - - let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); - - for (is_public, module_name, lint_name) in details { - if !is_public { - output.push_str(" #[cfg(feature = \"internal\")]\n"); - } - let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); - } - output.push_str("];\n"); - - output -} - -fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { - let mut res: String = GENERATED_FILE_COMMENT.into(); - for lint in lints { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); - } - res.push_str("\nfn main() {}\n"); - res -} - -fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { - let mut seen_lints = HashSet::new(); - let mut res: String = GENERATED_FILE_COMMENT.into(); - - res.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in lints { - if seen_lints.insert(&lint.new_name) { - writeln!(res, "#![allow({})]", lint.new_name).unwrap(); - } - } - seen_lints.clear(); - for lint in lints { - if seen_lints.insert(&lint.old_name) { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); - } +pub fn find_lint_decls() -> Vec<Lint> { + let mut lints = Vec::with_capacity(1000); + let mut contents = String::new(); + for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { + parse_clippy_lint_decls( + file.path(), + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); } - res.push_str("\nfn main() {}\n"); - res + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints } -/// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) { - let mut lints = Vec::with_capacity(1000); - let mut deprecated_lints = Vec::with_capacity(50); - let mut renamed_lints = Vec::with_capacity(50); - - for (rel_path, file) in clippy_lints_src_files() { - let path = file.path(); - let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::<Vec<_>>() - .join("::"); - - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - let module = if let Some(module) = module.strip_suffix("::mod.rs") { - module - } else { - module.strip_suffix(".rs").unwrap_or(&module) +/// Reads the source files from the given root directory +fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirEntry, String)> { + WalkDir::new(src_root).into_iter().filter_map(move |e| { + let e = match e { + Ok(e) => e, + Err(ref e) => panic_file(e, FileAction::Read, src_root), }; - - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); + let path = e.path().as_os_str().as_encoded_bytes(); + if let Some(path) = path.strip_suffix(b".rs") + && let Some(path) = path.get("clippy_lints/src/".len()..) + { + if path == b"lib" { + Some((e, String::new())) + } else { + let path = if let Some(path) = path.strip_suffix(b"mod") + && let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\")) + { + path + } else { + path + }; + if let Ok(path) = str::from_utf8(path) { + let path = path.replace(['/', '\\'], "::"); + Some((e, path)) + } else { + None + } + } } else { - parse_contents(&contents, module, &mut lints); + None } - } - (lints, deprecated_lints, renamed_lints) -} - -fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> { - let root_path = clippy_project_root().join("clippy_lints/src"); - let iter = WalkDir::new(&root_path).into_iter(); - iter.map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) + }) } -macro_rules! match_tokens { - ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { - { - $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult { - token_kind: TokenKind::$token $({$($fields)*})?, - content: $($capture @)? _, - .. - }) = $iter.next() else { - continue; - };)* - #[allow(clippy::unused_unit)] - { ($($($capture,)?)*) } +/// Parse a source file looking for `declare_clippy_lint` macro invocations. +fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec<Lint>) { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token<'_>] = &[ + // !{ /// docs + Bang, OpenBrace, AnyComment, + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // pub NAME, GROUP, + Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, + ]; + + let mut searcher = RustSearcher::new(contents); + while searcher.find_token(Ident("declare_clippy_lint")) { + let start = searcher.pos() as usize - "declare_clippy_lint".len(); + let (mut name, mut group) = ("", ""); + if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) { + lints.push(Lint { + name: name.to_lowercase(), + group: group.into(), + module: module.into(), + path: path.into(), + declaration_range: start..searcher.pos() as usize, + }); } } } -pub(crate) use match_tokens; - -pub(crate) struct LintDeclSearchResult<'a> { - pub token_kind: TokenKind, - pub content: &'a str, - pub range: Range<usize>, -} - -/// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { - let mut offset = 0usize; - let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &contents[range.clone()], - range, - } - }); +#[must_use] +pub fn read_deprecated_lints() -> (Vec<DeprecatedLint>, Vec<RenamedLint>) { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token<'_>] = &[ + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, + // ("first", "second"), + OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, + ]; + #[rustfmt::skip] + static DEPRECATED_TOKENS: &[Token<'_>] = &[ + // !{ DEPRECATED(DEPRECATED_VERSION) = [ + Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + #[rustfmt::skip] + static RENAMED_TOKENS: &[Token<'_>] = &[ + // !{ RENAMED(RENAMED_VERSION) = [ + Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + + let path = "clippy_lints/src/deprecated_lints.rs"; + let mut deprecated = Vec::with_capacity(30); + let mut renamed = Vec::with_capacity(80); + let mut contents = String::new(); + File::open_read_to_cleared_string(path, &mut contents); + + let mut searcher = RustSearcher::new(&contents); + + // First instance is the macro definition. + assert!( + searcher.find_token(Ident("declare_with_version")), + "error reading deprecated lints" + ); - while let Some(LintDeclSearchResult { range, .. }) = iter.find( - |LintDeclSearchResult { - token_kind, content, .. - }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint", - ) { - let start = range.start; - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - match iter.next() { - // #[clippy::version = "version"] pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Pound, - .. - }) => { - match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); - }, - // pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - .. - }) => (), - _ => continue, + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + let mut version = ""; + let mut name = ""; + let mut reason = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { + deprecated.push(DeprecatedLint { + name: parse_str_single_line(path.as_ref(), name), + reason: parse_str_single_line(path.as_ref(), reason), + version: parse_str_single_line(path.as_ref(), version), + }); } - - let (name, group, desc) = match_tokens!( - iter, - // LINT_NAME - Ident(name) Comma - // group, - Ident(group) Comma - // "description" - Literal{..}(desc) - ); - - 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)); + } else { + panic!("error reading deprecated lints"); + } + + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + let mut version = ""; + let mut old_name = ""; + let mut new_name = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { + renamed.push(RenamedLint { + old_name: parse_str_single_line(path.as_ref(), old_name), + new_name: parse_str_single_line(path.as_ref(), new_name), + version: parse_str_single_line(path.as_ref(), version), + }); } + } else { + panic!("error reading renamed lints"); } -} - -/// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint>, renamed: &mut Vec<RenamedLint>) { - let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { - return; - }; - let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { - return; - }; - - for line in deprecated_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); - - let (name, reason) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma - ); - deprecated.push(DeprecatedLint::new(name, reason)); - } - for line in renamed_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); - let (old_name, new_name) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma - ); - renamed.push(RenamedLint::new(old_name, new_name)); - } + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); + (deprecated, renamed) } /// Removes the line splices and surrounding quotes from a string literal -fn remove_line_splices(s: &str) -> String { +fn parse_str_lit(s: &str) -> String { + let (s, mode) = if let Some(s) = s.strip_prefix("r") { + (s.trim_matches('#'), rustc_literal_escaper::Mode::RawStr) + } else { + (s, rustc_literal_escaper::Mode::Str) + }; let s = s - .strip_prefix('r') - .unwrap_or(s) - .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); - unescape::unescape_unicode(s, unescape::Mode::Str, &mut |range, ch| { - if ch.is_ok() { - res.push_str(&s[range]); + rustc_literal_escaper::unescape_unicode(s, mode, &mut |_, ch| { + if let Ok(ch) = ch { + res.push(ch); } }); res } -fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { - match OpenOptions::new().create_new(true).write(true).open(new_name) { - Ok(file) => drop(file), - Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_file(e, new_name, "create"), - } - match fs::rename(old_name, new_name) { - Ok(()) => true, - Err(e) => { - drop(fs::remove_file(new_name)); - if e.kind() == io::ErrorKind::NotFound { - false - } else { - panic_file(e, old_name, "rename"); - } - }, - } -} - -#[allow(clippy::needless_pass_by_value)] -fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { - panic!("failed to {action} file `{}`: {error}", name.display()) -} - -fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option<String> { - let i = text.find(marker)?; - let (pre, post) = text.split_at(i); - Some([pre, new_text, post].into_iter().collect()) -} - -fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) { - let mut file = OpenOptions::new() - .write(true) - .read(true) - .open(path) - .unwrap_or_else(|e| panic_file(e, path, "open")); - let mut buf = String::new(); - file.read_to_string(&mut buf) - .unwrap_or_else(|e| panic_file(e, path, "read")); - if let Some(new_contents) = f(&buf) { - file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); - file.write_all(new_contents.as_bytes()) - .unwrap_or_else(|e| panic_file(e, path, "write")); - file.set_len(new_contents.len() as u64) - .unwrap_or_else(|e| panic_file(e, path, "write")); - } -} -fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); +fn parse_str_single_line(path: &Path, s: &str) -> String { + let value = parse_str_lit(s); + assert!( + !value.contains('\n'), + "error parsing `{}`: `{s}` should be a single line string", + path.display(), + ); + value } #[cfg(test)] @@ -894,7 +368,7 @@ mod tests { use super::*; #[test] - fn test_parse_contents() { + fn test_parse_clippy_lint_decls() { static CONTENTS: &str = r#" declare_clippy_lint! { #[clippy::version = "Hello Clippy!"] @@ -912,96 +386,27 @@ mod tests { } "#; let mut result = Vec::new(); - parse_contents(CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } let expected = vec![ - Lint::new( - "ptr_arg", - "style", - "\"really long text\"", - "module_name", - Range::default(), - ), - Lint::new( - "doc_markdown", - "pedantic", - "\"single line\"", - "module_name", - Range::default(), - ), + Lint { + name: "ptr_arg".into(), + group: "style".into(), + module: "module_name".into(), + path: PathBuf::new(), + declaration_range: Range::default(), + }, + Lint { + name: "doc_markdown".into(), + group: "pedantic".into(), + module: "module_name".into(), + path: PathBuf::new(), + declaration_range: Range::default(), + }, ]; assert_eq!(expected, result); } - - #[test] - fn test_usable_lints() { - let lints = vec![ - Lint::new( - "should_assert_eq2", - "Not Deprecated", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new( - "should_assert_eq2", - "internal", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new( - "should_assert_eq2", - "internal_style", - "\"abc\"", - "module_name", - Range::default(), - ), - ]; - let expected = vec![Lint::new( - "should_assert_eq2", - "Not Deprecated", - "\"abc\"", - "module_name", - Range::default(), - )]; - assert_eq!(expected, Lint::usable_lints(&lints)); - } - - #[test] - fn test_by_lint_group() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ]; - let mut expected: HashMap<String, Vec<Lint>> = HashMap::new(); - expected.insert( - "group1".to_string(), - vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ], - ); - expected.insert( - "group2".to_string(), - vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )], - ); - assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); - } } diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 206816398f5..fb2e25e655d 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,12 +1,114 @@ +use core::fmt::{self, Display}; +use core::slice; +use core::str::FromStr; +use rustc_lexer::{self as lexer, FrontmatterAllowed}; +use std::env; +use std::fs::{self, OpenOptions}; +use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::process::{self, ExitStatus}; -use std::{fs, io}; #[cfg(not(windows))] static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; #[cfg(windows)] static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; +#[derive(Clone, Copy)] +pub enum FileAction { + Open, + Read, + Write, + Create, + Rename, + Delete, +} +impl FileAction { + fn as_str(self) -> &'static str { + match self { + Self::Open => "opening", + Self::Read => "reading", + Self::Write => "writing", + Self::Create => "creating", + Self::Rename => "renaming", + Self::Delete => "deleting", + } + } +} + +#[cold] +#[track_caller] +pub fn panic_file(err: &impl Display, action: FileAction, path: &Path) -> ! { + panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) +} + +/// Wrapper around `std::fs::File` which panics with a path on failure. +pub struct File<'a> { + pub inner: fs::File, + pub path: &'a Path, +} +impl<'a> File<'a> { + /// Opens a file panicking on failure. + #[track_caller] + pub fn open(path: &'a (impl AsRef<Path> + ?Sized), options: &mut OpenOptions) -> Self { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Self { inner, path }, + Err(e) => panic_file(&e, FileAction::Open, path), + } + } + + /// Opens a file if it exists, panicking on any other failure. + #[track_caller] + pub fn open_if_exists(path: &'a (impl AsRef<Path> + ?Sized), options: &mut OpenOptions) -> Option<Self> { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Some(Self { inner, path }), + Err(e) if e.kind() == io::ErrorKind::NotFound => None, + Err(e) => panic_file(&e, FileAction::Open, path), + } + } + + /// Opens and reads a file into a string, panicking of failure. + #[track_caller] + pub fn open_read_to_cleared_string<'dst>( + path: &'a (impl AsRef<Path> + ?Sized), + dst: &'dst mut String, + ) -> &'dst mut String { + Self::open(path, OpenOptions::new().read(true)).read_to_cleared_string(dst) + } + + /// Read the entire contents of a file to the given buffer. + #[track_caller] + pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + match self.inner.read_to_string(dst) { + Ok(_) => {}, + Err(e) => panic_file(&e, FileAction::Read, self.path), + } + dst + } + + #[track_caller] + pub fn read_to_cleared_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + dst.clear(); + self.read_append_to_string(dst) + } + + /// Replaces the entire contents of a file. + #[track_caller] + pub fn replace_contents(&mut self, data: &[u8]) { + let res = match self.inner.seek(SeekFrom::Start(0)) { + Ok(_) => match self.inner.write_all(data) { + Ok(()) => self.inner.set_len(data.len() as u64), + Err(e) => Err(e), + }, + Err(e) => Err(e), + }; + if let Err(e) = res { + panic_file(&e, FileAction::Write, self.path); + } + } +} + /// Returns the path to the `cargo-clippy` binary /// /// # Panics @@ -14,34 +116,107 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; /// Panics if the path of current executable could not be retrieved. #[must_use] pub fn cargo_clippy_path() -> PathBuf { - let mut path = std::env::current_exe().expect("failed to get current executable name"); + let mut path = env::current_exe().expect("failed to get current executable name"); path.set_file_name(CARGO_CLIPPY_EXE); path } -/// Returns the path to the Clippy project directory -/// -/// # Panics -/// -/// Panics if the current directory could not be retrieved, there was an error reading any of the -/// Cargo.toml files or ancestor directory is the clippy root directory -#[must_use] -pub fn clippy_project_root() -> PathBuf { - let current_dir = std::env::current_dir().unwrap(); - for path in current_dir.ancestors() { - let result = fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result - && err.kind() == io::ErrorKind::NotFound +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + pub major: u16, + pub minor: u16, +} +impl FromStr for Version { + type Err = (); + fn from_str(s: &str) -> Result<Self, Self::Err> { + if let Some(s) = s.strip_prefix("0.") + && let Some((major, minor)) = s.split_once('.') + && let Ok(major) = major.parse() + && let Ok(minor) = minor.parse() { - continue; + Ok(Self { major, minor }) + } else { + Err(()) + } + } +} +impl Version { + /// Displays the version as a rust version. i.e. `x.y.0` + #[must_use] + pub fn rust_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}.0", self.0.major, self.0.minor) + } + } + X(self) + } + + /// Displays the version as it should appear in clippy's toml files. i.e. `0.x.y` + #[must_use] + pub fn toml_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0.{}.{}", self.0.major, self.0.minor) + } } + X(self) + } +} + +pub struct ClippyInfo { + pub path: PathBuf, + pub version: Version, +} +impl ClippyInfo { + #[must_use] + pub fn search_for_manifest() -> Self { + let mut path = env::current_dir().expect("error reading the working directory"); + let mut buf = String::new(); + loop { + path.push("Cargo.toml"); + if let Some(mut file) = File::open_if_exists(&path, OpenOptions::new().read(true)) { + let mut in_package = false; + let mut is_clippy = false; + let mut version: Option<Version> = None; + + // Ad-hoc parsing to avoid dependencies. We control all the file so this + // isn't actually a problem + for line in file.read_to_cleared_string(&mut buf).lines() { + if line.starts_with('[') { + in_package = line.starts_with("[package]"); + } else if in_package && let Some((name, value)) = line.split_once('=') { + match name.trim() { + "name" => is_clippy = value.trim() == "\"clippy\"", + "version" + if let Some(value) = value.trim().strip_prefix('"') + && let Some(value) = value.strip_suffix('"') => + { + version = value.parse().ok(); + }, + _ => {}, + } + } + } - let content = result.unwrap(); - if content.contains("[package]\nname = \"clippy\"") { - return path.to_path_buf(); + if is_clippy { + let Some(version) = version else { + panic!("error reading clippy version from {}", file.path.display()); + }; + path.pop(); + return ClippyInfo { path, version }; + } + } + + path.pop(); + assert!( + path.pop(), + "error finding project root, please run from inside the clippy directory" + ); } } - panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); } /// # Panics @@ -57,86 +232,381 @@ pub fn exit_if_err(status: io::Result<ExitStatus>) { } } -pub(crate) fn clippy_version() -> (u32, u32) { - fn parse_manifest(contents: &str) -> Option<(u32, u32)> { - let version = contents - .lines() - .filter_map(|l| l.split_once('=')) - .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?; - let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else { - return None; - }; - let (minor, patch) = version.split_once('.')?; - Some((minor.parse().ok()?, patch.parse().ok()?)) +#[derive(Clone, Copy)] +pub enum UpdateStatus { + Unchanged, + Changed, +} +impl UpdateStatus { + #[must_use] + pub fn from_changed(value: bool) -> Self { + if value { Self::Changed } else { Self::Unchanged } + } + + #[must_use] + pub fn is_changed(self) -> bool { + matches!(self, Self::Changed) } - let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`"); - parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy)] pub enum UpdateMode { - Check, Change, + Check, +} +impl UpdateMode { + #[must_use] + pub fn from_check(check: bool) -> Self { + if check { Self::Check } else { Self::Change } + } } -pub(crate) fn exit_with_failure() { - println!( - "Not all lints defined properly. \ - Please run `cargo dev update_lints` to make sure all lints are defined properly." - ); - process::exit(1); +#[derive(Default)] +pub struct FileUpdater { + src_buf: String, + dst_buf: String, } +impl FileUpdater { + fn update_file_checked_inner( + &mut self, + tool: &str, + mode: UpdateMode, + path: &Path, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + match (mode, update(path, &self.src_buf, &mut self.dst_buf)) { + (UpdateMode::Check, UpdateStatus::Changed) => { + eprintln!( + "the contents of `{}` are out of date\nplease run `{tool}` to update", + path.display() + ); + process::exit(1); + }, + (UpdateMode::Change, UpdateStatus::Changed) => file.replace_contents(self.dst_buf.as_bytes()), + (UpdateMode::Check | UpdateMode::Change, UpdateStatus::Unchanged) => {}, + } + } -/// Replaces a region in a file delimited by two lines matching regexes. -/// -/// `path` is the relative path to the file on which you want to perform the replacement. -/// -/// See `replace_region_in_text` for documentation of the other options. -/// -/// # Panics -/// -/// Panics if the path could not read or then written -pub(crate) fn replace_region_in_file( - update_mode: UpdateMode, + fn update_file_inner(&mut self, path: &Path, update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + if update(path, &self.src_buf, &mut self.dst_buf).is_changed() { + file.replace_contents(self.dst_buf.as_bytes()); + } + } + + pub fn update_file_checked( + &mut self, + tool: &str, + mode: UpdateMode, + path: impl AsRef<Path>, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + + #[expect(clippy::type_complexity)] + pub fn update_files_checked( + &mut self, + tool: &str, + mode: UpdateMode, + files: &mut [( + impl AsRef<Path>, + &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + )], + ) { + for (path, update) in files { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + } + + pub fn update_file( + &mut self, + path: impl AsRef<Path>, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_inner(path.as_ref(), update); + } +} + +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters +/// were found, or the missing delimiter if not. +pub fn update_text_region( path: &Path, start: &str, end: &str, - write_replacement: impl FnMut(&mut String), -) { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { - Ok(x) => x, - Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()), + src: &str, + dst: &mut String, + insert: &mut impl FnMut(&mut String), +) -> UpdateStatus { + let Some((src_start, src_end)) = src.split_once(start) else { + panic!("`{}` does not contain `{start}`", path.display()); }; + let Some((replaced_text, src_end)) = src_end.split_once(end) else { + panic!("`{}` does not contain `{end}`", path.display()); + }; + dst.push_str(src_start); + dst.push_str(start); + let new_start = dst.len(); + insert(dst); + let changed = dst[new_start..] != *replaced_text; + dst.push_str(end); + dst.push_str(src_end); + UpdateStatus::from_changed(changed) +} + +pub fn update_text_region_fn( + start: &str, + end: &str, + mut insert: impl FnMut(&mut String), +) -> impl FnMut(&Path, &str, &mut String) -> UpdateStatus { + move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) +} + +#[derive(Clone, Copy)] +pub enum Token<'a> { + /// Matches any number of comments / doc comments. + AnyComment, + Ident(&'a str), + CaptureIdent, + LitStr, + CaptureLitStr, + Bang, + CloseBrace, + CloseBracket, + CloseParen, + /// This will consume the first colon even if the second doesn't exist. + DoubleColon, + Comma, + Eq, + Lifetime, + Lt, + Gt, + OpenBrace, + OpenBracket, + OpenParen, + Pound, + Semi, + Slash, +} + +pub struct RustSearcher<'txt> { + text: &'txt str, + cursor: lexer::Cursor<'txt>, + pos: u32, + next_token: lexer::Token, +} +impl<'txt> RustSearcher<'txt> { + #[must_use] + #[expect(clippy::inconsistent_struct_constructor)] + pub fn new(text: &'txt str) -> Self { + let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); + Self { + text, + pos: 0, + next_token: cursor.advance_token(), + cursor, + } + } + + #[must_use] + pub fn peek_text(&self) -> &'txt str { + &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] + } + + #[must_use] + pub fn peek_len(&self) -> u32 { + self.next_token.len + } + + #[must_use] + pub fn peek(&self) -> lexer::TokenKind { + self.next_token.kind + } + + #[must_use] + pub fn pos(&self) -> u32 { + self.pos + } + + #[must_use] + pub fn at_end(&self) -> bool { + self.next_token.kind == lexer::TokenKind::Eof + } - match update_mode { - UpdateMode::Check if contents != new_contents => exit_with_failure(), - UpdateMode::Check => (), - UpdateMode::Change => { - if let Err(e) = fs::write(path, new_contents.as_bytes()) { - panic!("Cannot write to `{}`: {e}", path.display()); + pub fn step(&mut self) { + // `next_len` is zero for the sentinel value and the eof marker. + self.pos += self.next_token.len; + self.next_token = self.cursor.advance_token(); + } + + /// Consumes the next token if it matches the requested value and captures the value if + /// requested. Returns true if a token was matched. + fn read_token(&mut self, token: Token<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + loop { + match (token, self.next_token.kind) { + (_, lexer::TokenKind::Whitespace) + | ( + Token::AnyComment, + lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, + ) => self.step(), + (Token::AnyComment, _) => return true, + (Token::Bang, lexer::TokenKind::Bang) + | (Token::CloseBrace, lexer::TokenKind::CloseBrace) + | (Token::CloseBracket, lexer::TokenKind::CloseBracket) + | (Token::CloseParen, lexer::TokenKind::CloseParen) + | (Token::Comma, lexer::TokenKind::Comma) + | (Token::Eq, lexer::TokenKind::Eq) + | (Token::Lifetime, lexer::TokenKind::Lifetime { .. }) + | (Token::Lt, lexer::TokenKind::Lt) + | (Token::Gt, lexer::TokenKind::Gt) + | (Token::OpenBrace, lexer::TokenKind::OpenBrace) + | (Token::OpenBracket, lexer::TokenKind::OpenBracket) + | (Token::OpenParen, lexer::TokenKind::OpenParen) + | (Token::Pound, lexer::TokenKind::Pound) + | (Token::Semi, lexer::TokenKind::Semi) + | (Token::Slash, lexer::TokenKind::Slash) + | ( + Token::LitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) => { + self.step(); + return true; + }, + (Token::Ident(x), lexer::TokenKind::Ident) if x == self.peek_text() => { + self.step(); + return true; + }, + (Token::DoubleColon, lexer::TokenKind::Colon) => { + self.step(); + if !self.at_end() && matches!(self.next_token.kind, lexer::TokenKind::Colon) { + self.step(); + return true; + } + return false; + }, + ( + Token::CaptureLitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) + | (Token::CaptureIdent, lexer::TokenKind::Ident) => { + **captures.next().unwrap() = self.peek_text(); + self.step(); + return true; + }, + _ => return false, + } + } + } + + #[must_use] + pub fn find_token(&mut self, token: Token<'_>) -> bool { + let mut capture = [].iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return false; + } + } + true + } + + #[must_use] + pub fn find_capture_token(&mut self, token: Token<'_>) -> Option<&'txt str> { + let mut res = ""; + let mut capture = &mut res; + let mut capture = slice::from_mut(&mut capture).iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return None; + } + } + Some(res) + } + + #[must_use] + pub fn match_tokens(&mut self, tokens: &[Token<'_>], captures: &mut [&mut &'txt str]) -> bool { + let mut captures = captures.iter_mut(); + tokens.iter().all(|&t| self.read_token(t, &mut captures)) + } +} + +#[expect(clippy::must_use_candidate)] +pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(ref e) => panic_file(e, FileAction::Create, new_name), + } + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(ref e) => { + drop(fs::remove_file(new_name)); + // `NotADirectory` happens on posix when renaming a directory to an existing file. + // Windows will ignore this and rename anyways. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { + false + } else { + panic_file(e, FileAction::Rename, old_name); } }, } } -/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters -/// were found, or the missing delimiter if not. -pub(crate) fn replace_region_in_text<'a>( - text: &str, - start: &'a str, - end: &'a str, - mut write_replacement: impl FnMut(&mut String), -) -> Result<String, &'a str> { - let (text_start, rest) = text.split_once(start).ok_or(start)?; - let (_, text_end) = rest.split_once(end).ok_or(end)?; - - let mut res = String::with_capacity(text.len() + 4096); - res.push_str(text_start); - res.push_str(start); - write_replacement(&mut res); - res.push_str(end); - res.push_str(text_end); - - Ok(res) +#[expect(clippy::must_use_candidate)] +pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { + match fs::create_dir(new_name) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(ref e) => panic_file(e, FileAction::Create, new_name), + } + // Windows can't reliably rename to an empty directory. + #[cfg(windows)] + drop(fs::remove_dir(new_name)); + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(ref e) => { + // Already dropped earlier on windows. + #[cfg(not(windows))] + drop(fs::remove_dir(new_name)); + // `NotADirectory` happens on posix when renaming a file to an existing directory. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { + false + } else { + panic_file(e, FileAction::Rename, old_name); + } + }, + } +} + +pub fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_file(&e, FileAction::Write, path)); +} + +#[expect(clippy::must_use_candidate)] +pub fn delete_file_if_exists(path: &Path) -> bool { + match fs::remove_file(path) { + Ok(()) => true, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::IsADirectory) => false, + Err(ref e) => panic_file(e, FileAction::Delete, path), + } +} + +pub fn delete_dir_if_exists(path: &Path) { + match fs::remove_dir_all(path) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) => {}, + Err(ref e) => panic_file(e, FileAction::Delete, path), + } } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d36b6ae9c95..7e3cb404247 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" @@ -19,10 +19,7 @@ itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", optional = true } -tempfile = { version = "3.3.0", optional = true } toml = "0.7.3" -regex = { version = "1.5", optional = true } unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" @@ -31,10 +28,6 @@ url = "2.2" [dev-dependencies] walkdir = "2.3" -[features] -# build clippy with internal lints enabled, off by default -internal = ["serde_json", "tempfile", "regex"] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 5b8a7461386..01ac1fe0003 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -397,7 +397,9 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // Filters the auto-included Rust standard library. continue; } - println!("Unknown item: {item:?}"); + if cfg!(debug_assertions) { + rustc_middle::bug!("unknown item: {item:?}"); + } } } else if let ItemKind::Impl(_) = item.kind && get_item_name(item).is_some() diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index bf376eea70d..8b8b42bbf72 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -3,14 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local}; +use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { @@ -86,9 +85,9 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { && ctxt.is_root() && let which_trait = match fn_name { sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, - _ if fn_name.as_str() == "to_owned" - && is_diag_trait_item(cx, fn_id, sym::ToOwned) - && self.msrv.meets(cx, msrvs::CLONE_INTO) => + sym::to_owned + if is_diag_trait_item(cx, fn_id, sym::ToOwned) + && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, @@ -111,8 +110,8 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // Only suggest if `clone_from`/`clone_into` is explicitly implemented && resolved_assoc_items.in_definition_order().any(|assoc| match which_trait { - CloneTrait::Clone => assoc.name == sym::clone_from, - CloneTrait::ToOwned => assoc.name.as_str() == "clone_into", + CloneTrait::Clone => assoc.name() == sym::clone_from, + CloneTrait::ToOwned => assoc.name() == sym::clone_into, } ) && !clone_source_borrows_from_dest(cx, lhs, rhs.span) diff --git a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 457692ed5dc..4d64eec25d2 100644 --- a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -1,17 +1,15 @@ 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 clippy_utils::sym; use rustc_ast::MetaItemInner; use rustc_lint::{EarlyContext, Level, LintContext}; +use rustc_span::DUMMY_SP; use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { - if let Some(lint_name) = extract_clippy_lint(lint) - && lint_name.as_str() == "restriction" - && name != sym::allow - { + if name != sym::allow && extract_clippy_lint(lint) == Some(sym::restriction) { span_lint_and_help( cx, BLANKET_CLIPPY_RESTRICTION_LINTS, diff --git a/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/clippy_lints/src/attrs/deprecated_cfg_attr.rs index cd38aed26a3..0edb50be8c7 100644 --- a/clippy_lints/src/attrs/deprecated_cfg_attr.rs +++ b/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -1,10 +1,10 @@ use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, MsrvStack}; +use clippy_utils::sym; use rustc_ast::AttrStyle; use rustc_errors::Applicability; use rustc_lint::EarlyContext; -use rustc_span::sym; pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) { // check cfg_attr @@ -18,7 +18,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) { && msrv.meets(msrvs::TOOL_ATTRIBUTES) // check for `rustfmt_skip` and `rustfmt::skip` && let Some(skip_item) = &items[1].meta_item() - && (skip_item.has_name(sym!(rustfmt_skip)) + && (skip_item.has_name(sym::rustfmt_skip) || skip_item .path .segments @@ -73,7 +73,7 @@ fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::Met } fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) { - if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") { + if item.has_name(sym::feature) && item.value_str() == Some(sym::cargo_clippy) { span_lint_and_sugg( cx, DEPRECATED_CLIPPY_CFG_ATTR, diff --git a/clippy_lints/src/attrs/deprecated_semver.rs b/clippy_lints/src/attrs/deprecated_semver.rs index 50943b36809..bd6459d6f9d 100644 --- a/clippy_lints/src/attrs/deprecated_semver.rs +++ b/clippy_lints/src/attrs/deprecated_semver.rs @@ -1,5 +1,6 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; use rustc_ast::{LitKind, MetaItemLit}; use rustc_lint::EarlyContext; use rustc_span::Span; @@ -7,7 +8,7 @@ use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind - && (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok()) + && (is == sym::TBD || Version::parse(is.as_str()).is_ok()) { return; } diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index f7f168cb267..9a124298041 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -468,7 +468,7 @@ declare_clippy_lint! { /// #[ignore = "Some good reason"] /// fn test() {} /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.88.0"] pub IGNORE_WITHOUT_REASON, pedantic, "ignored tests without messages" diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 064b72b9305..4059f9603c3 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -2,10 +2,10 @@ use super::USELESS_ATTRIBUTE; use super::utils::{is_lint_level, is_word, namespace_and_lint}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, first_line_of_span}; +use clippy_utils::sym; use rustc_ast::{Attribute, Item, ItemKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, LintContext}; -use rustc_span::sym; pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use)); @@ -26,16 +26,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if namespace.is_none() && matches!( - name.as_str(), - "ambiguous_glob_reexports" - | "dead_code" - | "deprecated" - | "hidden_glob_reexports" - | "unreachable_pub" - | "unused" - | "unused_braces" - | "unused_import_braces" - | "unused_imports" + name, + sym::ambiguous_glob_reexports + | sym::dead_code + | sym::deprecated + | sym::hidden_glob_reexports + | sym::unreachable_pub + | sym::unused + | sym::unused_braces + | sym::unused_import_braces + | sym::unused_imports ) { return; @@ -43,16 +43,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if namespace == Some(sym::clippy) && matches!( - name.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - | "disallowed_types" - | "unused_trait_names" + name, + sym::wildcard_imports + | sym::enum_glob_use + | sym::redundant_pub_crate + | sym::macro_use_imports + | sym::unsafe_removed_from_name + | sym::module_name_repetitions + | sym::single_component_path_imports + | sym::disallowed_types + | sym::unused_trait_names ) { return; @@ -62,7 +62,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if is_word(lint, sym::unused_imports) && skip_unused_imports { return; } - if is_word(lint, sym!(unused_extern_crates)) { + if is_word(lint, sym::unused_extern_crates) { return; } }, diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 52d1d5b4c67..31cc004f685 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::paths::{self, PathNS}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; @@ -182,6 +182,7 @@ impl AwaitHolding { let (def_ids, _) = create_disallowed_map( tcx, &conf.await_holding_invalid_types, + PathNS::Type, crate::disallowed_types::def_kind_predicate, "type", false, @@ -275,12 +276,10 @@ fn emit_invalid_type( } fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) + match cx.tcx.get_diagnostic_name(def_id) { + Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard), + None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)), + } } fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index adac2f27ea8..ae36bb76117 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -53,10 +54,10 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - .not_trait() .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[])) .and_then(|trait_id| { - cx.tcx.associated_items(trait_id).find_by_name_and_kind( + cx.tcx.associated_items(trait_id).find_by_ident_and_kind( cx.tcx, Ident::from_str("Output"), - ty::AssocKind::Type, + ty::AssocTag::Type, trait_id, ) }) @@ -73,10 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); - let eq_macro = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => true, - "assert_ne" | "debug_assert_ne" => false, + let eq_macro = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_eq_macro | sym::debug_assert_eq_macro) => true, + Some(sym::assert_ne_macro | sym::debug_assert_ne_macro) => false, _ => return, }; let Some((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -115,6 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { return; } + let macro_name = cx.tcx.item_name(macro_call.def_id); let macro_name = macro_name.as_str(); let non_eq_mac = ¯o_name[..macro_name.len() - 3]; span_lint_and_then( diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 876ceef8c32..129e7747840 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::HasSession; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_else_clause, is_in_const_context}; +use clippy_utils::{higher, is_else_clause, is_in_const_context, span_contains_comment}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -46,18 +47,25 @@ declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::If(cond, then, Some(else_)) = expr.kind - && matches!(cond.kind, ExprKind::DropTemps(_)) + if !expr.span.from_expansion() + && let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) && let Some(then_lit) = as_int_bool_lit(then) - && let Some(else_lit) = as_int_bool_lit(else_) + && let Some(else_lit) = as_int_bool_lit(r#else) && then_lit != else_lit - && !expr.span.from_expansion() && !is_in_const_context(cx) { let ty = cx.typeck_results().expr_ty(then); - let mut applicability = Applicability::MachineApplicable; + let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; let snippet = { - let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); + let mut sugg = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); if !then_lit { sugg = !sugg; } @@ -91,10 +99,11 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { } } -fn as_int_bool_lit(e: &Expr<'_>) -> Option<bool> { - if let ExprKind::Block(b, _) = e.kind +fn as_int_bool_lit(expr: &Expr<'_>) -> Option<bool> { + if let ExprKind::Block(b, _) = expr.kind && b.stmts.is_empty() && let Some(e) = b.expr + && !e.span.from_expansion() && let ExprKind::Lit(lit) = e.kind && let LitKind::Int(x, _) = lit.node { diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 249f3b375c0..bc6ba84772b 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -199,7 +199,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) { && !expr.span.from_expansion() && !inner.span.from_expansion() && let Some(suggestion) = simplify_not(cx, msrv, inner) - && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow + && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow { use clippy_utils::sugg::{Sugg, has_enclosing_paren}; let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) { @@ -609,7 +609,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> { } } let nonminimal_bool_lint = |mut suggestions: Vec<_>| { - if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow { + if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow { suggestions.sort(); span_lint_hir_and_then( self.cx, diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index 64345c81a24..ad0a4f8cdf3 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,11 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::Msrv; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::{is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core}; +use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::{Adjust, AutoBorrow}; use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -29,10 +30,6 @@ pub(super) fn check<'tcx>( } let (suggestion, span) = if msrv.meets(cx, msrvs::RAW_REF_OP) { - let operator_kind = match mutability { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; // Make sure that the span to be replaced doesn't include parentheses, that could break the // suggestion. let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) { @@ -42,7 +39,7 @@ pub(super) fn check<'tcx>( } else { expr.span }; - (format!("&raw {operator_kind} {snip}"), span) + (format!("&raw {} {snip}", mutability.ptr_str()), span) } else { let Some(std_or_core) = std_or_core(cx) else { return false; @@ -59,3 +56,25 @@ pub(super) fn check<'tcx>( } false } + +/// Check for an implicit cast from reference to raw pointer outside an explicit `as`. +pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) { + if !expr.span.from_expansion() + && let ExprKind::AddrOf(BorrowKind::Ref, _, pointee) = expr.kind + && !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..))) + && let [deref, borrow] = cx.typeck_results().expr_adjustments(expr) + && matches!(deref.kind, Adjust::Deref(..)) + && let Adjust::Borrow(AutoBorrow::RawPtr(mutability)) = borrow.kind + // Do not suggest taking a raw pointer to a temporary value + && !is_expr_temporary_value(cx, pointee) + { + span_lint_and_then(cx, BORROW_AS_PTR, expr.span, "implicit borrow as raw pointer", |diag| { + diag.span_suggestion_verbose( + expr.span.until(pointee.span), + "use a raw pointer instead", + format!("&raw {} ", mutability.ptr_str()), + Applicability::MachineApplicable, + ); + }); + } +} diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 164d3540253..ba31a51f738 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -19,7 +20,7 @@ pub(super) fn check( if let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind - && method_path.ident.name.as_str() == "abs" + && method_path.ident.name == sym::abs && msrv.meets(cx, msrvs::UNSIGNED_ABS) { let span = if from.bit_width() == to.bit_width() { diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 8742f5f1a0e..e92879b853d 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,9 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::expr_or_init; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; +use clippy_utils::{expr_or_init, sym}; use rustc_abi::IntegerType; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; @@ -73,7 +73,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b nbits }, ExprKind::MethodCall(method, _value, [], _) => { - if method.ident.name.as_str() == "signum" { + if method.ident.name == sym::signum { 0 // do not lint if cast comes from a `signum` function } else { nbits diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 3fca0f89707..01020f3eee2 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_c_void; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, sym}; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::CAST_PTR_ALIGNMENT; @@ -20,7 +19,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 - && method_path.ident.name.as_str() == "cast" + && method_path.ident.name == sym::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/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs b/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs new file mode 100644 index 00000000000..31cdd078f45 --- /dev/null +++ b/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::{Symbol, sym}; + +use super::CONFUSING_METHOD_TO_NUMERIC_CAST; + +fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Char => Some("char"), + ty::Int(int) => Some(int.name_str()), + ty::Uint(uint) => Some(uint.name_str()), + ty::Float(float) => Some(float.name_str()), + _ => None, + } +} + +fn get_const_name_and_ty_name( + cx: &LateContext<'_>, + method_name: Symbol, + method_def_id: DefId, + generics: &[GenericArg<'_>], +) -> Option<(&'static str, &'static str)> { + let method_name = method_name.as_str(); + let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id); + + let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) { + // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. + if let [ty] = generics + && let Some(ty) = ty.as_type() + { + get_primitive_ty_name(ty)? + } else { + return None; + } + } else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id) + && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) + && ["min", "max", "minimum", "maximum", "min_value", "max_value"].contains(&method_name) + { + ty_name + } else { + return None; + }; + + let const_name = if method_name.starts_with("max") { "MAX" } else { "MIN" }; + Some((const_name, ty_name)) +} + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We allow casts from any function type to any function type. + match cast_to.kind() { + ty::FnDef(..) | ty::FnPtr(..) => return, + _ => { /* continue to checks */ }, + } + + if let ty::FnDef(def_id, generics) = cast_from.kind() + && let Some(method_name) = cx.tcx.opt_item_name(*def_id) + && let Some((const_name, ty_name)) = get_const_name_and_ty_name(cx, method_name, *def_id, generics.as_slice()) + { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + + span_lint_and_then( + cx, + CONFUSING_METHOD_TO_NUMERIC_CAST, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_verbose( + expr.span, + "did you mean to use the associated constant?", + format!("{ty_name}::{const_name} as {cast_to}"), + applicability, + ); + }, + ); + } +} diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index 8ace27eca89..61dfc0fc042 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_normalizable; -use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core}; +use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; @@ -55,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind && let Some(fun_id) = path_def_id(cx, fun) - && match_def_path(cx, fun_id, &paths::ALIGN_OF) + && paths::ALIGN_OF.matches(cx, fun_id) && let Some(args) = path.segments.last().and_then(|seg| seg.args) && let [GenericArg::Type(generic_ty)] = args.args { @@ -71,12 +70,10 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned<LitKind>, to: &Ty<'_>) return false; } let to_mid_ty = cx.typeck_results().node_type(to.hir_id); - is_normalizable(cx, cx.param_env, to_mid_ty) - && cx - .tcx - .layout_of(cx.typing_env().as_query_input(to_mid_ty)) - .is_ok_and(|layout| { - let align = u128::from(layout.align.abi.bytes()); - u128::from(val) <= align - }) + cx.tcx + .layout_of(cx.typing_env().as_query_input(to_mid_ty)) + .is_ok_and(|layout| { + let align = u128::from(layout.align.abi.bytes()); + u128::from(val) <= align + }) } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 7e4b4357567..daae9a8bb08 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -14,6 +14,7 @@ mod cast_sign_loss; mod cast_slice_different_sizes; mod cast_slice_from_raw_parts; mod char_lit_as_u8; +mod confusing_method_to_numeric_cast; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; @@ -780,12 +781,38 @@ declare_clippy_lint! { /// let aligned = std::ptr::dangling::<u32>(); /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); /// ``` - #[clippy::version = "1.87.0"] + #[clippy::version = "1.88.0"] pub MANUAL_DANGLING_PTR, style, "casting small constant literals to pointers to create dangling pointers" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. + /// + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. + /// + /// ### Example + /// ```no_run + /// let _ = u16::max as usize; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let _ = u16::MAX as usize; + /// ``` + #[clippy::version = "1.86.0"] + pub CONFUSING_METHOD_TO_NUMERIC_CAST, + suspicious, + "casting a primitive method pointer to any integer type" +} + pub struct Casts { msrv: Msrv, } @@ -823,6 +850,7 @@ impl_lint_pass!(Casts => [ REF_AS_PTR, AS_POINTER_UNDERSCORE, MANUAL_DANGLING_PTR, + CONFUSING_METHOD_TO_NUMERIC_CAST, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -847,6 +875,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); + confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); @@ -878,6 +907,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } + if self.msrv.meets(cx, msrvs::RAW_REF_OP) { + borrow_as_ptr::check_implicit_cast(cx, expr); + } cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, self.msrv); diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 18e32c5c8a1..2471c735551 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -53,7 +53,8 @@ pub(super) fn check<'tcx>( } if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { - let sugg = Sugg::hir(cx, cast_expr, "_"); + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app); let constness = match *to_mutbl { Mutability::Not => "const", Mutability::Mut => "mut", @@ -66,7 +67,7 @@ pub(super) fn check<'tcx>( "`as` casting between raw pointers while changing only its constness", format!("try `pointer::cast_{constness}`, a safer alternative"), format!("{}.cast_{constness}()", sugg.maybe_paren()), - Applicability::MachineApplicable, + app, ); } } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index ae994e94a32..8e8c55cf383 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -8,7 +8,9 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; +use rustc_span::{Symbol, sym}; use std::ops::ControlFlow; use super::UNNECESSARY_CAST; @@ -142,6 +144,33 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) { + enum MaybeParenOrBlock { + Paren, + Block, + Nothing, + } + + fn is_borrow_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::AddrOf(..)) + || cx + .typeck_results() + .expr_adjustments(expr) + .first() + .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) + } + + fn is_in_allowed_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + const ALLOWED_MACROS: &[Symbol] = &[ + sym::format_args_macro, + sym::assert_eq_macro, + sym::debug_assert_eq_macro, + sym::assert_ne_macro, + sym::debug_assert_ne_macro, + ]; + matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym))) + } + if let Some(id) = path_to_local(cast_expr) && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { @@ -150,15 +179,15 @@ pub(super) fn check<'tcx>( return false; } - // If the whole cast expression is a unary expression (`(*x as T)`) or an addressof - // expression (`(&x as T)`), then not surrounding the suggestion into a block risks us - // changing the precedence of operators if the cast expression is followed by an operation - // with higher precedence than the unary operator (`(*x as T).foo()` would become - // `*x.foo()`, which changes what the `*` applies on). - // The same is true if the expression encompassing the cast expression is a unary - // expression or an addressof expression. - let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)) - || get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))); + // Changing `&(x as i32)` to `&x` would change the meaning of the code because the previous creates + // a reference to the temporary while the latter creates a reference to the original value. + let surrounding = match cx.tcx.parent_hir_node(expr.hir_id) { + Node::Expr(parent) if is_borrow_expr(cx, parent) && !is_in_allowed_macro(cx, parent) => { + MaybeParenOrBlock::Block + }, + Node::Expr(parent) if cast_expr.precedence() < parent.precedence() => MaybeParenOrBlock::Paren, + _ => MaybeParenOrBlock::Nothing, + }; span_lint_and_sugg( cx, @@ -166,10 +195,10 @@ pub(super) fn check<'tcx>( expr.span, format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - if needs_block { - format!("{{ {cast_str} }}") - } else { - cast_str + match surrounding { + MaybeParenOrBlock::Paren => format!("({cast_str})"), + MaybeParenOrBlock::Block => format!("{{ {cast_str} }}"), + MaybeParenOrBlock::Nothing => cast_str, }, Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs new file mode 100644 index 00000000000..6b239a1541b --- /dev/null +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -0,0 +1,100 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{is_in_const_context, is_mutable, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for slice references with cloned references such as `&[f.clone()]`. + /// + /// ### Why is this bad + /// + /// A reference does not need to be owned in order to used as a slice. + /// + /// ### Known problems + /// + /// This lint does not know whether or not a clone implementation has side effects. + /// + /// ### Example + /// + /// ```ignore + /// let data = 10; + /// let data_ref = &data; + /// take_slice(&[data_ref.clone()]); + /// ``` + /// Use instead: + /// ```ignore + /// use std::slice; + /// let data = 10; + /// let data_ref = &data; + /// take_slice(slice::from_ref(data_ref)); + /// ``` + #[clippy::version = "1.87.0"] + pub CLONED_REF_TO_SLICE_REFS, + perf, + "cloning a reference for slice references" +} + +pub struct ClonedRefToSliceRefs<'a> { + msrv: &'a Msrv, +} +impl<'a> ClonedRefToSliceRefs<'a> { + pub fn new(conf: &'a Conf) -> Self { + Self { msrv: &conf.msrv } + } +} + +impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); + +impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if self.msrv.meets(cx, { + if is_in_const_context(cx) { + msrvs::CONST_SLICE_FROM_REF + } else { + msrvs::SLICE_FROM_REF + } + }) + // `&[foo.clone()]` expressions + && let ExprKind::AddrOf(_, mutability, arr) = &expr.kind + // mutable references would have a different meaning + && mutability.is_not() + + // check for single item arrays + && let ExprKind::Array([item]) = &arr.kind + + // check for clones + && let ExprKind::MethodCall(_, val, _, _) = item.kind + && is_trait_method(cx, item, sym::Clone) + + // check for immutability or purity + && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) + + // get appropriate crate for `slice::from_ref` + && let Some(builtin_crate) = clippy_utils::std_or_core(cx) + { + let mut sugg = Sugg::hir(cx, val, "_"); + if !cx.typeck_results().expr_ty(val).is_ref() { + sugg = sugg.addr(); + } + + span_lint_and_sugg( + cx, + CLONED_REF_TO_SLICE_REFS, + expr.span, + format!("this call to `clone` can be replaced with `{builtin_crate}::slice::from_ref`"), + "try", + format!("{builtin_crate}::slice::from_ref({sugg})"), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 20fae8a6775..7f6ecea99fb 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,11 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; use rustc_ast::BinOpKind; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -78,14 +78,14 @@ declare_clippy_lint! { } pub struct CollapsibleIf { - let_chains_enabled: bool, + msrv: Msrv, lint_commented_code: bool, } impl CollapsibleIf { - pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - let_chains_enabled: tcx.features().let_chains(), + msrv: conf.msrv, lint_commented_code: conf.lint_commented_code, } } @@ -127,7 +127,7 @@ impl CollapsibleIf { if let Some(inner) = expr_block(then) && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind - && self.eligible_condition(check_inner) + && self.eligible_condition(cx, check_inner) && let ctxt = expr.span.ctxt() && inner.span.ctxt() == ctxt && (self.lint_commented_code || !block_starts_with_comment(cx, then)) @@ -163,8 +163,9 @@ impl CollapsibleIf { } } - pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool { - self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..)) + fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { + !matches!(cond.kind, ExprKind::Let(..)) + || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) } } @@ -180,7 +181,7 @@ impl LateLintPass<'_> for CollapsibleIf { { Self::check_collapsible_else_if(cx, then.span, else_); } else if else_.is_none() - && self.eligible_condition(cond) + && self.eligible_condition(cx, cond) && let ExprKind::Block(then, None) = then.kind { self.check_collapsible_if_if(cx, expr, cond, then); @@ -202,13 +203,12 @@ fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { match block.stmts { [] => block.expr, - [stmt] => { - if let StmtKind::Semi(expr) = stmt.kind { - Some(expr) - } else { - None - } - }, + [ + Stmt { + kind: StmtKind::Semi(expr), + .. + }, + ] if block.expr.is_none() => Some(expr), _ => None, } } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 9c3009a86cd..238ebd4a444 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -75,11 +75,15 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } // Check that there exists at least one explicit else condition - let (conds, _) = if_sequence(expr); + let (conds, blocks) = if_sequence(expr); if conds.len() < 2 { return; } + if blocks.len() < 3 { + return; + } + for cond in conds.windows(2) { if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) = (&cond[0].kind, &cond[1].kind) @@ -125,6 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else { unreachable!(); }; + let lhs = Sugg::hir(cx, lhs, "..").maybe_paren(); let rhs = Sugg::hir(cx, rhs, "..").addr(); span_lint_and_sugg( diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 42fbe6438d4..48bf3dafa6e 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; use clippy_utils::visitors::for_each_expr_without_closures; @@ -567,7 +567,7 @@ fn method_caller_is_mutable<'tcx>( /// Implementation of `IFS_SAME_COND`. fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for (i, j) in search_same( + for group in search_same( conds, |e| hash_expr(cx, e), |lhs, rhs| { @@ -584,14 +584,8 @@ fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mu } }, ) { - span_lint_and_note( - cx, - IFS_SAME_COND, - j.span, - "this `if` has the same condition as a previous `if`", - Some(i.span), - "same as this", - ); + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); } } @@ -609,14 +603,13 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { SpanlessEq::new(cx).eq_expr(lhs, rhs) }; - for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) { - span_lint_and_note( + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( cx, SAME_FUNCTIONS_IN_IF_CONDITION, - j.span, - "this `if` has the same function call as a previous `if`", - Some(i.span), - "same as this", + spans, + "these `if` branches have the same function call", ); } } diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index c2aac7ca090..19f62e8bf79 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{Span, kw}; declare_clippy_lint! { /// ### What it does @@ -105,12 +105,11 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> { fn is_crate_keyword(tt: &TokenTree) -> Option<Span> { if let TokenTree::Token( Token { - kind: TokenKind::Ident(symbol, _), + kind: TokenKind::Ident(kw::Crate, _), span, }, _, ) = tt - && symbol.as_str() == "crate" { Some(*span) } else { diff --git a/clippy_lints/src/declare_clippy_lint.rs b/clippy_lints/src/declare_clippy_lint.rs index 4d908af4084..9f82f876727 100644 --- a/clippy_lints/src/declare_clippy_lint.rs +++ b/clippy_lints/src/declare_clippy_lint.rs @@ -165,17 +165,4 @@ macro_rules! declare_clippy_lint { $(, $eval_always)? } }; - - ( - $(#[doc = $lit:literal])* - pub $lint_name:ident, - internal, - $desc:literal - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Allow, crate::LintCategory::Internal, $desc, - None, "0.0.0" - } - }; } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3e9469256e5..bb825c7655f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -3,36 +3,6 @@ // Manual edits will be overwritten. pub static LINTS: &[&crate::LintInfo] = &[ - #[cfg(feature = "internal")] - crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO, - #[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, @@ -94,6 +64,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, crate::casts::CHAR_LIT_AS_U8_INFO, + crate::casts::CONFUSING_METHOD_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, @@ -105,6 +76,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::ZERO_PTR_INFO, crate::cfg_not_test::CFG_NOT_TEST_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, + crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_IF_INFO, @@ -491,6 +463,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::SWAP_WITH_TEMPORARY_INFO, crate::methods::TYPE_ID_ON_BOX_INFO, crate::methods::UNBUFFERED_BYTES_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO, @@ -667,6 +640,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::redundant_slicing::DEREF_BY_SLICING_INFO, crate::redundant_slicing::REDUNDANT_SLICING_INFO, crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, + crate::redundant_test_prefix::REDUNDANT_TEST_PREFIX_INFO, crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO, crate::ref_option_ref::REF_OPTION_REF_INFO, crate::ref_patterns::REF_PATTERNS_INFO, @@ -733,13 +707,9 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, - crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, - crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, - crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, - crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index b60c11d79d4..5204f73ea0a 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -2,53 +2,52 @@ // Prefer to use those when possible. macro_rules! declare_with_version { - ($name:ident($name_version:ident): &[$ty:ty] = &[$( + ($name:ident($name_version:ident) = [$( #[clippy::version = $version:literal] $e:expr, )*]) => { - pub static $name: &[$ty] = &[$($e),*]; + pub static $name: &[(&str, &str)] = &[$($e),*]; #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } #[rustfmt::skip] -declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ - #[clippy::version = "pre 1.29.0"] - ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), +declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ + #[clippy::version = "1.30.0"] + ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), + #[clippy::version = "1.86.0"] + ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), + #[clippy::version = "pre 1.29.0"] + ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), + #[clippy::version = "1.86.0"] + ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), + #[clippy::version = "1.54.0"] + ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "pre 1.29.0"] ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), + #[clippy::version = "1.47.0"] + ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), + #[clippy::version = "1.44.0"] + ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), + ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), + ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "pre 1.29.0"] - ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), - #[clippy::version = "1.30.0"] - ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), + ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), + ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), #[clippy::version = "1.39.0"] ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), - #[clippy::version = "1.44.0"] - ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), - #[clippy::version = "1.47.0"] - ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), - #[clippy::version = "1.54.0"] - ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "1.54.0"] ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), - #[clippy::version = "1.86.0"] - ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), - #[clippy::version = "1.86.0"] - ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), - // end deprecated lints. used by `cargo dev deprecate_lint` ]} #[rustfmt::skip] -declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ +declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), #[clippy::version = ""] @@ -62,6 +61,12 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::box_vec", "clippy::box_collection"), #[clippy::version = ""] + ("clippy::cast_ref_to_mut", "invalid_reference_casting"), + #[clippy::version = ""] + ("clippy::clone_double_ref", "suspicious_double_ref_op"), + #[clippy::version = ""] + ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = ""] ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), #[clippy::version = ""] ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), @@ -71,15 +76,35 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::disallowed_method", "clippy::disallowed_methods"), #[clippy::version = ""] ("clippy::disallowed_type", "clippy::disallowed_types"), + #[clippy::version = "1.86.0"] + ("clippy::double_neg", "double_negations"), + #[clippy::version = ""] + ("clippy::drop_bounds", "drop_bounds"), + #[clippy::version = ""] + ("clippy::drop_copy", "dropping_copy_types"), + #[clippy::version = ""] + ("clippy::drop_ref", "dropping_references"), #[clippy::version = ""] ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - #[clippy::version = "1.51.0"] - ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = "1.53.0"] ("clippy::filter_map", "clippy::manual_filter_map"), + #[clippy::version = "1.51.0"] + ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = ""] ("clippy::fn_address_comparisons", "unpredictable_function_pointer_comparisons"), #[clippy::version = ""] + ("clippy::fn_null_check", "useless_ptr_null_checks"), + #[clippy::version = ""] + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::forget_copy", "forgetting_copy_types"), + #[clippy::version = ""] + ("clippy::forget_ref", "forgetting_references"), + #[clippy::version = ""] ("clippy::identity_conversion", "clippy::useless_conversion"), #[clippy::version = "pre 1.29.0"] ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"), @@ -92,7 +117,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), #[clippy::version = ""] + ("clippy::into_iter_on_array", "array_into_iter"), + #[clippy::version = ""] + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + #[clippy::version = "CURRENT_RUSTC_VERSION"] + ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), + #[clippy::version = ""] + ("clippy::invalid_ref", "invalid_value"), + #[clippy::version = ""] + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), + #[clippy::version = ""] + ("clippy::let_underscore_drop", "let_underscore_drop"), + #[clippy::version = ""] ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), + #[clippy::version = "1.80.0"] + ("clippy::maybe_misused_cfg", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + #[clippy::version = "1.80.0"] + ("clippy::mismatched_target_os", "unexpected_cfgs"), #[clippy::version = ""] ("clippy::new_without_default_derive", "clippy::new_without_default"), #[clippy::version = ""] @@ -108,6 +151,10 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), #[clippy::version = ""] + ("clippy::panic_params", "non_fmt_panics"), + #[clippy::version = ""] + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), + #[clippy::version = ""] ("clippy::ref_in_deref", "clippy::needless_borrow"), #[clippy::version = ""] ("clippy::result_expect_used", "clippy::expect_used"), @@ -116,67 +163,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::result_unwrap_used", "clippy::unwrap_used"), #[clippy::version = ""] + ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + #[clippy::version = ""] ("clippy::single_char_push_str", "clippy::single_char_add_str"), #[clippy::version = ""] ("clippy::stutter", "clippy::module_name_repetitions"), #[clippy::version = ""] + ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = ""] ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), #[clippy::version = ""] ("clippy::to_string_in_display", "clippy::recursive_format_impl"), - #[clippy::version = ""] - ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), - #[clippy::version = ""] - ("clippy::zero_width_space", "clippy::invisible_characters"), - #[clippy::version = ""] - ("clippy::cast_ref_to_mut", "invalid_reference_casting"), - #[clippy::version = ""] - ("clippy::clone_double_ref", "suspicious_double_ref_op"), - #[clippy::version = ""] - ("clippy::cmp_nan", "invalid_nan_comparisons"), - #[clippy::version = "CURRENT_RUSTC_VERSION"] - ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), - #[clippy::version = "1.86.0"] - ("clippy::double_neg", "double_negations"), - #[clippy::version = ""] - ("clippy::drop_bounds", "drop_bounds"), - #[clippy::version = ""] - ("clippy::drop_copy", "dropping_copy_types"), - #[clippy::version = ""] - ("clippy::drop_ref", "dropping_references"), - #[clippy::version = ""] - ("clippy::fn_null_check", "useless_ptr_null_checks"), - #[clippy::version = ""] - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::forget_copy", "forgetting_copy_types"), - #[clippy::version = ""] - ("clippy::forget_ref", "forgetting_references"), - #[clippy::version = ""] - ("clippy::into_iter_on_array", "array_into_iter"), - #[clippy::version = ""] - ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), - #[clippy::version = ""] - ("clippy::invalid_ref", "invalid_value"), - #[clippy::version = ""] - ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), - #[clippy::version = ""] - ("clippy::let_underscore_drop", "let_underscore_drop"), - #[clippy::version = "1.80.0"] - ("clippy::maybe_misused_cfg", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), - #[clippy::version = "1.80.0"] - ("clippy::mismatched_target_os", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::panic_params", "non_fmt_panics"), - #[clippy::version = ""] - ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), - #[clippy::version = ""] - ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_float_to_int", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_char", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_float", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] @@ -184,8 +189,9 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::unused_label", "unused_labels"), #[clippy::version = ""] + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), + #[clippy::version = ""] ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), #[clippy::version = ""] - ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), - // end renamed lints. used by `cargo dev rename_lint` + ("clippy::zero_width_space", "clippy::invisible_characters"), ]} diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7da5a530eaa..a22a2ee66d2 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -252,13 +253,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } let typeck = cx.typeck_results(); - let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { + let Some((kind, sub_expr, skip_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { report(cx, expr, state, data, typeck); } return; }; + self.skip_expr = skip_expr; match (self.state.take(), kind) { (None, kind) => { @@ -671,42 +673,38 @@ fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, -) -> Option<(RefOp, &'tcx Expr<'tcx>)> { - let (is_ufcs, def_id, arg) = match expr.kind { - ExprKind::MethodCall(_, arg, [], _) => (false, typeck.type_dependent_def_id(expr.hir_id)?, arg), +) -> Option<(RefOp, &'tcx Expr<'tcx>, Option<HirId>)> { + let (call_path_id, def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, arg, [], _) => (None, typeck.type_dependent_def_id(expr.hir_id)?, arg), ExprKind::Call( - Expr { - kind: ExprKind::Path(path), + &Expr { + kind: ExprKind::Path(QPath::Resolved(None, path)), hir_id, .. }, [arg], - ) => (true, typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ) => (Some(hir_id), path.res.opt_def_id()?, arg), ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_raw_ptr() => { - return Some((RefOp::Deref, sub_expr)); + return Some((RefOp::Deref, sub_expr, None)); + }, + ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => { + return Some((RefOp::AddrOf(mutability), sub_expr, None)); }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)), _ => return None, }; - if tcx.is_diagnostic_item(sym::deref_method, def_id) { - Some(( - RefOp::Method { - mutbl: Mutability::Not, - is_ufcs, - }, - arg, - )) - } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { - Some(( - RefOp::Method { - mutbl: Mutability::Mut, - is_ufcs, - }, - arg, - )) - } else { - None - } + let mutbl = match tcx.get_diagnostic_name(def_id) { + Some(sym::deref_method) => Mutability::Not, + Some(sym::deref_mut_method) => Mutability::Mut, + _ => return None, + }; + Some(( + RefOp::Method { + mutbl, + is_ufcs: call_path_id.is_some(), + }, + arg, + call_path_id, + )) } // Checks if the adjustments contains a deref of `ManuallyDrop<_>` @@ -853,7 +851,7 @@ impl TyCoercionStability { continue; }, ty::Param(_) if for_return => Self::Deref, - ty::Alias(ty::Weak | ty::Inherent, _) => unreachable!("should have been normalized away above"), + ty::Alias(ty::Free | ty::Inherent, _) => unreachable!("should have been normalized away above"), ty::Alias(ty::Projection, _) if !for_return && ty.has_non_region_param() => Self::Reborrow, ty::Infer(_) | ty::Error(_) @@ -944,7 +942,7 @@ fn report<'tcx>( mutbl, } => { let mut app = Applicability::MachineApplicable; - let (expr_str, _expr_is_macro_call) = + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); let (_, ref_count) = peel_middle_ty_refs(ty); @@ -968,20 +966,11 @@ fn report<'tcx>( "&" }; - // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's - // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. - /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix { + let expr_str = if !expr_is_macro_call && is_ufcs && expr.precedence() < ExprPrecedence::Prefix { Cow::Owned(format!("({expr_str})")) } else { expr_str }; - */ - - // Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`. - if is_ufcs { - return; - } span_lint_and_sugg( cx, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 06528f875a2..3443b36eb4f 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -2,7 +2,7 @@ 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}; +use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; @@ -377,7 +377,7 @@ fn check_unsafe_derive_deserialize<'tcx>( } if let Some(trait_def_id) = trait_ref.trait_def_id() - && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE) + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() && let Some(local_def_id) = def.did().as_local() && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index fa33fef2306..25b7099c855 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -2,6 +2,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; @@ -75,6 +76,7 @@ impl DisallowedMacros { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_macros, + PathNS::Macro, |def_kind| matches!(def_kind, DefKind::Macro(_)), "macro", false, diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 1382dafa931..fb970e17f38 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; @@ -66,6 +67,7 @@ impl DisallowedMethods { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, + PathNS::Value, |def_kind| { matches!( def_kind, diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index 53c24a3faf1..d1a8590c59b 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -69,7 +69,7 @@ impl EarlyLintPass for DisallowedScriptIdents { // Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint: // https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint/src/non_ascii_idents.rs - let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).0 != Level::Allow; + let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow; if !check_disallowed_script_idents { return; } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 2bae82648ac..d0b2f0c8407 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; @@ -60,7 +61,14 @@ pub struct DisallowedTypes { impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true); + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); Self { def_ids, prim_tys } } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ab77edf1147..87da380e954 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -93,7 +93,7 @@ declare_clippy_lint! { /// ```no_run /// //! <code>[first](x)second</code> /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub DOC_LINK_CODE, nursery, "link with code back-to-back with other code" diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 24a6e6ce770..ec4538039a9 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -38,7 +38,7 @@ pub fn check( // of all `#[test]` attributes in not ignored code examples fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec<Range<usize>>) { rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_globals_then(edition, None, || { + rustc_span::create_session_globals_then(edition, &[], None, || { let mut test_attr_spans = vec![]; let filename = FileName::anon_source_code(&code); diff --git a/clippy_lints/src/duplicate_mod.rs b/clippy_lints/src/duplicate_mod.rs index 243c99a19ce..ce551a64d99 100644 --- a/clippy_lints/src/duplicate_mod.rs +++ b/clippy_lints/src/duplicate_mod.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind}; use rustc_errors::MultiSpan; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; +use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; use rustc_span::{FileName, Span}; use std::collections::BTreeMap; @@ -45,11 +46,10 @@ declare_clippy_lint! { "file loaded as module multiple times" } -#[derive(PartialOrd, Ord, PartialEq, Eq)] struct Modules { local_path: PathBuf, spans: Vec<Span>, - lint_levels: Vec<Level>, + lint_levels: Vec<LevelAndSource>, } #[derive(Default)] @@ -95,11 +95,11 @@ impl EarlyLintPass for DuplicateMod { .iter() .zip(lint_levels) .filter_map(|(span, lvl)| { - if let Some(id) = lvl.get_expectation_id() { + if let Some(id) = lvl.lint_id { cx.fulfill_expectation(id); } - (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span) + (!matches!(lvl.level, Level::Allow | Level::Expect)).then_some(*span) }) .collect(); diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index 7d87f04fef9..a38d6df89f2 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -1,10 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Item, ItemKind, Variant, VariantData}; +use clippy_utils::attrs::span_contains_cfg; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_lexer::TokenKind; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; +use rustc_hir::def::CtorOf; +use rustc_hir::def::DefKind::Ctor; +use rustc_hir::def::Res::Def; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node, Path, QPath, Variant, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -70,10 +75,23 @@ declare_clippy_lint! { "finds enum variants with empty brackets" } -declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); +#[derive(Debug)] +enum Usage { + Unused { redundant_use_sites: Vec<Span> }, + Used, + NoDefinition { redundant_use_sites: Vec<Span> }, +} + +#[derive(Default)] +pub struct EmptyWithBrackets { + // Value holds `Usage::Used` if the empty tuple variant was used as a function + empty_tuple_enum_variants: FxIndexMap<LocalDefId, Usage>, +} + +impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); -impl EarlyLintPass for EmptyWithBrackets { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +impl LateLintPass<'_> for EmptyWithBrackets { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Struct(ident, var_data, _) = &item.kind && has_brackets(var_data) && let span_after_ident = item.span.with_lo(ident.span.hi()) @@ -96,70 +114,175 @@ impl EarlyLintPass for EmptyWithBrackets { } } - fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &Variant) { + fn check_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>) { + // the span of the parentheses/braces let span_after_ident = variant.span.with_lo(variant.ident.span.hi()); - if has_brackets(&variant.data) && has_no_fields(cx, &variant.data, span_after_ident) { - span_lint_and_then( + if has_no_fields(cx, &variant.data, span_after_ident) { + match variant.data { + VariantData::Struct { .. } => { + // Empty struct variants can be linted immediately + span_lint_and_then( + cx, + EMPTY_ENUM_VARIANTS_WITH_BRACKETS, + span_after_ident, + "enum variant has empty brackets", + |diagnostic| { + diagnostic.span_suggestion_hidden( + span_after_ident, + "remove the brackets", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + }, + VariantData::Tuple(.., local_def_id) => { + // Don't lint reachable tuple enums + if cx.effective_visibilities.is_reachable(variant.def_id) { + return; + } + if let Some(entry) = self.empty_tuple_enum_variants.get_mut(&local_def_id) { + // empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before the + // definition was encountered. Now that there's a definition, convert it + // to Usage::Unused. + if let Usage::NoDefinition { redundant_use_sites } = entry { + *entry = Usage::Unused { + redundant_use_sites: redundant_use_sites.clone(), + }; + } + } else { + self.empty_tuple_enum_variants.insert( + local_def_id, + Usage::Unused { + redundant_use_sites: vec![], + }, + ); + } + }, + VariantData::Unit(..) => {}, + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(def_id) = check_expr_for_enum_as_function(expr) { + if let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr) { + // Do not count expressions from macro expansion as a redundant use site. + if expr.span.from_expansion() { + return; + } + match self.empty_tuple_enum_variants.get_mut(&def_id) { + Some( + &mut (Usage::Unused { + ref mut redundant_use_sites, + } + | Usage::NoDefinition { + ref mut redundant_use_sites, + }), + ) => { + redundant_use_sites.push(parentheses_span); + }, + None => { + // The variant isn't in the IndexMap which means its definition wasn't encountered yet. + self.empty_tuple_enum_variants.insert( + def_id, + Usage::NoDefinition { + redundant_use_sites: vec![parentheses_span], + }, + ); + }, + _ => {}, + } + } else { + // The parentheses are not redundant. + self.empty_tuple_enum_variants.insert(def_id, Usage::Used); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'_>) { + for (local_def_id, usage) in &self.empty_tuple_enum_variants { + // Ignore all variants with Usage::Used or Usage::NoDefinition + let Usage::Unused { redundant_use_sites } = usage else { + continue; + }; + // Attempt to fetch the Variant from LocalDefId. + let Node::Variant(variant) = cx.tcx.hir_node( + cx.tcx + .local_def_id_to_hir_id(cx.tcx.parent(local_def_id.to_def_id()).expect_local()), + ) else { + continue; + }; + // Span of the parentheses in variant definition + let span = variant.span.with_lo(variant.ident.span.hi()); + span_lint_hir_and_then( cx, EMPTY_ENUM_VARIANTS_WITH_BRACKETS, - span_after_ident, + variant.hir_id, + span, "enum variant has empty brackets", |diagnostic| { - diagnostic.span_suggestion_hidden( - span_after_ident, - "remove the brackets", - "", - Applicability::MaybeIncorrect, - ); + if redundant_use_sites.is_empty() { + // If there's no redundant use sites, the definition is the only place to modify. + diagnostic.span_suggestion_hidden( + span, + "remove the brackets", + "", + Applicability::MaybeIncorrect, + ); + } else { + let mut parentheses_spans: Vec<_> = + redundant_use_sites.iter().map(|span| (*span, String::new())).collect(); + parentheses_spans.push((span, String::new())); + diagnostic.multipart_suggestion( + "remove the brackets", + parentheses_spans, + Applicability::MaybeIncorrect, + ); + } }, ); } } } -fn has_no_ident_token(braces_span_str: &str) -> bool { - !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) +fn has_brackets(var_data: &VariantData<'_>) -> bool { + !matches!(var_data, VariantData::Unit(..)) } -fn has_brackets(var_data: &VariantData) -> bool { - !matches!(var_data, VariantData::Unit(_)) -} - -fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { - if !var_data.fields().is_empty() { - return false; - } - +fn has_no_fields(cx: &LateContext<'_>, var_data: &VariantData<'_>, braces_span: Span) -> bool { + var_data.fields().is_empty() && // there might still be field declarations hidden from the AST // (conditionally compiled code using #[cfg(..)]) - - let Some(braces_span_str) = snippet_opt(cx, braces_span) else { - return false; - }; - - has_no_ident_token(braces_span_str.as_ref()) + !span_contains_cfg(cx, braces_span) } -#[cfg(test)] -mod unit_test { - use super::*; - - #[test] - fn test_has_no_ident_token() { - let input = "{ field: u8 }"; - assert!(!has_no_ident_token(input)); - - let input = "(u8, String);"; - assert!(!has_no_ident_token(input)); - - let input = " { - // test = 5 - } - "; - assert!(has_no_ident_token(input)); +// If expression HIR ID and callee HIR ID are same, returns the span of the parentheses, else, +// returns None. +fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Span> { + if let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + && let ExprKind::Call(callee, ..) = parent.kind + && callee.hir_id == expr.hir_id + { + Some(parent.span.with_lo(expr.span.hi())) + } else { + None + } +} - let input = " ();"; - assert!(has_no_ident_token(input)); +// Returns the LocalDefId of the variant being called as a function if it exists. +fn check_expr_for_enum_as_function(expr: &Expr<'_>) -> Option<LocalDefId> { + if let ExprKind::Path(QPath::Resolved( + _, + Path { + res: Def(Ctor(CtorOf::Variant, _), def_id), + .. + }, + )) = expr.kind + { + def_id.as_local() + } else { + None } } diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index cd9ab2764ac..72f5eaf8a4b 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -45,6 +45,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { pats.iter().all(unary_pattern) } match &pat.kind { + PatKind::Missing => unreachable!(), PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) @@ -67,6 +68,38 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T } } +/// Check if the pattern has any type mismatch that would prevent it from being used in an equality +/// check. This can happen if the expr has a reference type and the corresponding pattern is a +/// literal. +fn contains_type_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + let mut result = false; + pat.walk(|p| { + if result { + return false; + } + + if p.span.in_external_macro(cx.sess().source_map()) { + return true; + } + + let adjust_pat = match p.kind { + PatKind::Or([p, ..]) => p, + _ => p, + }; + + if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) + && adjustments.first().is_some_and(|first| first.source.is_ref()) + { + result = true; + return false; + } + + true + }); + + result +} + impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Let(let_expr) = expr.kind @@ -77,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let mut applicability = Applicability::MachineApplicable; - if is_structural_partial_eq(cx, exp_ty, pat_ty) { + if is_structural_partial_eq(cx, exp_ty, pat_ty) && !contains_type_mismatch(cx, let_expr.pat) { let pat_str = match let_expr.pat.kind { PatKind::Struct(..) => format!( "({})", diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d5f0659f842..e653a57196d 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, - numeric_literal, peel_blocks, sugg, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -435,7 +435,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind - && path.ident.name.as_str() == "exp" + && path.ident.name == sym::exp && cx.typeck_results().expr_ty(lhs).is_floating_point() && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) @@ -759,12 +759,12 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { let recv_ty = cx.typeck_results().expr_ty(receiver); if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { - match path.ident.name.as_str() { - "ln" => check_ln1p(cx, expr, receiver), - "log" => check_log_base(cx, expr, receiver, args), - "powf" => check_powf(cx, expr, receiver, args), - "powi" => check_powi(cx, expr, receiver, args), - "sqrt" => check_hypot(cx, expr, receiver), + match path.ident.name { + sym::ln => check_ln1p(cx, expr, receiver), + sym::log => check_log_base(cx, expr, receiver, args), + sym::powf => check_powf(cx, expr, receiver, args), + sym::powi => check_powi(cx, expr, receiver, args), + sym::sqrt => check_hypot(cx, expr, receiver), _ => {}, } } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 75f0d626f0c..8a3f8e1c587 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -550,7 +550,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { // a `Target` that is in `self.ty_msrv_map`. if let Some(deref_trait_id) = self.cx.tcx.lang_items().deref_trait() && implements_trait(self.cx, ty, deref_trait_id, &[]) - && let Some(target_ty) = self.cx.get_associated_type(ty, deref_trait_id, "Target") + && let Some(target_ty) = self.cx.get_associated_type(ty, deref_trait_id, sym::Target) && let Some(msrv) = self.ty_msrv_map.get(&target_ty) && msrv.is_none_or(|msrv| self.msrv.meets(self.cx, msrv)) { diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 5b42a40d850..0535ecf5240 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; +use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; +use rustc_span::Symbol; use rustc_span::symbol::kw; -use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -185,13 +185,13 @@ impl FormatImplExpr<'_, '_> { && let trait_name = match placeholder.format_trait { FormatTrait::Display => sym::Display, FormatTrait::Debug => sym::Debug, - FormatTrait::LowerExp => sym!(LowerExp), - FormatTrait::UpperExp => sym!(UpperExp), - FormatTrait::Octal => sym!(Octal), + FormatTrait::LowerExp => sym::LowerExp, + FormatTrait::UpperExp => sym::UpperExp, + FormatTrait::Octal => sym::Octal, FormatTrait::Pointer => sym::Pointer, - FormatTrait::Binary => sym!(Binary), - FormatTrait::LowerHex => sym!(LowerHex), - FormatTrait::UpperHex => sym!(UpperHex), + FormatTrait::Binary => sym::Binary, + FormatTrait::LowerHex => sym::LowerHex, + FormatTrait::UpperHex => sym::UpperHex, } && trait_name == self.format_trait_impl.name && let Ok(index) = placeholder.argument.index diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index 68cc50f3939..b64d608c0c7 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::ty::is_type_lang_item; -use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; +use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString { return; } }, - ExprKind::AssignOp(op, left, arg) if op.node == BinOpKind::Add && is_string(cx, left) => arg, + ExprKind::AssignOp(op, left, arg) if op.node == AssignOpKind::AddAssign && is_string(cx, left) => arg, _ => return, }; if is_format(cx, arg) { diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index c8828c93615..5e2e2c9dbf7 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; use clippy_utils::ty::is_c_void; +use clippy_utils::{path_def_id, sym}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -41,7 +40,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.as_str() == "from_raw" + && seg.ident.name == sym::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/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 25b087e8484..b816963cc82 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{is_in_const_context, is_integer_literal}; +use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { // check if the second part of the path indeed calls the associated // function `from_str_radix` - && pathseg.ident.name.as_str() == "from_str_radix" + && pathseg.ident.name == sym::from_str_radix // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 854fe144c29..fa63876410f 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind}; +use rustc_hir::{BlockCheckMode, Body, ExprKind, FnDecl, ImplicitSelfKind, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -40,14 +40,25 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: name }; - // Body must be &(mut) <self_data>.name + // Body must be `&(mut) <self_data>.name`, potentially in an `unsafe` block // self_data is not necessarily self, to also lint sub-getters, etc… let block_expr = if let ExprKind::Block(block, _) = body.value.kind && block.stmts.is_empty() && let Some(block_expr) = block.expr { - block_expr + if let ExprKind::Block(unsafe_block, _) = block_expr.kind + && unsafe_block.stmts.is_empty() + && matches!( + unsafe_block.rules, + BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) + && let Some(unsafe_block_expr) = unsafe_block.expr + { + unsafe_block_expr + } else { + block_expr + } } else { return; }; diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 5f3fc5100e7..d0d02a382d1 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -9,8 +9,8 @@ mod too_many_arguments; mod too_many_lines; use clippy_config::Conf; -use clippy_utils::def_path_def_ids; use clippy_utils::msrvs::Msrv; +use clippy_utils::paths::{PathNS, lookup_path_str}; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; @@ -469,7 +469,7 @@ impl Functions { trait_ids: conf .allow_renamed_params_for .iter() - .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>())) + .flat_map(|p| lookup_path_str(tcx, PathNS::Type, p)) .collect(), msrv: conf.msrv, } diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index f239bafe979..2d22bb157a9 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -22,8 +22,8 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id) && !is_from_ignored_trait(trait_ref, ignored_traits) { - let mut param_idents_iter = cx.tcx.hir_body_param_names(body_id); - let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); + let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id); + let mut default_param_idents_iter = cx.tcx.fn_arg_idents(did).iter().copied(); let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter); if !renames.0.is_empty() { diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index d2545e57652..4c17834c3ad 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -10,10 +10,10 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::Span; -use rustc_span::symbol::sym; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { @@ -326,6 +326,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if let ExprKind::Call(fun, args) = e.kind && let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind + && matches!(method.ident.name, sym::new | sym::with_capacity) && let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind && let Some(ty_did) = ty_path.res.opt_def_id() { @@ -333,10 +334,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { return; } - if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { - if method.ident.name == sym::new { + match (self.cx.tcx.get_diagnostic_name(ty_did), method.ident.name) { + (Some(sym::HashMap), sym::new) => { self.suggestions.insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashMap), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -344,11 +346,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } - } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { - if method.ident.name == sym::new { + }, + (Some(sym::HashSet), sym::new) => { self.suggestions.insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashSet), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -356,7 +358,8 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } + }, + _ => {}, } } diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 41d2b18803d..185fc2aa2d4 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_context; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{AssignOpKind, BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { && ex.span.ctxt() == ctxt && expr1.span.ctxt() == ctxt && clippy_utils::SpanlessEq::new(cx).eq_expr(l, target) - && BinOpKind::Add == op1.node + && AssignOpKind::AddAssign == op1.node && let ExprKind::Lit(lit) = value.kind && let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node && block.expr.is_none() diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 068723cebbf..0823ef53ef9 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -8,7 +8,7 @@ use clippy_utils::{ use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{AssignOpKind, BinOp, BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -69,7 +69,7 @@ declare_clippy_lint! { /// /// let result = a.saturating_sub(b); /// ``` - #[clippy::version = "1.44.0"] + #[clippy::version = "1.83.0"] pub INVERTED_SATURATING_SUB, correctness, "Check if a variable is smaller than another one and still subtract from it even if smaller" @@ -366,7 +366,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp match peel_blocks_with_stmt(expr).kind { ExprKind::AssignOp(ref op1, target, value) => { // Check if literal being subtracted is one - (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) + (AssignOpKind::SubAssign == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { if let ExprKind::Binary(ref op1, left1, right1) = value.kind diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index d02d9b2102b..6b89abdb036 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -8,7 +8,7 @@ use rustc_hir::{ }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; +use rustc_middle::ty::{self, AssocItem, ClauseKind, Generics, Ty, TyCtxt}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -315,7 +315,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) { assocs .filter_by_name_unhygienic(constraint.ident.name) .next() - .is_some_and(|assoc| assoc.kind == ty::AssocKind::Type) + .is_some_and(AssocItem::is_type) }) { emit_lint(cx, poly_trait, bounds, index, implied_constraints, bound); diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index be801d43a52..99a393b4d53 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -2,13 +2,12 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; -use clippy_utils::{higher, is_from_proc_macro, is_in_test}; +use clippy_utils::{higher, is_from_proc_macro, is_in_test, sym}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -268,7 +267,7 @@ fn ty_has_applicable_get_function<'tcx>( index_expr: &Expr<'_>, ) -> bool { if let ty::Adt(_, _) = array_ty.kind() - && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| { + && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym::get).map(|m| { cx.tcx .fn_sig(m.def_id) .skip_binder() diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 427a1f82555..c4e10837bf1 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::higher; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -156,7 +155,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name.as_str() == "flat_map" + if method.ident.name == sym::flat_map && args.len() == 1 && let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { @@ -224,7 +223,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { return MaybeInfinite.and(is_infinite(cx, receiver)); } } - if method.ident.name.as_str() == "last" && args.is_empty() { + if method.ident.name == sym::last && args.is_empty() { let not_double_ended = cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) @@ -232,7 +231,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { if not_double_ended { return is_infinite(cx, receiver); } - } else if method.ident.name.as_str() == "collect" { + } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( get_type_diagnostic_name(cx, ty), diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index b1271a264b5..3d4dcd02070 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -5,7 +5,7 @@ use clippy_utils::macros::span_is_local; use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; +use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Symbol; @@ -162,6 +162,7 @@ pub struct ItemNameRepetitions { enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, + allow_exact_repetitions: bool, allow_private_module_inception: bool, allowed_prefixes: FxHashSet<String>, } @@ -173,6 +174,7 @@ impl ItemNameRepetitions { enum_threshold: conf.enum_variant_name_threshold, struct_threshold: conf.struct_field_name_threshold, avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + allow_exact_repetitions: conf.allow_exact_repetitions, allow_private_module_inception: conf.allow_private_module_inception, allowed_prefixes: conf.allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), } @@ -405,6 +407,7 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_> if count_match_start(item_name, name).char_count == item_name_chars && name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric()) + && !check_enum_tuple_path_match(name, variant.data) { span_lint_hir( cx, @@ -420,7 +423,9 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) let name = variant.ident.name.as_str(); let item_name_chars = item_name.chars().count(); - if count_match_end(item_name, name).char_count == item_name_chars { + if count_match_end(item_name, name).char_count == item_name_chars + && !check_enum_tuple_path_match(name, variant.data) + { span_lint_hir( cx, ENUM_VARIANT_NAMES, @@ -431,6 +436,27 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) } } +/// Checks if an enum tuple variant contains a single field +/// whose qualified path contains the variant's name. +fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_>) -> bool { + // Only check single-field tuple variants + let VariantData::Tuple(fields, ..) = variant_data else { + return false; + }; + if fields.len() != 1 { + return false; + } + // Check if field type is a path and contains the variant name + match fields[0].ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => path + .segments + .iter() + .any(|segment| segment.ident.name.as_str() == variant_name), + TyKind::Path(QPath::TypeRelative(_, segment)) => segment.ident.name.as_str() == variant_name, + _ => false, + } +} + impl LateLintPass<'_> for ItemNameRepetitions { fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { let Some(_ident) = item.kind.ident() else { return }; @@ -462,11 +488,21 @@ impl LateLintPass<'_> for ItemNameRepetitions { } // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() - && cx.tcx.visibility(mod_owner_id.def_id).is_public() - && item_camel.len() > mod_camel.len() - { + // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. + + let both_are_public = + cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); + + if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + + if both_are_public && item_camel.len() > mod_camel.len() { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 173232c511a..900b20aa9cf 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_as_impl; use clippy_utils::source::snippet; use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection}; +use clippy_utils::{get_parent_as_impl, sym}; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -141,7 +140,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.as_str() == "IntoIter" { + if item.ident.name == sym::IntoIter { Some(cx.tcx.hir_impl_item(item.id).expect_type().span) } else { None diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 18bcf634097..aded31971ce 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{fulfill_or_allowed, get_item_name, get_parent_as_impl, is_trait_method, peel_ref_operators}; +use clippy_utils::{ + fulfill_or_allowed, get_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, +}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -13,10 +15,9 @@ use rustc_hir::{ QPath, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; +use rustc_middle::ty::{self, FnSig, Ty}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; use rustc_span::{Ident, Span, Symbol}; use rustc_trait_selection::traits::supertrait_def_ids; @@ -295,16 +296,10 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Iden { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx); - let is_empty = sym!(is_empty); - let is_empty_method_found = current_and_super_traits .items() - .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) - .any(|i| { - i.kind == AssocKind::Fn - && i.fn_has_self_parameter - && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1 - }); + .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(sym::is_empty)) + .any(|i| i.is_method() && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1); if !is_empty_method_found { span_lint( @@ -476,13 +471,12 @@ fn check_for_is_empty( return; }; - let is_empty = Symbol::intern("is_empty"); let is_empty = cx .tcx .inherent_impls(impl_ty) .iter() - .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) - .find(|item| item.kind == AssocKind::Fn); + .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty)) + .find(|item| item.is_fn()); let (msg, is_empty_span, self_kind) = match is_empty { None => ( @@ -502,7 +496,7 @@ fn check_for_is_empty( None, ), Some(is_empty) - if !(is_empty.fn_has_self_parameter + if !(is_empty.is_method() && check_is_empty_sig( cx, cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(), @@ -541,9 +535,7 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method - if let Some(name) = get_item_name(cx, method) - && name.as_str() == "is_empty" - { + if parent_item_name(cx, method) == Some(sym::is_empty) { return; } @@ -626,7 +618,7 @@ fn is_empty_array(expr: &Expr<'_>) -> bool { fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { - if item.kind == AssocKind::Fn { + if item.is_fn() { let sig = cx.tcx.fn_sig(item.def_id).skip_binder(); let ty = sig.skip_binder(); ty.inputs().len() == 1 @@ -637,11 +629,10 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { - let is_empty = sym!(is_empty); cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) - .filter_by_name_unhygienic(is_empty) + .filter_by_name_unhygienic(sym::is_empty) .any(|item| is_is_empty(cx, item)) }) } @@ -649,10 +640,9 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { match ty.kind() { ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { - let is_empty = sym!(is_empty); cx.tcx .associated_items(principal.def_id()) - .filter_by_name_unhygienic(is_empty) + .filter_by_name_unhygienic(sym::is_empty) .any(|item| is_is_empty(cx, item)) }), ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), @@ -662,7 +652,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { implements_trait(cx, ty, deref_id, &[]) && cx - .get_associated_type(ty, deref_id, "Target") + .get_associated_type(ty, deref_id, sym::Target) .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) })) }, diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index bdbf5b37c5f..916191b2a7b 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; +use clippy_utils::ty::{implements_trait, is_must_use_ty}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; use rustc_hir::{LetStmt, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -129,12 +129,6 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]); -const SYNC_GUARD_PATHS: [&[&str]; 3] = [ - &paths::PARKING_LOT_MUTEX_GUARD, - &paths::PARKING_LOT_RWLOCK_READ_GUARD, - &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, -]; - impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if matches!(local.source, LocalSource::Normal) @@ -144,7 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)), + GenericArgKind::Type(inner_ty) => inner_ty + .ty_adt_def() + .is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); if contains_sync_guard { diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 9c8488ff381..1917ca24a05 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_errors::Applicability; use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,13 +33,19 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { && !local.span.in_external_macro(cx.tcx.sess.source_map()) && !is_from_proc_macro(cx, ty) { - span_lint_and_help( + span_lint_and_then( cx, LET_WITH_TYPE_UNDERSCORE, local.span, "variable declared with type underscore", - Some(ty.span.with_lo(local.pat.span.hi())), - "remove the explicit type `_` declaration", + |diag| { + diag.span_suggestion_verbose( + ty.span.with_lo(local.pat.span.hi()), + "remove the explicit type `_` declaration", + "", + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9e1df7881ce..006145cc623 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,4 @@ #![feature(array_windows)] -#![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(macro_metavar_expr_concat)] #![feature(f128)] @@ -7,7 +6,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(rustc_private)] @@ -65,7 +63,6 @@ mod declare_clippy_lint; #[macro_use] extern crate clippy_utils; -#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; pub mod ctfe; // Very important lint, do not remove (rust#125116) @@ -97,6 +94,7 @@ mod cargo; mod casts; mod cfg_not_test; mod checked_conversions; +mod cloned_ref_to_slice_refs; mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; @@ -320,6 +318,7 @@ mod redundant_locals; mod redundant_pub_crate; mod redundant_slicing; mod redundant_static_lifetimes; +mod redundant_test_prefix; mod redundant_type_annotations; mod ref_option_ref; mod ref_patterns; @@ -412,21 +411,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; use utils::attr_collector::{AttrCollector, AttrStorage}; -/// Register all pre expansion lints -/// -/// Pre-expansion lints run before any macro expansion has happened. -/// -/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate -/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. -/// -/// Used in `./src/driver.rs`. -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)] struct RegistrationGroups { all: Vec<LintId>, @@ -439,8 +423,6 @@ struct RegistrationGroups { restriction: Vec<LintId>, style: Vec<LintId>, suspicious: Vec<LintId>, - #[cfg(feature = "internal")] - internal: Vec<LintId>, } impl RegistrationGroups { @@ -456,8 +438,6 @@ impl RegistrationGroups { store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction); store.register_group(true, "clippy::style", Some("clippy_style"), self.style); store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); - #[cfg(feature = "internal")] - store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal); } } @@ -472,8 +452,6 @@ pub(crate) enum LintCategory { Restriction, Style, Suspicious, - #[cfg(feature = "internal")] - Internal, } #[allow(clippy::enum_glob_use)] @@ -495,8 +473,6 @@ impl LintCategory { Restriction => &mut groups.restriction, Style => &mut groups.style, Suspicious => &mut groups.suspicious, - #[cfg(feature = "internal")] - Internal => &mut groups.internal, } } } @@ -530,8 +506,6 @@ impl LintInfo { Restriction => "restriction", Style => "style", Suspicious => "suspicious", - #[cfg(feature = "internal")] - Internal => "internal", } } } @@ -589,6 +563,13 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_removed(name, reason); } + // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. + // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate + // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); + + store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); + let format_args_storage = FormatArgsStorage::default(); let format_args = format_args_storage.clone(); store.register_early_pass(move || { @@ -601,30 +582,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { let attrs = attr_storage.clone(); store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone()))); - // all the internal lints - #[cfg(feature = "internal")] - { - store.register_early_pass(|| { - Box::new(utils::internal_lints::unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths) - }); - store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); - store.register_late_pass(|_| { - Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default() - }); - store.register_late_pass(|_| { - Box::<utils::internal_lints::lint_without_lint_pass::LintWithoutLintPass>::default() - }); - store.register_late_pass(|_| Box::<utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath>::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); - 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)); store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))); @@ -771,8 +728,9 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints)); store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); + store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf))); + store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); @@ -789,7 +747,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); - store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); + store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))); store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))); store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); @@ -857,7 +815,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone()))); store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf))); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); - store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets)); + store.register_late_pass(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); @@ -971,7 +929,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); - store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); + store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); @@ -984,5 +942,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); + store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); + store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 9b13dfbd0e9..9a64226b1ed 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -14,7 +14,7 @@ use rustc_hir::intravisit::{ }; use rustc_hir::{ AmbigArg, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, - Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, + Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeKind, LifetimeParamKind, Node, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, WherePredicateKind, lang_items, }; @@ -88,7 +88,7 @@ declare_clippy_lint! { /// x.chars() /// } /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.87.0"] pub ELIDABLE_LIFETIME_NAMES, pedantic, "lifetime name that can be replaced with the anonymous lifetime" @@ -218,7 +218,7 @@ fn check_fn_inner<'tcx>( for bound in pred.bounds { let mut visitor = RefVisitor::new(cx); walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { + if visitor.lts.iter().any(|lt| matches!(lt.kind, LifetimeKind::Param(_))) { return; } if let GenericBound::Trait(ref trait_ref) = *bound { @@ -235,7 +235,7 @@ fn check_fn_inner<'tcx>( _ => None, }); for bound in lifetimes { - if bound.res != LifetimeName::Static && !bound.is_elided() { + if bound.kind != LifetimeKind::Static && !bound.is_elided() { return; } } @@ -314,7 +314,7 @@ fn could_use_elision<'tcx>( return None; } - let mut checker = BodyLifetimeChecker; + let mut checker = BodyLifetimeChecker::new(cx); if checker.visit_expr(body.value).is_break() { return None; } @@ -421,8 +421,8 @@ fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { } fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> { - match lt.res { - LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + match lt.kind { + LifetimeKind::Param(id) if !lt.is_anonymous() => Some(id), _ => None, } } @@ -614,7 +614,7 @@ where // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if let LifetimeName::Param(def_id) = lifetime.res + if let LifetimeKind::Param(def_id) = lifetime.kind && let Some(usages) = self.map.get_mut(&def_id) { usages.push(Usage { @@ -826,7 +826,7 @@ fn report_elidable_lifetimes( .iter() .map(|<| cx.tcx.def_span(lt)) .chain(usages.iter().filter_map(|usage| { - if let LifetimeName::Param(def_id) = usage.res + if let LifetimeKind::Param(def_id) = usage.kind && elidable_lts.contains(&def_id) { return Some(usage.ident.span); @@ -911,10 +911,23 @@ fn elision_suggestions( Some(suggestions) } -struct BodyLifetimeChecker; +struct BodyLifetimeChecker<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> BodyLifetimeChecker<'tcx> { + fn new(cx: &LateContext<'tcx>) -> Self { + Self { tcx: cx.tcx } + } +} -impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { +impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker<'tcx> { type Result = ControlFlow<()>; + type NestedFilter = middle_nested_filter::OnlyBodies; + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.tcx + } // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) -> ControlFlow<()> { if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime { diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 412c78cc804..d0b26c91ffa 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -2,6 +2,7 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{ implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, @@ -11,7 +12,6 @@ use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, EarlyBinder, Ty}; -use rustc_span::sym; pub(super) fn check( cx: &LateContext<'_>, @@ -119,7 +119,7 @@ fn is_ref_iterable<'tcx>( && let typing_env = ty::TypingEnv::non_body_analysis(cx.tcx, fn_id) && implements_trait_with_env(cx.tcx, typing_env, req_self_ty, trait_id, Some(fn_id), &[]) && let Some(into_iter_ty) = - make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym!(IntoIter), [req_self_ty]) + make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym::IntoIter, [req_self_ty]) && let req_res_ty = normalize_with_regions(cx.tcx, typing_env, req_res_ty) && into_iter_ty == req_res_ty { @@ -152,7 +152,7 @@ fn is_ref_iterable<'tcx>( // Using by value won't consume anything if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::None, self_ty)); @@ -169,7 +169,7 @@ fn is_ref_iterable<'tcx>( }; if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::reborrow(mutbl), self_ty)); @@ -183,7 +183,7 @@ fn is_ref_iterable<'tcx>( let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, self_ty, mutbl); if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::borrow(mutbl), self_ty)); @@ -206,7 +206,7 @@ fn is_ref_iterable<'tcx>( && target != self_ty && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::auto_reborrow(mutbl), target)) @@ -224,7 +224,7 @@ fn is_ref_iterable<'tcx>( if is_copy(cx, target) && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::Deref, target)) @@ -242,7 +242,7 @@ fn is_ref_iterable<'tcx>( if self_ty.is_ref() && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::auto_borrow(mutbl), target)) diff --git a/clippy_lints/src/loops/manual_slice_fill.rs b/clippy_lints/src/loops/manual_slice_fill.rs index 343f7c5d2d1..15c656cc7bc 100644 --- a/clippy_lints/src/loops/manual_slice_fill.rs +++ b/clippy_lints/src/loops/manual_slice_fill.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{HasSession, snippet_with_applicability}; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_slice_like}; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; use rustc_ast::ast::LitKind; @@ -58,6 +58,8 @@ pub(super) fn check<'tcx>( && let Res::Local(idx_hir) = idx_path.res && !is_local_used(cx, assignval, idx_hir) && msrv.meets(cx, msrvs::SLICE_FILL) + && let slice_ty = cx.typeck_results().expr_ty(slice).peel_refs() + && is_slice_like(cx, slice_ty) { sugg(cx, body, expr, slice.span, assignval.span); } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 2b66827e82e..56d2bef2305 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -778,7 +778,7 @@ declare_clippy_lint! { /// let _ = s[idx..]; /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.88.0"] pub CHAR_INDICES_AS_BYTE_INDICES, correctness, "using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected" diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 661b4b590d8..388034c39f5 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{msrvs, path_to_local, std_or_core}; +use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -11,7 +11,6 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::SyntaxContext; -use rustc_span::symbol::sym; /// Detects for loop pushing the same item into a Vec pub(super) fn check<'tcx>( @@ -187,8 +186,8 @@ fn get_vec_push<'tcx>( // Extract method being called and figure out the parameters for the method call && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind // Check that the method being called is push() on a Vec + && path.ident.name == sym::push && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) - && path.ident.name.as_str() == "push" { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 468c27b508b..2f6950b4380 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -3,7 +3,7 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; +use rustc_hir::{AssignOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -58,7 +58,7 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { match parent.kind { ExprKind::AssignOp(op, lhs, rhs) => { if lhs.hir_id == expr.hir_id { - *state = if op.node == BinOpKind::Add + *state = if op.node == AssignOpKind::AddAssign && is_integer_const(self.cx, rhs, 1) && *state == IncrementVisitorVarState::Initial && self.depth == 0 diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index f026e0fc875..9071c9c95f9 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -6,6 +6,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; +use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext, sym}; use std::collections::BTreeMap; @@ -250,7 +251,12 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { .flatten() .copied() .inspect(|&unsafe_block| { - if let Level::Expect(id) = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block).0 { + if let LevelAndSource { + level: Level::Expect, + lint_id: Some(id), + .. + } = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block) + { // Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon, // which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out // except for the one we emit the warning at, we must manually fulfill the lint diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index b712b351d06..98ad1f6a160 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -153,9 +153,15 @@ impl LateLintPass<'_> for MacroUseImports { [] | [_] => return, [root, item] => { if !check_dup.contains(&(*item).to_string()) { - used.entry(((*root).to_string(), span, hir_id)) - .or_insert_with(Vec::new) - .push((*item).to_string()); + used.entry(( + (*root).to_string(), + span, + hir_id.local_id, + cx.tcx.def_path_hash(hir_id.owner.def_id.into()), + )) + .or_insert_with(|| (vec![], hir_id)) + .0 + .push((*item).to_string()); check_dup.push((*item).to_string()); } }, @@ -171,15 +177,27 @@ impl LateLintPass<'_> for MacroUseImports { } }) .collect::<Vec<_>>(); - used.entry(((*root).to_string(), span, hir_id)) - .or_insert_with(Vec::new) - .push(filtered.join("::")); + used.entry(( + (*root).to_string(), + span, + hir_id.local_id, + cx.tcx.def_path_hash(hir_id.owner.def_id.into()), + )) + .or_insert_with(|| (vec![], hir_id)) + .0 + .push(filtered.join("::")); check_dup.extend(filtered); } else { let rest = rest.to_vec(); - used.entry(((*root).to_string(), span, hir_id)) - .or_insert_with(Vec::new) - .push(rest.join("::")); + used.entry(( + (*root).to_string(), + span, + hir_id.local_id, + cx.tcx.def_path_hash(hir_id.owner.def_id.into()), + )) + .or_insert_with(|| (vec![], hir_id)) + .0 + .push(rest.join("::")); check_dup.extend(rest.iter().map(ToString::to_string)); } }, @@ -190,7 +208,7 @@ impl LateLintPass<'_> for MacroUseImports { // If mac_refs is not empty we have encountered an import we could not handle // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { - for ((root, span, hir_id), path) in used { + for ((root, span, ..), (path, hir_id)) in used { let import = if let [single] = &path[..] { format!("{root}::{single}") } else { diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index c515e41f242..bac4b3d32f2 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// a.abs_diff(b) /// # ; /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.88.0"] pub MANUAL_ABS_DIFF, complexity, "using an if-else pattern instead of `abs_diff`" diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index 70db403e103..ed0cce754b9 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -1,8 +1,9 @@ -use clippy_utils::SpanlessEq; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -11,9 +12,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self}; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; - -use clippy_config::Conf; declare_clippy_lint! { /// ### What it does @@ -141,8 +139,7 @@ fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.peel_refs().kind() { ty::Uint(_) => true, - ty::Int(_) => cx.tcx.features().enabled(Symbol::intern("int_roundings")), - + ty::Int(_) => cx.tcx.features().enabled(sym::int_roundings), _ => false, } } @@ -202,9 +199,9 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); + let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index f71264a93ca..b3ee45cc020 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -3,12 +3,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, path_to_local_id}; +use clippy_utils::{is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -66,7 +65,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.as_str() == "build_hasher" + && seg.ident.name == sym::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) @@ -94,7 +93,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.as_str() == "finish" + && seg.ident.name == sym::finish && self.msrv.meets(cx, msrvs::BUILD_HASHER_HASH_ONE) { diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index d92069edb6d..57c03fbb2ed 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,6 +1,7 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -10,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{Ty, UintTy}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -47,9 +48,9 @@ enum MatchType<'a, 'b> { fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> { if let MethodCall(path, expr, _, _) = kind { - let is_lower = match path.ident.name.as_str() { - "to_ascii_lowercase" => true, - "to_ascii_uppercase" => false, + let is_lower = match path.ident.name { + sym::to_ascii_lowercase => true, + sym::to_ascii_uppercase => false, _ => return None, }; let ty_raw = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 8ab49bd2ea8..ac8c88f0205 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; +use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -103,7 +103,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.as_str() == "contains" + && path.ident.name == sym::contains && let Some(higher::Range { start: Some(start), end: Some(end), diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs index 841adfec462..b4cd988329d 100644 --- a/clippy_lints/src/manual_is_power_of_two.rs +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -1,13 +1,14 @@ -use clippy_utils::SpanlessEq; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use rustc_ast::LitKind; -use rustc_data_structures::packed::Pu128; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::ty_from_hir_ty; +use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Uint; -use rustc_session::declare_lint_pass; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -33,112 +34,111 @@ declare_clippy_lint! { "manually reimplementing `is_power_of_two`" } -declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); +pub struct ManualIsPowerOfTwo { + msrv: Msrv, +} -impl LateLintPass<'_> for ManualIsPowerOfTwo { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let mut applicability = Applicability::MachineApplicable; +impl_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); - if let ExprKind::Binary(bin_op, left, right) = expr.kind - && bin_op.node == BinOpKind::Eq - { - // a.count_ones() == 1 - if let ExprKind::MethodCall(method_name, receiver, [], _) = left.kind - && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() - && check_lit(right, 1) - { - build_sugg(cx, expr, receiver, &mut applicability); - } +impl ManualIsPowerOfTwo { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } - // 1 == a.count_ones() - if let ExprKind::MethodCall(method_name, receiver, [], _) = right.kind - && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() - && check_lit(left, 1) - { - build_sugg(cx, expr, receiver, &mut applicability); - } + fn build_sugg(&self, cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { + if is_in_const_context(cx) && !self.msrv.meets(cx, msrvs::CONST_IS_POWER_OF_TWO) { + return; + } - // a & (a - 1) == 0 - if let ExprKind::Binary(op1, left1, right1) = left.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = right1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, left1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() - && check_lit(right2, 1) - && check_lit(right, 0) - { - build_sugg(cx, expr, left1, &mut applicability); - } + let mut applicability = Applicability::MachineApplicable; + let snippet = Sugg::hir_with_applicability(cx, receiver, "_", &mut applicability); - // (a - 1) & a == 0; - if let ExprKind::Binary(op1, left1, right1) = left.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = left1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, right1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() - && check_lit(right2, 1) - && check_lit(right, 0) - { - build_sugg(cx, expr, right1, &mut applicability); - } + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + format!("{}.is_power_of_two()", snippet.maybe_paren()), + applicability, + ); + } +} - // 0 == a & (a - 1); - if let ExprKind::Binary(op1, left1, right1) = right.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = right1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, left1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() - && check_lit(right2, 1) - && check_lit(left, 0) +impl<'tcx> LateLintPass<'tcx> for ManualIsPowerOfTwo { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if !expr.span.from_expansion() + && let Some((lhs, rhs)) = unexpanded_binop_operands(expr, BinOpKind::Eq) + { + if let Some(a) = count_ones_receiver(cx, lhs) + && is_integer_literal(rhs, 1) { - build_sugg(cx, expr, left1, &mut applicability); - } - - // 0 == (a - 1) & a - if let ExprKind::Binary(op1, left1, right1) = right.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = left1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, right1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() - && check_lit(right2, 1) - && check_lit(left, 0) + self.build_sugg(cx, expr, a); + } else if let Some(a) = count_ones_receiver(cx, rhs) + && is_integer_literal(lhs, 1) + { + self.build_sugg(cx, expr, a); + } else if is_integer_literal(rhs, 0) + && let Some(a) = is_and_minus_one(cx, lhs) + { + self.build_sugg(cx, expr, a); + } else if is_integer_literal(lhs, 0) + && let Some(a) = is_and_minus_one(cx, rhs) { - build_sugg(cx, expr, right1, &mut applicability); + self.build_sugg(cx, expr, a); } } } } -fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, applicability: &mut Applicability) { - let snippet = snippet_with_applicability(cx, receiver.span, "..", applicability); - - span_lint_and_sugg( - cx, - MANUAL_IS_POWER_OF_TWO, - expr.span, - "manually reimplementing `is_power_of_two`", - "consider using `.is_power_of_two()`", - format!("{snippet}.is_power_of_two()"), - *applicability, - ); +/// Return the unsigned integer receiver of `.count_ones()` or the argument of +/// `<int-type>::count_ones(…)`. +fn count_ones_receiver<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + let (method, ty, receiver) = if let ExprKind::MethodCall(method_name, receiver, [], _) = expr.kind { + (method_name, cx.typeck_results().expr_ty_adjusted(receiver), receiver) + } else if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, func_name)) = func.kind + { + (func_name, ty_from_hir_ty(cx, ty), arg) + } else { + return None; + }; + (method.ident.as_str() == "count_ones" && matches!(ty.kind(), ty::Uint(_))).then_some(receiver) } -fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool { - if let ExprKind::Lit(lit) = expr.kind - && let LitKind::Int(Pu128(num), _) = lit.node - && num == expected_num +/// Return `greater` if `smaller == greater - 1` +fn is_one_less<'tcx>( + cx: &LateContext<'tcx>, + greater: &'tcx Expr<'tcx>, + smaller: &Expr<'tcx>, +) -> Option<&'tcx Expr<'tcx>> { + if let Some((lhs, rhs)) = unexpanded_binop_operands(smaller, BinOpKind::Sub) + && SpanlessEq::new(cx).eq_expr(greater, lhs) + && is_integer_literal(rhs, 1) + && matches!(cx.typeck_results().expr_ty_adjusted(greater).kind(), ty::Uint(_)) { - return true; + Some(greater) + } else { + None } - false } -fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { - SpanlessEq::new(cx).eq_expr(lhs, rhs) +/// Return `v` if `expr` is `v & (v - 1)` or `(v - 1) & v` +fn is_and_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + let (lhs, rhs) = unexpanded_binop_operands(expr, BinOpKind::BitAnd)?; + is_one_less(cx, lhs, rhs).or_else(|| is_one_less(cx, rhs, lhs)) +} + +/// Return the operands of the `expr` binary operation if the operator is `op` and none of the +/// operands come from expansion. +fn unexpanded_binop_operands<'hir>(expr: &Expr<'hir>, op: BinOpKind) -> Option<(&'hir Expr<'hir>, &'hir Expr<'hir>)> { + if let ExprKind::Binary(binop, lhs, rhs) = expr.kind + && binop.node == op + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + Some((lhs, rhs)) + } else { + None + } } diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index d6ac6e106b4..0b3bec714c0 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -4,11 +4,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; +use clippy_utils::{ + MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, +}; use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; @@ -91,14 +94,15 @@ impl<'tcx> QuestionMark { let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + + let pat_arm = &arms[1 - idx]; // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. // However, if it arrives in second position, its pattern may cover some cases already covered // by the diverging one. - // TODO: accept the non-diverging arm as a second position if patterns are disjointed. - if idx == 0 { + if idx == 0 && !is_arms_disjointed(cx, diverging_arm, pat_arm) { return; } - let pat_arm = &arms[1 - idx]; + let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else { return; }; @@ -110,6 +114,63 @@ impl<'tcx> QuestionMark { } } +/// Checks if the patterns of the arms are disjointed. Currently, we only support patterns of simple +/// enum variants without nested patterns or bindings. +/// +/// TODO: Support more complex patterns. +fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> bool { + if arm1.guard.is_some() || arm2.guard.is_some() { + return false; + } + + if !is_enum_variant(cx, arm1.pat) || !is_enum_variant(cx, arm2.pat) { + return false; + } + + true +} + +/// Returns `true` if the given pattern is a variant of an enum. +pub fn is_enum_variant(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + struct Pat<'hir>(&'hir rustc_hir::Pat<'hir>); + + impl<'hir> MaybePath<'hir> for Pat<'hir> { + fn qpath_opt(&self) -> Option<&QPath<'hir>> { + match self.0.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::Expr(&PatExpr { + kind: PatExprKind::Path(ref qpath), + .. + }) => Some(qpath), + _ => None, + } + } + + fn hir_id(&self) -> HirId { + self.0.hir_id + } + } + + let res = path_res(cx, &Pat(pat)); + matches!( + res, + Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) + ) +} + fn emit_manual_let_else( cx: &LateContext<'_>, span: Span, diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index e4ad3953b67..b55c11f2d5b 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs}; +use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -76,30 +76,30 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } }, ExprKind::MethodCall(seg, callee, [], _) => { - if seg.ident.name.as_str() == "unwrap_or_default" { + if seg.ident.name == sym::unwrap_or_default { check_map(cx, callee, span, self.msrv); } }, - ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() { - "unwrap_or" => { + ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name { + sym::unwrap_or => { if is_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, - "unwrap_or_else" => { + sym::unwrap_or_else => { if returns_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, _ => {}, }, - ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() { - "map_or" => { + ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name { + sym::map_or => { if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } }, - "map_or_else" => { + sym::map_or_else => { if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } @@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"]) + paths::SLICE_FROM_REF.matches_path(cx, expr) } diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index 02f69b34af1..4959908dad6 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -85,7 +85,7 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool /// contains `Err(IDENT)`, `None` otherwise. fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'hir Ident)> { if let PatKind::TupleStruct(qpath, [arg], _) = &pat.kind - && let PatKind::Binding(BindingMode::NONE, _, ident, _) = &arg.kind + && let PatKind::Binding(BindingMode::NONE, _, ident, None) = &arg.kind && let res = cx.qpath_res(qpath, pat.hir_id) && let Res::Def(DefKind::Ctor(..), id) = res && let id @ Some(_) = cx.tcx.opt_parent(id) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index b64ae0b24d8..3ac2c9fc2b3 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath}; @@ -16,7 +17,7 @@ use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<HirId> { if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind - && let PatKind::Binding(_, pat_id, _, _) = pat.kind + && let PatKind::Binding(BindingMode(ByRef::No, _), pat_id, _, _) = pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match // against it. diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 250f17fa902..dbb29ee776b 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; +use itertools::Itertools; use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -110,57 +111,68 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { && check_same_body() }; - let mut appl = Applicability::MaybeIncorrect; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { - if matches!(arm2.pat.kind, PatKind::Wild) { - if !cx.tcx.features().non_exhaustive_omitted_patterns_lint() - || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id) - { - let arm_span = adjusted_arm_span(cx, arm1.span); - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - arm1.hir_id, - arm_span, - "this match arm has an identical body to the `_` wildcard arm", - |diag| { - diag.span_suggestion(arm_span, "try removing the arm", "", appl) - .help("or try changing either arm body") - .span_note(arm2.span, "`_` wildcard arm here"); - }, - ); - } - } else { - let back_block = backwards_blocking_idxs[j]; - let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) { - (arm1, arm2) - } else { - (arm2, arm1) - }; - - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - keep_arm.hir_id, - keep_arm.span, - "this match arm has an identical body to another arm", - |diag| { - let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "<pat2>", &mut appl); - let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "<pat1>", &mut appl); + for mut group in search_same(&indexed_arms, hash, eq) { + // Filter out (and fulfill) `#[allow]`ed and `#[expect]`ed arms + group.retain(|(_, arm)| !fulfill_or_allowed(cx, MATCH_SAME_ARMS, [arm.hir_id])); - diag.multipart_suggestion( - "or try merging the arm patterns and removing the obsolete arm", - vec![ - (keep_arm.pat.span, format!("{keep_pat_snip} | {move_pat_snip}")), - (adjusted_arm_span(cx, move_arm.span), String::new()), - ], - appl, - ) - .help("try changing either arm body"); - }, - ); + if group.len() < 2 { + continue; } + + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + group.iter().map(|(_, arm)| arm.span).collect_vec(), + "these match arms have identical bodies", + |diag| { + diag.help("if this is unintentional make the arms return different values"); + + if let [prev @ .., (_, last)] = group.as_slice() + && is_wildcard_arm(last.pat) + && is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, last.hir_id) + { + diag.span_label(last.span, "the wildcard arm"); + + let s = if prev.len() > 1 { "s" } else { "" }; + diag.multipart_suggestion_verbose( + format!("otherwise remove the non-wildcard arm{s}"), + prev.iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect(), + Applicability::MaybeIncorrect, + ); + } else if let &[&(first_idx, _), .., &(last_idx, _)] = group.as_slice() { + let back_block = backwards_blocking_idxs[last_idx]; + let split = if back_block < first_idx + || (back_block == 0 && forwards_blocking_idxs[first_idx] <= last_idx) + { + group.split_first() + } else { + group.split_last() + }; + + if let Some(((_, dest), src)) = split + && let Some(pat_snippets) = group + .iter() + .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .collect::<Option<Vec<_>>>() + { + let mut suggs = src + .iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect_vec(); + + suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); + diag.multipart_suggestion_verbose( + "otherwise merge the patterns into a single arm", + suggs, + Applicability::MaybeIncorrect, + ); + } + } + }, + ); } } @@ -253,6 +265,7 @@ fn iter_matching_struct_fields<'a>( impl<'a> NormalizedPat<'a> { fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { + PatKind::Missing => unreachable!(), PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) @@ -449,3 +462,11 @@ fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool { pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.swap_remove(&id)); result && ids.is_empty() } + +fn is_wildcard_arm(pat: &Pat<'_>) -> bool { + match pat.kind { + PatKind::Wild => true, + PatKind::Or([.., last]) => matches!(last.kind, PatKind::Wild), + _ => false, + } +} diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index bca41956f1c..adda3586990 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::HirNode; -use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability}; +use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_context}; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind}; @@ -24,16 +24,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let bind_names = arms[0].pat.span; let match_body = peel_blocks(arms[0].body); let mut app = Applicability::MaybeIncorrect; - let mut snippet_body = snippet_block_with_context( - cx, - match_body.span, - arms[0].span.ctxt(), - "..", - Some(expr.span), - &mut app, - ) - .0 - .to_string(); + let ctxt = expr.span.ctxt(); + let mut snippet_body = snippet_block_with_context(cx, match_body.span, ctxt, "..", Some(expr.span), &mut app) + .0 + .to_string(); // Do we need to add ';' to suggestion ? if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) @@ -77,10 +71,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e span, format!( "let {} = {};\n{}let {} = {snippet_body};", - snippet_with_applicability(cx, bind_names, "..", &mut app), - snippet_with_applicability(cx, matched_vars, "..", &mut app), + snippet_with_context(cx, bind_names, ctxt, "..", &mut app).0, + snippet_with_context(cx, matched_vars, ctxt, "..", &mut app).0, " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, pat_span, "..", &mut app) + snippet_with_context(cx, pat_span, ctxt, "..", &mut app).0 ), ), None => { @@ -204,14 +198,17 @@ fn sugg_with_curlies<'a>( s }); + let ctxt = match_expr.span.ctxt(); let scrutinee = if needs_var_binding { format!( "let {} = {}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) + snippet_with_context(cx, bind_names, ctxt, "..", applicability).0, + snippet_with_context(cx, matched_vars, ctxt, "..", applicability).0 ) } else { - snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + snippet_with_context(cx, matched_vars, ctxt, "..", applicability) + .0 + .to_string() }; format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c4d51a5946e..db20be40f27 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -4,7 +4,7 @@ use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; -use clippy_utils::{higher, is_expn_of, is_trait_method}; +use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; @@ -12,7 +12,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use std::fmt::Write; use std::ops::ControlFlow; @@ -138,9 +138,9 @@ fn find_method_and_type<'tcx>( Some(("is_some()", op_ty)) } else if Some(id) == lang_items.poll_ready_variant() { Some(("is_ready()", op_ty)) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V4))) { + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym::V4)) { Some(("is_ipv4()", op_ty)) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V6))) { + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym::V6)) { Some(("is_ipv6()", op_ty)) } else { None @@ -345,8 +345,8 @@ fn found_good_method<'tcx>( arms, path_left, path_right, - Item::Diag(sym::IpAddr, sym!(V4)), - Item::Diag(sym::IpAddr, sym!(V6)), + Item::Diag(sym::IpAddr, sym::V4), + Item::Diag(sym::IpAddr, sym::V6), "is_ipv4()", "is_ipv6()", ) @@ -437,8 +437,8 @@ fn get_good_method<'tcx>( "None" => (Item::Lang(OptionNone), "is_none()", "is_some()"), "Ready" => (Item::Lang(PollReady), "is_ready()", "is_pending()"), "Pending" => (Item::Lang(PollPending), "is_pending()", "is_ready()"), - "V4" => (Item::Diag(sym::IpAddr, sym!(V4)), "is_ipv4()", "is_ipv6()"), - "V6" => (Item::Diag(sym::IpAddr, sym!(V6)), "is_ipv6()", "is_ipv4()"), + "V4" => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), + "V6" => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), _ => return None, }; return find_good_method_for_matches_macro( diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 735ba63eb77..08c0caa4266 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -407,6 +407,7 @@ impl<'a> PatState<'a> { pats.iter().map(|p| p.pat), ), + PatKind::Missing => unreachable!(), PatKind::Wild | PatKind::Binding(_, _, _, None) | PatKind::Expr(_) diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index a54d835b538..28efd2038b3 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { /// let mut an_option = Some(0); /// let taken = an_option.replace(1); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MEM_REPLACE_OPTION_WITH_SOME, style, "replacing an `Option` with `Some` instead of `replace()`" diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index d07870d4951..292fa08b598 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::sym; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -21,8 +22,8 @@ pub(super) fn check<'tcx>( ) { if let ExprKind::MethodCall(path_segment, ..) = recv.kind && matches!( - path_segment.ident.name.as_str(), - "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" + path_segment.ident.name, + sym::to_lowercase | sym::to_uppercase | sym::to_ascii_lowercase | sym::to_ascii_uppercase ) { return; diff --git a/clippy_lints/src/methods/double_ended_iterator_last.rs b/clippy_lints/src/methods/double_ended_iterator_last.rs index e82211bbf3e..6d841853fbe 100644 --- a/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_mutable, is_trait_method, path_to_local}; +use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; +use clippy_utils::{is_mutable, is_trait_method, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Instance; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::DOUBLE_ENDED_ITERATOR_LAST; @@ -24,13 +24,18 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) // find the provided definition of Iterator::last && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) - && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last") + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name() == sym::last) // if the resolved method is the same as the provided definition && fn_def.def_id() == last_def.def_id + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && !has_non_owning_mutable_access(cx, self_ty) { let mut sugg = vec![(call_span, String::from("next_back()"))]; let mut dont_apply = false; + // if `self_expr` is a reference, it is mutable because it is used for `.last()` + // TODO: Change this to lint only when the referred iterator is not used later. If it is used later, + // changing to `next_back()` may change its behavior. if !(is_mutable(cx, self_expr) || self_type.is_ref()) { if let Some(hir_id) = path_to_local(self_expr) && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index ae300cd5fe5..4dd54cf1974 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks}; +use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::symbol::{Ident, Symbol}; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP}; @@ -43,10 +43,10 @@ fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool } fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { - is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym::is_some) } fn is_ok_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { - is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_ok)) + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym::is_ok) } #[derive(Debug, Copy, Clone)] @@ -233,12 +233,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.as_str() == "is_some" { + if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::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.as_str() == "is_ok" { + } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok { Some(Self::IsOk { receiver, side_effect_expr_span, @@ -429,16 +429,15 @@ fn is_find_or_filter<'a>( } fn acceptable_methods(method: &PathSegment<'_>) -> bool { - let methods: [Symbol; 8] = [ - sym::clone, - sym::as_ref, - sym!(copied), - sym!(cloned), - sym!(as_deref), - sym!(as_mut), - sym!(as_deref_mut), - sym!(to_owned), - ]; - - methods.contains(&method.ident.name) + matches!( + method.ident.name, + sym::clone + | sym::as_ref + | sym::copied + | sym::cloned + | sym::as_deref + | sym::as_mut + | sym::as_deref_mut + | sym::to_owned + ) } diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 1f26b44767b..045363058d1 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,25 +1,31 @@ +use std::fmt::Write as _; + use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::{self as hir, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::GenericParamDefKind; use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>], func: &Expr<'_>) { if is_path_diagnostic_item(cx, func, sym::from_iter_fn) - && let ty = cx.typeck_results().expr_ty(expr) && let arg_ty = cx.typeck_results().expr_ty(&args[0]) && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && implements_trait(cx, arg_ty, iter_id, &[]) { - // `expr` implements `FromIterator` trait + let mut app = Applicability::MaybeIncorrect; + let turbofish = match func.kind { + ExprKind::Path(QPath::TypeRelative(hir_ty, _)) => build_full_type(cx, hir_ty, &mut app), + ExprKind::Path(QPath::Resolved(Some(self_ty), _)) => build_full_type(cx, self_ty, &mut app), + _ => return, + }; let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_paren(); - let turbofish = extract_turbofish(cx, expr, ty); let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); span_lint_and_sugg( cx, @@ -28,54 +34,47 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", sugg, - Applicability::MaybeIncorrect, + app, ); } } -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String { - fn strip_angle_brackets(s: &str) -> Option<&str> { - s.strip_prefix('<')?.strip_suffix('>') - } - - let call_site = expr.span.source_callsite(); - if let Some(snippet) = call_site.get_source_text(cx) - && let snippet_split = snippet.split("::").collect::<Vec<_>>() - && let Some((_, elements)) = snippet_split.split_last() +/// Build a type which can be used in a turbofish syntax from `hir_ty`, either by copying the +/// existing generic arguments with the exception of elided lifetimes, or by inserting placeholders +/// for types and consts without default values. +fn build_full_type(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, app: &mut Applicability) -> String { + if let TyKind::Path(ty_qpath) = hir_ty.kind + && let QPath::Resolved(None, ty_path) = &ty_qpath + && let Res::Def(_, ty_did) = ty_path.res { - if let [type_specifier, _] = snippet_split.as_slice() - && let Some(type_specifier) = strip_angle_brackets(type_specifier) - && let Some((type_specifier, ..)) = type_specifier.split_once(" as ") - { - type_specifier.to_string() + let mut ty_str = itertools::join(ty_path.segments.iter().map(|s| s.ident), "::"); + let mut first = true; + let mut append = |arg: &str| { + write!(&mut ty_str, "{}{arg}", [", ", "<"][usize::from(first)]).unwrap(); + first = false; + }; + if let Some(args) = ty_path.segments.last().and_then(|segment| segment.args) { + args.args + .iter() + .filter(|arg| !matches!(arg, GenericArg::Lifetime(lt) if lt.is_elided())) + .for_each(|arg| append(&snippet_with_applicability(cx, arg.span().source_callsite(), "_", app))); } else { - // is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { - // remove the type specifier from the path elements - let without_ts = elements - .iter() - .filter_map(|e| { - if e == type_specifier { - None - } else { - Some((*e).to_string()) - } - }) - .collect::<Vec<_>>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`) - format!("{}{type_specifier}", without_ts.join("::")) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or(ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{wildcards}>", elements.join("::")) - } + cx.tcx + .generics_of(ty_did) + .own_params + .iter() + .filter(|param| { + matches!( + param.kind, + GenericParamDefKind::Type { has_default: false, .. } + | GenericParamDefKind::Const { has_default: false, .. } + ) + }) + .for_each(|_| append("_")); } + ty_str.push_str([">", ""][usize::from(first)]); + ty_str } else { - ty.to_string() + snippet_with_applicability(cx, hir_ty.span.source_callsite(), "_", app).into() } } diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs index bdc834bd47a..ec4b9c7ae2e 100644 --- a/clippy_lints/src/methods/io_other_error.rs +++ b/clippy_lints/src/methods/io_other_error.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{expr_or_init, paths}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args if let [error_kind, error] = args && !expr.span.from_expansion() && !error_kind.span.from_expansion() - && clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW) - && clippy_utils::is_expr_path_def_path( - cx, - clippy_utils::expr_or_init(cx, error_kind), - &clippy_utils::paths::IO_ERRORKIND_OTHER, - ) && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind + && paths::IO_ERROR_NEW.matches_path(cx, path) + && paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind)) && msrv.meets(cx, msrvs::IO_ERROR_OTHER) { span_lint_and_then( diff --git a/clippy_lints/src/methods/iter_filter.rs b/clippy_lints/src/methods/iter_filter.rs index bafabec7e06..adeff375c8a 100644 --- a/clippy_lints/src/methods/iter_filter.rs +++ b/clippy_lints/src/methods/iter_filter.rs @@ -6,12 +6,12 @@ use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::symbol::{Ident, Symbol}; /// /// Returns true if the expression is a method call to `method_name` @@ -154,7 +154,7 @@ fn expression_type( if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Option) && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() - && is_method(cx, filter_arg, sym::Option, sym!(is_some), &[]) + && is_method(cx, filter_arg, sym::Option, sym::is_some, &[]) { return Some(FilterType::IsSome); } @@ -162,7 +162,7 @@ fn expression_type( if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Result) && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() - && is_method(cx, filter_arg, sym::Result, sym!(is_ok), &[]) + && is_method(cx, filter_arg, sym::Result, sym::is_ok, &[]) { return Some(FilterType::IsOk); } diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 94415fc9106..3ac9299ba91 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, } - && let ty = cx.typeck_results().expr_ty(recv) + && let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index f51bdc78f8a..7bb625222ec 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>( && let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id) && cx.tcx.trait_of_item(method_id) == Some(iter_id) && let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv) - && let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, "Item") + && let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, sym::Item) && matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty)) { if needs_into_iter diff --git a/clippy_lints/src/methods/manual_c_str_literals.rs b/clippy_lints/src/methods/manual_c_str_literals.rs index 0274e31b4c3..3fa83cd39d1 100644 --- a/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/clippy_lints/src/methods/manual_c_str_literals.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use super::MANUAL_C_STR_LITERALS; @@ -71,15 +71,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args && cx.tcx.sess.edition() >= Edition2021 && msrv.meets(cx, msrvs::C_STR_LITERALS) { - match fn_name.as_str() { - name @ ("from_bytes_with_nul" | "from_bytes_with_nul_unchecked") + match fn_name { + sym::from_bytes_with_nul | sym::from_bytes_with_nul_unchecked if !arg.span.from_expansion() && let ExprKind::Lit(lit) = arg.kind && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node => { - check_from_bytes(cx, expr, arg, name); + check_from_bytes(cx, expr, arg, fn_name); }, - "from_ptr" => check_from_ptr(cx, expr, arg), + sym::from_ptr => check_from_ptr(cx, expr, arg), _ => {}, } } @@ -106,13 +106,13 @@ fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) { } } /// Checks `CStr::from_bytes_with_nul(b"foo\0")` -fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: &str) { +fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: Symbol) { let (span, applicability) = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(method, ..) = parent.kind && [sym::unwrap, sym::expect].contains(&method.ident.name) { (parent.span, Applicability::MachineApplicable) - } else if method == "from_bytes_with_nul_unchecked" { + } else if method == sym::from_bytes_with_nul_unchecked { // `*_unchecked` returns `&CStr` directly, nothing needs to be changed (expr.span, Applicability::MachineApplicable) } else { diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 18978a1d2bc..e2df8ce1513 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, path_def_id}; +use clippy_utils::{path_res, sym}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -79,16 +80,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> { } let ty = cx.typeck_results().expr_ty(expr); - let ty_str = ty.to_string(); - // `std::T::MAX` `std::T::MIN` constants - if let Some(id) = path_def_id(cx, expr) { - if match_def_path(cx, id, &["core", &ty_str, "MAX"]) { - return Some(MinMax::Max); - } - - if match_def_path(cx, id, &["core", &ty_str, "MIN"]) { - return Some(MinMax::Min); + // `T::MAX` and `T::MIN` constants + if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind + && let Res::PrimTy(_) = path_res(cx, base) + { + match seg.ident.name { + sym::MAX => return Some(MinMax::Max), + sym::MIN => return Some(MinMax::Min), + _ => {}, } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7e2ce157c73..e0e6a1a59b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -114,6 +114,7 @@ mod suspicious_command_arg_space; mod suspicious_map; mod suspicious_splitn; mod suspicious_to_owned; +mod swap_with_temporary; mod type_id_on_box; mod unbuffered_bytes; mod uninit_assumed_init; @@ -4369,11 +4370,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// - /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using a question mark (`?`) instead. + /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using + /// the `?` operator instead. /// /// ### Why is this bad? - /// /// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`. /// This can be replaced with the `?` operator, which is more concise and idiomatic. /// @@ -4428,7 +4428,7 @@ declare_clippy_lint! { /// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap()); /// file.bytes(); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub UNBUFFERED_BYTES, perf, "calling .bytes() is very inefficient when data is not in memory" @@ -4453,7 +4453,7 @@ declare_clippy_lint! { /// values.contains(&10) /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MANUAL_CONTAINS, perf, "unnecessary `iter().any()` on slices that can be replaced with `contains()`" @@ -4481,6 +4481,53 @@ declare_clippy_lint! { "calling `std::io::Error::new(std::io::ErrorKind::Other, _)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `std::mem::swap` with temporary values. + /// + /// ### Why is this bad? + /// Storing a new value in place of a temporary value which will + /// be dropped right after the `swap` is an inefficient way of performing + /// an assignment. The same result can be achieved by using a regular + /// assignment. + /// + /// ### Examples + /// ```no_run + /// fn replace_string(s: &mut String) { + /// std::mem::swap(s, &mut String::from("replaced")); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn replace_string(s: &mut String) { + /// *s = String::from("replaced"); + /// } + /// ``` + /// + /// Also, swapping two temporary values has no effect, as they will + /// both be dropped right after swapping them. This is likely an indication + /// of a bug. For example, the following code swaps the references to + /// the last element of the vectors, instead of swapping the elements + /// themselves: + /// + /// ```no_run + /// fn bug(v1: &mut [i32], v2: &mut [i32]) { + /// // Incorrect: swapping temporary references (`&mut &mut` passed to swap) + /// std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn correct(v1: &mut [i32], v2: &mut [i32]) { + /// std::mem::swap(v1.last_mut().unwrap(), v2.last_mut().unwrap()); + /// } + /// ``` + #[clippy::version = "1.88.0"] + pub SWAP_WITH_TEMPORARY, + complexity, + "detect swap with a temporary value" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4658,9 +4705,12 @@ impl_lint_pass!(Methods => [ UNBUFFERED_BYTES, MANUAL_CONTAINS, IO_OTHER_ERROR, + SWAP_WITH_TEMPORARY, ]); /// Extracts a method call name, args, and `Span` of the method name. +/// This ensures that neither the receiver nor any of the arguments +/// come from expansion. pub fn method_call<'tcx>( recv: &'tcx Expr<'tcx>, ) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { @@ -4689,6 +4739,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { manual_c_str_literals::check(cx, expr, func, args, self.msrv); useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv); io_other_error::check(cx, expr, func, args, self.msrv); + swap_with_temporary::check(cx, expr, func, args); }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; @@ -4858,6 +4909,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { @@ -5000,29 +5052,12 @@ impl Methods { Some(("err", recv, [], err_span, _)) => { err_expect::check(cx, expr, recv, span, err_span, self.msrv); }, - _ => unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ), + _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("expect_err", [_]) => { + ("expect_err", [_]) | ("unwrap_err" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ); }, ("extend", [arg]) => { string_extend_chars::check(cx, expr, recv, arg); @@ -5388,27 +5423,6 @@ impl Methods { _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); - }, - ("unwrap_err", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); }, ("unwrap_or", [u_arg]) => { match method_call(recv) { @@ -5437,9 +5451,6 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_unchecked" | "unwrap_err_unchecked", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - }, ("unwrap_or_else", [u_arg]) => { match method_call(recv) { Some(("map", recv, [map_arg], _, _)) @@ -5477,6 +5488,56 @@ impl Methods { _ => {}, } } + // Handle method calls whose receiver and arguments may come from expansion + if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + match (path.ident.name.as_str(), args) { + ("expect", [_]) if !matches!(method_call(recv), Some(("ok" | "err", _, [], _, _))) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + ("expect_err", [_]) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + ("unwrap", []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + ("unwrap_err", []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + _ => {}, + } + } } } diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 743aacf0588..71c1576cd57 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,9 +7,8 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; +use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -32,7 +31,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && method.ident.name.as_str() == "is_ascii" + && method.ident.name == sym::is_ascii && path_to_local_id(receiver, first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char @@ -76,9 +75,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && let ExprKind::Path(path) = fn_path.kind - && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) + && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { @@ -102,7 +99,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind - && method.ident.name.as_str() == "chars" + && method.ident.name == sym::chars && let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() && *str_ty.kind() == ty::Str { diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 4b3fd452954..4c1ed6a1d83 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -4,10 +4,12 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection}; +use clippy_utils::ty::{ + get_type_diagnostic_name, has_non_owning_mutable_access, make_normalized_projection, make_projection, +}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, - path_to_local_id, + path_to_local_id, sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; @@ -17,12 +19,13 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_middle::ty::{self, AssocTag, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::Span; use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, name_span: Span, @@ -30,6 +33,11 @@ pub(super) fn check<'tcx>( iter_expr: &'tcx Expr<'tcx>, call_span: Span, ) { + let iter_ty = cx.typeck_results().expr_ty(iter_expr); + if has_non_owning_mutable_access(cx, iter_ty) { + return; // don't lint if the iterator has side effects + } + match cx.tcx.parent_hir_node(collect_expr.hir_id) { Node::Expr(parent) => { check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr); @@ -238,10 +246,10 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - .instantiate_bound_regions_with_erased(sig.rebind(search_ty)) .kind() && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) - && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_name_and_kind( + && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_ident_and_kind( cx.tcx, Ident::with_dummy_span(sym::Item), - AssocKind::Type, + AssocTag::Type, iter_trait, ) && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))]) @@ -331,7 +339,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.as_str() == "collect" + && method_name.ident.name == sym::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)); @@ -349,20 +357,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let Some(hir_id) = self.current_statement_hir_id { self.hir_id_uses_map.insert(hir_id, self.uses.len()); } - match method_name.ident.name.as_str() { - "into_iter" => self.uses.push(Some(IterFunction { + match method_name.ident.name { + sym::into_iter => self.uses.push(Some(IterFunction { func: IterFunctionKind::IntoIter(expr.hir_id), span: expr.span, })), - "len" => self.uses.push(Some(IterFunction { + sym::len => self.uses.push(Some(IterFunction { func: IterFunctionKind::Len, span: expr.span, })), - "is_empty" => self.uses.push(Some(IterFunction { + sym::is_empty => self.uses.push(Some(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span, })), - "contains" => self.uses.push(Some(IterFunction { + sym::contains => self.uses.push(Some(IterFunction { func: IterFunctionKind::Contains(args[0].span), span: expr.span, })), diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index da084871402..bce314e64f0 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::{match_any_def_paths, paths}; +use clippy_utils::paths; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -13,7 +13,7 @@ use rustc_span::{Span, sym}; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS) + is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { @@ -126,14 +126,14 @@ fn get_open_options( && let ExprKind::Path(path) = callee.kind && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() { - let std_file_options = [sym::file_options, sym::open_options_new]; - - let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS]; + let is_std_options = matches!( + cx.tcx.get_diagnostic_name(did), + Some(sym::file_options | sym::open_options_new) + ); - let is_std_options = std_file_options - .into_iter() - .any(|sym| cx.tcx.is_diagnostic_item(sym, did)); - is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some() + is_std_options + || paths::TOKIO_IO_OPEN_OPTIONS_NEW.matches(cx, did) + || paths::TOKIO_FILE_OPTIONS.matches(cx, did) } else { false } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index c03420a5143..b78b082e460 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -6,13 +6,13 @@ use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ - contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, + contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; -use rustc_span::symbol::{self, Symbol, sym}; +use rustc_span::symbol::{self, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; @@ -66,8 +66,8 @@ pub(super) fn check<'tcx>( }; let sugg = match (name, call_expr.is_some()) { - ("unwrap_or", true) | ("unwrap_or_else", false) => sym!(unwrap_or_default), - ("or_insert", true) | ("or_insert_with", false) => sym!(or_default), + ("unwrap_or", true) | ("unwrap_or_else", false) => sym::unwrap_or_default, + ("or_insert", true) | ("or_insert_with", false) => sym::or_default, _ => return false, }; @@ -78,8 +78,7 @@ pub(super) fn check<'tcx>( .iter() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) .find_map(|assoc| { - if assoc.fn_has_self_parameter - && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 + if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 { Some(assoc.def_id) } else { diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index fe999a3b5f8..407f2e80aff 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,17 +1,16 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::READ_LINE_WITHOUT_TRIM; @@ -44,7 +43,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.as_str() == "parse" + && segment.ident.name == sym::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 +57,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.as_str() == "ends_with" + } else if segment.ident.name == sym::ends_with && recv.span == expr.span && let [arg] = args && expr_is_string_literal_without_trailing_newline(arg) diff --git a/clippy_lints/src/methods/return_and_then.rs b/clippy_lints/src/methods/return_and_then.rs index e8861935d42..df8544f9220 100644 --- a/clippy_lints/src/methods/return_and_then.rs +++ b/clippy_lints/src/methods/return_and_then.rs @@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; -use clippy_utils::{is_expr_final_block_expr, peel_blocks}; +use clippy_utils::{get_parent_expr, peel_blocks}; use super::RETURN_AND_THEN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'_>, ) { - if !is_expr_final_block_expr(cx.tcx, expr) { + if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() { return; } @@ -55,13 +55,32 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - let msg = "use the question mark operator instead of an `and_then` call"; - let sugg = format!( - "let {} = {}?;\n{}", - arg_snip, - recv_snip, - reindent_multiline(inner, false, indent_of(cx, expr.span)) - ); + // If suggestion is going to get inserted as part of a `return` expression, it must be blockified. + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + let base_indent = indent_of(cx, parent_expr.span); + let inner_indent = base_indent.map(|i| i + 4); + format!( + "{}\n{}\n{}", + reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent), + reindent_multiline(inner, false, inner_indent), + reindent_multiline("}", false, base_indent), + ) + } else { + format!( + "let {} = {}?;\n{}", + arg_snip, + recv_snip, + reindent_multiline(inner, false, indent_of(cx, expr.span)) + ) + }; - span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability); + span_lint_and_sugg( + cx, + RETURN_AND_THEN, + expr.span, + "use the `?` operator instead of an `and_then` call", + "try", + sugg, + applicability, + ); } diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs index 96180862fd0..8b51268da46 100644 --- a/clippy_lints/src/methods/seek_from_current.rs +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -3,12 +3,11 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_enum_variant_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::{is_enum_variant_ctor, sym}; use super::SEEK_FROM_CURRENT; @@ -38,7 +37,7 @@ fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) if let ExprKind::Call(f, [arg]) = expr.kind && let ExprKind::Path(ref path) = f.kind && let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id() - && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id) + && is_enum_variant_ctor(cx, sym::SeekFrom, sym::Current, ctor_call_id) // check if argument of `SeekFrom::Current` is `0` && let ExprKind::Lit(lit) = arg.kind && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node diff --git a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 7b1dd9e58c5..b8405a78f23 100644 --- a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified}; +use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified, sym}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::SEEK_TO_START_INSTEAD_OF_REWIND; @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(func, [arg]) = arg.kind && let ExprKind::Path(ref path) = func.kind && let Some(ctor_call_id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Start), ctor_call_id) + && is_enum_variant_ctor(cx, sym::SeekFrom, sym::Start, ctor_call_id) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node { diff --git a/clippy_lints/src/methods/str_split.rs b/clippy_lints/src/methods/str_split.rs index 3586e11f56a..fb4ac7b3613 100644 --- a/clippy_lints/src/methods/str_split.rs +++ b/clippy_lints/src/methods/str_split.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::visitors::is_const_evaluatable; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -19,7 +20,7 @@ pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &' && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && (matches!(split_lit.node, LitKind::Char('\n')) - || matches!(split_lit.node, LitKind::Str(sym, _) if (sym.as_str() == "\n" || sym.as_str() == "\r\n"))) + || matches!(split_lit.node, LitKind::Str(sym::LF | sym::CRLF, _))) { let mut app = Applicability::MaybeIncorrect; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index d183457da25..c8efb600f57 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, path_to_local_id, paths}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -288,7 +288,7 @@ fn parse_iter_usage<'tcx>( match (name.ident.as_str(), args) { ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { - return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE) + return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did) && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind() && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) && let ty::Tuple(subs) = subs.type_at(0).kind() diff --git a/clippy_lints/src/methods/swap_with_temporary.rs b/clippy_lints/src/methods/swap_with_temporary.rs new file mode 100644 index 00000000000..de729fb343a --- /dev/null +++ b/clippy_lints/src/methods/swap_with_temporary.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use rustc_ast::BorrowKind; +use rustc_errors::{Applicability, Diag}; +use rustc_hir::{Expr, ExprKind, Node, QPath}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::SWAP_WITH_TEMPORARY; + +const MSG_TEMPORARY: &str = "this expression returns a temporary value"; +const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value"; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { + if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind + && let Some(func_def_id) = func_path.res.opt_def_id() + && cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id) + { + match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) { + (ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => { + emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp); + }, + (ArgKind::RefMutToTemp(left_temp), right) => emit_lint_assign(cx, expr, &right, &args[0], left_temp), + (left, ArgKind::RefMutToTemp(right_temp)) => emit_lint_assign(cx, expr, &left, &args[1], right_temp), + _ => {}, + } + } +} + +enum ArgKind<'tcx> { + // Mutable reference to a place, coming from a macro + RefMutToPlaceAsMacro(&'tcx Expr<'tcx>), + // Place behind a mutable reference + RefMutToPlace(&'tcx Expr<'tcx>), + // Temporary value behind a mutable reference + RefMutToTemp(&'tcx Expr<'tcx>), + // Any other case + Expr(&'tcx Expr<'tcx>), +} + +impl<'tcx> ArgKind<'tcx> { + fn new(arg: &'tcx Expr<'tcx>) -> Self { + if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind { + if target.is_syntactic_place_expr() { + if arg.span.from_expansion() { + ArgKind::RefMutToPlaceAsMacro(arg) + } else { + ArgKind::RefMutToPlace(target) + } + } else { + ArgKind::RefMutToTemp(target) + } + } else { + ArgKind::Expr(arg) + } + } +} + +// Emits a note either on the temporary expression if it can be found in the same context as the +// base and returns `true`, or on the mutable reference to the temporary expression otherwise and +// returns `false`. +fn emit_note(diag: &mut Diag<'_, ()>, base: &Expr<'_>, expr: &Expr<'_>, expr_temp: &Expr<'_>) -> bool { + if base.span.eq_ctxt(expr.span) { + diag.span_note(expr_temp.span.source_callsite(), MSG_TEMPORARY); + true + } else { + diag.span_note(expr.span.source_callsite(), MSG_TEMPORARY_REFMUT); + false + } +} + +fn emit_lint_useless( + cx: &LateContext<'_>, + expr: &Expr<'_>, + left: &Expr<'_>, + right: &Expr<'_>, + left_temp: &Expr<'_>, + right_temp: &Expr<'_>, +) { + span_lint_and_then( + cx, + SWAP_WITH_TEMPORARY, + expr.span, + "swapping temporary values has no effect", + |diag| { + emit_note(diag, expr, left, left_temp); + emit_note(diag, expr, right, right_temp); + }, + ); +} + +fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>, reftemp: &Expr<'_>, temp: &Expr<'_>) { + span_lint_and_then( + cx, + SWAP_WITH_TEMPORARY, + expr.span, + "swapping with a temporary value is inefficient", + |diag| { + if !emit_note(diag, expr, reftemp, temp) { + return; + } + + // Make the suggestion only when the original `swap()` call is a statement + // or the last expression in a block. + if matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(..) | Node::Block(..)) { + let mut applicability = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + let assign_target = match target { + ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => { + Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref() + }, + ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + ArgKind::RefMutToTemp(_) => unreachable!(), + }; + let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability); + diag.span_suggestion( + expr.span, + "use assignment instead", + format!("{assign_target} = {assign_source}"), + applicability, + ); + } + }, + ); +} diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index f920f306bc1..79ed352193f 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -3,13 +3,12 @@ use clippy_utils::diagnostics::span_lint; 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::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; @@ -95,7 +94,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.as_str() == "then_some" + if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() && path_to_local_id(arg, arg_id) { diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index c0e01568588..20cf35363d1 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -99,7 +99,7 @@ pub fn check_for_loop_iter( && let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) && let collection_ty = cx.typeck_results().expr_ty(collection) && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) - && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item") + && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, sym::Item) && iter_item_ty == into_iter_item_ty && let Some(collection_snippet) = collection.span.get_source_text(cx) { diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index e3596cf6a60..71e606add52 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -24,7 +24,12 @@ pub(super) fn check<'tcx>( let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); if (is_option || is_result || is_bool) - && let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind + && let hir::ExprKind::Closure(&hir::Closure { + body, + fn_decl, + kind: hir::ClosureKind::Closure, + .. + }) = arg.kind { let body = cx.tcx.hir_body(body); let body_expr = &body.value; diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 62ba3012643..87bb8d46a1d 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -7,7 +7,7 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_ use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, - return_ty, + return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -20,7 +20,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; @@ -153,7 +153,7 @@ fn check_addr_of_expr( } if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && implements_trait(cx, receiver_ty, deref_trait_id, &[]) - && cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty) + && cx.get_associated_type(receiver_ty, deref_trait_id, sym::Target) == Some(target_ty) // Make sure that it's actually calling the right `.to_string()`, (#10033) // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) // but that's ok for Cow::into_owned specifically) @@ -312,8 +312,7 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, /// call of a `to_owned`-like function is unnecessary. fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) - && let Some((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent) - && fn_name.as_str() == "split" + && let Some((sym::split, argument_expr)) = get_fn_name_and_arg(cx, parent) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) { @@ -322,7 +321,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb // add `.as_ref()` to the suggestion. let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) - && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, "Target") + && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, sym::Target) != Some(cx.tcx.types.str_) { ".as_ref()" @@ -614,8 +613,7 @@ fn has_lifetime(ty: Ty<'_>) -> bool { /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - (method_name.as_str() == "cloned" || method_name.as_str() == "copied") - && is_diag_trait_item(cx, method_def_id, sym::Iterator) + matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" @@ -628,7 +626,7 @@ fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: /// Returns true if the named method is `Cow::into_owned`. fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow) + method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that @@ -648,7 +646,7 @@ fn is_to_string_on_string_like<'a>( && let GenericArgKind::Type(ty) = generic_arg.unpack() && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) - && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) + && (cx.get_associated_type(ty, deref_trait_id, sym::Target) == Some(cx.tcx.types.str_) || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) { true diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 0cbf6004be3..17e2620d9dd 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,9 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; -use clippy_utils::{ - get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs, -}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } - } else if match_def_path(cx, def_id, &["core", "option", "Option", call_name]) - || match_def_path(cx, def_id, &["core", "result", "Result", call_name]) + } else if let Some(impl_id) = cx.tcx.opt_parent(def_id) + && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() + && (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did())) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index ed89b3b3438..64eafc0ebcc 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,10 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -79,12 +78,10 @@ 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.as_str() == "max" { - fetch_const(cx, Some(receiver), args, MinMax::Max) - } else if path.ident.name.as_str() == "min" { - fetch_const(cx, Some(receiver), args, MinMax::Min) - } else { - None + match path.ident.name { + sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + _ => None, } } else { None diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index d9acf655b8a..c8e3462b24e 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -3,14 +3,15 @@ use std::ops::ControlFlow; use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, hash_expr, higher}; -use rustc_ast::{LitKind, RangeLimits}; +use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp}; +use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -97,7 +98,7 @@ enum LengthComparison { /// /// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, v.len()))` fn len_comparison<'hir>( - bin_op: BinOp, + bin_op: BinOpKind, left: &'hir Expr<'hir>, right: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { @@ -112,7 +113,7 @@ fn len_comparison<'hir>( // normalize comparison, `v.len() > 4` becomes `4 < v.len()` // this simplifies the logic a bit - let (op, left, right) = normalize_comparison(bin_op.node, left, right)?; + let (op, left, right) = normalize_comparison(bin_op, left, right)?; match (op, left.kind, right.kind) { (Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, left as usize, right)), (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, right as usize, left)), @@ -134,19 +135,31 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind && let ExprKind::Binary(bin_op, left, right) = &condition.kind - - && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right) - && let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind - && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() - && method.ident.name == sym::len - // check if `then` block has a never type expression && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind && cx.typeck_results().expr_ty(then_expr).is_never() { + len_comparison(bin_op.node, left, right)? + } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_eq_macro) => Some((macro_call, BinOpKind::Eq)), + Some(sym::assert_ne_macro) => Some((macro_call, BinOpKind::Ne)), + _ => None, + } + }) && let Some((left, right, _)) = find_assert_eq_args(cx, expr, macro_call.expn) + { + len_comparison(bin_op, left, right)? + } else { + return None; + }; + + if let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind + && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() + && method.ident.name == sym::len + { Some((cmp, asserted_len, recv)) } else { None @@ -310,7 +323,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un indexes: mem::take(indexes), is_first_highest: *is_first_highest, slice, - assert_span: expr.span, + assert_span: expr.span.source_callsite(), comparison, asserted_len, }; @@ -319,7 +332,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un indexes.push(IndexEntry::StrayAssert { asserted_len, comparison, - assert_span: expr.span, + assert_span: expr.span.source_callsite(), slice, }); } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 67537a251da..f3e24044fb6 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -155,9 +155,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let mir = cx.tcx.mir_drops_elaborated_and_const_checked(def_id); + let mir = cx.tcx.optimized_mir(def_id); - if let Ok(()) = is_min_const_fn(cx, &mir.borrow(), self.msrv) + if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = cx.tcx.hir_node_by_def_id(def_id) { @@ -197,7 +197,7 @@ fn fn_inputs_has_impl_trait_ty(cx: &LateContext<'_>, def_id: LocalDefId) -> bool inputs.iter().any(|input| { matches!( input.kind(), - ty::Alias(ty::AliasTyKind::Weak, alias_ty) if cx.tcx.type_of(alias_ty.def_id).skip_binder().is_impl_trait() + ty::Alias(ty::AliasTyKind::Free, alias_ty) if cx.tcx.type_of(alias_ty.def_id).skip_binder().is_impl_trait() ) }) } diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index b234b190153..7772051eb5c 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -48,6 +48,8 @@ pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. crate_items_only: bool, + /// Whether to allow fields starting with an underscore to skip documentation requirements + allow_unused: bool, /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. doc_hidden_stack: Vec<bool>, @@ -59,6 +61,7 @@ impl MissingDoc { pub fn new(conf: &'static Conf) -> Self { Self { crate_items_only: conf.missing_docs_in_crate_items, + allow_unused: conf.missing_docs_allow_unused, doc_hidden_stack: vec![false], prev_span: None, } @@ -260,11 +263,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { - if !sf.is_positional() { + if !(sf.is_positional() + || is_from_proc_macro(cx, sf) + || self.allow_unused && sf.ident.as_str().starts_with('_')) + { let attrs = cx.tcx.hir_attrs(sf.hir_id); - if !is_from_proc_macro(cx, sf) { - self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); - } + self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); } self.prev_span = Some(sf.span); } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 66631a69206..a1e621cc9f6 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; -use clippy_utils::def_path_def_ids; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::{PathNS, lookup_path_str}; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -56,8 +56,12 @@ impl ImportRename { renames: conf .enforced_import_renames .iter() - .map(|x| (x.path.split("::").collect::<Vec<_>>(), Symbol::intern(&x.rename))) - .flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename))) + .map(|x| (&x.path, Symbol::intern(&x.rename))) + .flat_map(|(path, rename)| { + lookup_path_str(tcx, PathNS::Arbitrary, path) + .into_iter() + .map(move |id| (id, rename)) + }) .collect(), } } diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 1932d2d5f97..be7dd74fd62 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_path_lang_item; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; +use clippy_utils::{is_path_lang_item, sym}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -13,7 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -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.as_str() == "finish_non_exhaustive" + } else if path.ident.name == sym::finish_non_exhaustive && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) { has_finish_non_exhaustive = true; diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs index 7ee746365d1..e266c36b6e7 100644 --- a/clippy_lints/src/missing_trait_methods.rs +++ b/clippy_lints/src/missing_trait_methods.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { cx, MISSING_TRAIT_METHODS, cx.tcx.def_span(item.owner_id), - format!("missing trait method provided by default: `{}`", assoc.name), + format!("missing trait method provided by default: `{}`", assoc.name()), |diag| { diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method"); }, diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index fd017060436..d9f4fb271fb 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -136,7 +136,7 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> { fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(self.cx, e) - && self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" + && self.cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) { return; } @@ -261,10 +261,11 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St | ExprKind::Assign(..) | ExprKind::Index(..) | ExprKind::Repeat(_, _) - | ExprKind::Struct(_, _, _) => { + | ExprKind::Struct(_, _, _) + | ExprKind::AssignOp(_, _, _) => { walk_expr(vis, expr); }, - ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => { + ExprKind::Binary(op, _, _) => { if op.node == BinOpKind::And || op.node == BinOpKind::Or { // x && y and x || y always evaluate x first, so these are // strictly sequenced. diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 8065a0a359b..98614baffce 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -73,8 +73,8 @@ impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); impl EarlyLintPass for ModStyle { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow - && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow + if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow { return; } diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index d5bed54f0be..9b327955608 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -42,10 +43,9 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); if !matches!( - macro_name.as_str(), - "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::debug_assert_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) { return; } @@ -60,7 +60,10 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - format!("do not call a function with mutable arguments inside of `{macro_name}!`"), + format!( + "do not call a function with mutable arguments inside of `{}!`", + cx.tcx.item_name(macro_call.def_id) + ), ); } } diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index f686cc912dd..e579dd5947d 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -299,7 +299,7 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { .associated_items(trait_def_id) .in_definition_order() .any(|assoc_item| { - if assoc_item.fn_has_self_parameter { + if assoc_item.is_method() { let self_ty = cx .tcx .fn_sig(assoc_item.def_id) diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 90b27f5dbac..7dd96f1f037 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -3,12 +3,12 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::has_iter_method; +use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) - && method_name.ident.name.as_str() == "for_each" + && method_name.ident.name == sym::for_each && is_trait_method(cx, expr, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 72b0a80260e..2a2160c3be2 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,6 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::path_res; -use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -9,52 +8,38 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Suggests alternatives for useless applications of `?` in terminating expressions + /// Suggests replacing `Ok(x?)` or `Some(x?)` with `x` in return positions where the `?` operator + /// is not needed to convert the type of `x`. /// /// ### Why is this bad? /// There's no reason to use `?` to short-circuit when execution of the body will end there anyway. /// /// ### Example /// ```no_run - /// struct TO { - /// magic: Option<usize>, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option<usize> { + /// Some(s.find('x')?) /// } /// - /// fn f(to: TO) -> Option<usize> { - /// Some(to.magic?) + /// fn g(s: &str) -> Result<usize, ParseIntError> { + /// Ok(s.parse()?) /// } - /// - /// struct TR { - /// magic: Result<usize, bool>, - /// } - /// - /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> { - /// tr.and_then(|t| Ok(t.magic?)) - /// } - /// /// ``` /// Use instead: /// ```no_run - /// struct TO { - /// magic: Option<usize>, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option<usize> { + /// s.find('x') /// } /// - /// fn f(to: TO) -> Option<usize> { - /// to.magic - /// } - /// - /// struct TR { - /// magic: Result<usize, bool>, - /// } - /// - /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> { - /// tr.and_then(|t| t.magic) + /// fn g(s: &str) -> Result<usize, ParseIntError> { + /// s.parse() /// } /// ``` #[clippy::version = "1.51.0"] pub NEEDLESS_QUESTION_MARK, complexity, - "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`." + "using `Ok(x?)` or `Some(x?)` where `x` would be equivalent" } declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); @@ -111,10 +96,10 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) - && let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { - "Some()" + && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { + "Some" } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { - "Ok()" + "Ok" } else { return; } @@ -126,14 +111,25 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { && let inner_ty = cx.typeck_results().expr_ty(inner_expr) && expr_ty == inner_ty { - span_lint_and_sugg( + span_lint_hir_and_then( cx, NEEDLESS_QUESTION_MARK, + expr.hir_id, expr.span, - "question mark operator is useless here", - format!("try removing question mark and `{sugg_remove}`"), - format!("{}", snippet(cx, inner_expr.span, r#""...""#)), - Applicability::MachineApplicable, + format!("enclosing `{variant}` and `?` operator are unneeded"), + |diag| { + diag.multipart_suggestion( + format!("remove the enclosing `{variant}` and `?` operator"), + vec![ + (expr.span.until(inner_expr.span), String::new()), + ( + inner_expr.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + String::new(), + ), + ], + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 9b53608ae7f..a27c6aa75e3 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -1,56 +1,78 @@ -use std::ptr; +// Implementation for lints detecting interior mutability in constants. +// +// For `declare_interior_mutable_const` there are three strategies used to +// determine if a value has interior mutability: +// * A type-based check. This is the least accurate, but can always run. +// * A const-eval based check. This is the most accurate, but this requires that the value is +// defined and does not work with generics. +// * A HIR-tree based check. This is less accurate than const-eval, but it can be applied to generic +// values. +// +// For `borrow_interior_mutable_const` the same three strategies are applied +// when checking a constant's value, but field and array index projections at +// the borrow site are taken into account as well. As an example: `FOO.bar` may +// have interior mutability, but `FOO.baz` may not. When borrowing `FOO.baz` no +// warning will be issued. +// +// No matter the lint or strategy, a warning should only be issued if a value +// definitely contains interior mutability. use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::{InteriorMut, implements_trait}; -use rustc_abi::VariantIdx; +use clippy_utils::paths::{PathNS, lookup_path_str}; +use clippy_utils::ty::{get_field_idx_by_name, implements_trait}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ - BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, + Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr, TraitItem, TraitItemKind, UnOp, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::mir::{ConstValue, UnevaluatedConst}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::{ + self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, + TypeckResults, TypingEnv, }; -use rustc_lint::{LateContext, LateLintPass, Lint}; -use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, sym}; +use std::collections::hash_map::Entry; -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks for declaration of `const` items which is interior - /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). + /// Checks for the declaration of named constant which contain interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. /// - /// The `const` should better be replaced by a `static` item if a global - /// variable is wanted, or replaced by a `const fn` if a constructor is wanted. + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's used. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` /// - /// ### Known problems - /// A "non-constant" const item is a legacy way to supply an - /// initialized value to downstream `static` items (e.g., the - /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, - /// and this lint should be suppressed. + /// If shared modification of the value is desired, a `static` item is needed instead. + /// If that is not desired, a `const fn` constructor should be used to make it obvious + /// at the use site that a new value is created. /// - /// Even though the lint avoids triggering on a constant whose type has enums that have variants - /// with interior mutability, and its value uses non interior mutable variants (see - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); - /// it complains about associated constants without default values only based on its types; - /// which might not be preferable. - /// There're other enums plus associated constants cases that the lint cannot handle. + /// ### Known problems + /// Prior to `const fn` stabilization this was the only way to provide a value which + /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In + /// this case the use of `const` is required and this lint should be suppressed. /// - /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issue - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -74,20 +96,44 @@ declare_clippy_lint! { "declaring `const` with interior mutability" } -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks if `const` items which is interior mutable (e.g., - /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. + /// Checks for a borrow of a named constant with interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. + /// + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's borrowed + /// // for use by `fetch_add`. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` /// - /// The `const` value should be stored inside a `static` item. + /// ### Known problems + /// This lint does not, and cannot in general, determine if the borrow of the constant + /// is used in a way which causes a mutation. e.g. + /// + /// ```rust + /// use core::cell::Cell; + /// const CELL: Cell<usize> = Cell::new(0); + /// fn get_cell() -> Cell<usize> { + /// // This is fine. It borrows a copy of `CELL`, but never mutates it through the + /// // borrow. + /// CELL.clone() + /// } + /// ``` + /// + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -113,60 +159,101 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -#[derive(Copy, Clone)] -enum Source<'tcx> { - Item { item: Span, ty: Ty<'tcx> }, - Assoc { item: Span }, - Expr { expr: Span }, +#[derive(Clone, Copy)] +enum IsFreeze { + /// The type and all possible values are `Freeze` + Yes, + /// The type itself is non-`Freeze`, but not all values are. + Maybe, + /// The type and all possible values are non-`Freeze` + No, } +impl IsFreeze { + /// Merges the variants of a sum type (i.e. an enum). + fn from_variants(iter: impl Iterator<Item = Self>) -> Self { + iter.fold(Self::Yes, |x, y| match (x, y) { + (Self::Maybe, _) | (_, Self::Maybe) | (Self::No, Self::Yes) | (Self::Yes, Self::No) => Self::Maybe, + (Self::No, Self::No) => Self::No, + (Self::Yes, Self::Yes) => Self::Yes, + }) + } -impl Source<'_> { - #[must_use] - fn lint(&self) -> (&'static Lint, &'static str, Span) { - match self { - Self::Item { item, .. } | Self::Assoc { item, .. } => ( - DECLARE_INTERIOR_MUTABLE_CONST, - "a `const` item should not be interior mutable", - *item, - ), - Self::Expr { expr } => ( - BORROW_INTERIOR_MUTABLE_CONST, - "a `const` item with interior mutability should not be borrowed", - *expr, - ), - } + /// Merges the fields of a product type (e.g. a struct or tuple). + fn from_fields(mut iter: impl Iterator<Item = Self>) -> Self { + iter.try_fold(Self::Yes, |x, y| match (x, y) { + (Self::No, _) | (_, Self::No) => None, + (Self::Maybe, _) | (_, Self::Maybe) => Some(Self::Maybe), + (Self::Yes, Self::Yes) => Some(Self::Yes), + }) + .unwrap_or(Self::No) + } + + /// Checks if this is definitely `Freeze`. + fn is_freeze(self) -> bool { + matches!(self, Self::Yes) + } + + /// Checks if this is definitely not `Freeze`. + fn is_not_freeze(self) -> bool { + matches!(self, Self::No) } } -fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { - let (lint, msg, span) = source.lint(); - span_lint_and_then(cx, lint, span, msg, |diag| { - if span.from_expansion() { - return; // Don't give suggestions into macros. - } - match source { - Source::Item { ty, .. } => { - let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { - return; - }; - if implements_trait(cx, ty, sync_trait, &[]) { - diag.help("consider making this a static item"); - } else { - diag.help( - "consider making this `Sync` so that it can go in a static item or using a `thread_local`", - ); - } - }, - Source::Assoc { .. } => (), - Source::Expr { .. } => { - diag.help("assign this const to a local or static variable, and use the variable here"); +/// What operation caused a borrow to occur. +#[derive(Clone, Copy)] +enum BorrowCause { + Borrow, + Deref, + Index, + AutoDeref, + AutoBorrow, + AutoDerefField, +} +impl BorrowCause { + fn note(self) -> Option<&'static str> { + match self { + Self::Borrow => None, + Self::Deref => Some("this deref expression is a call to `Deref::deref`"), + Self::Index => Some("this index expression is a call to `Index::index`"), + Self::AutoDeref => Some("there is a compiler inserted call to `Deref::deref` here"), + Self::AutoBorrow => Some("there is a compiler inserted borrow here"), + Self::AutoDerefField => { + Some("there is a compiler inserted call to `Deref::deref` when accessing this field") }, } - }); + } +} + +/// The source of a borrow. Both what caused it and where. +struct BorrowSource<'tcx> { + expr: &'tcx Expr<'tcx>, + cause: BorrowCause, +} +impl<'tcx> BorrowSource<'tcx> { + fn new(tcx: TyCtxt<'tcx>, expr: &'tcx Expr<'tcx>, cause: BorrowCause) -> Self { + // Custom deref and index impls will always have an auto-borrow inserted since we + // never work with reference types. + let (expr, cause) = if matches!(cause, BorrowCause::AutoBorrow) + && let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + { + match parent.kind { + ExprKind::Unary(UnOp::Deref, _) => (parent, BorrowCause::Deref), + ExprKind::Index(..) => (parent, BorrowCause::Index), + ExprKind::Field(..) => (parent, BorrowCause::AutoDerefField), + _ => (expr, cause), + } + } else { + (expr, cause) + }; + Self { expr, cause } + } } pub struct NonCopyConst<'tcx> { - interior_mut: InteriorMut<'tcx>, + ignore_tys: DefIdSet, + // Cache checked types. We can recurse through a type multiple times so this + // can be hit quite frequently. + freeze_tys: FxHashMap<Ty<'tcx>, IsFreeze>, } impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); @@ -174,332 +261,629 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE impl<'tcx> NonCopyConst<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability), + ignore_tys: conf + .ignore_interior_mutability + .iter() + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) + .collect(), + freeze_tys: FxHashMap::default(), } } - fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { - // No branch that we check (yet) should continue if val isn't a branch - let Some(branched_val) = val.try_to_branch() else { - return false; - }; - match *ty.kind() { - // the fact that we have to dig into every structs to search enums - // leads us to the point checking `UnsafeCell` directly is the only option. - ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, - // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the - // contained value. - ty::Adt(def, ..) if def.is_union() => false, - ty::Array(ty, _) => branched_val - .iter() - .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Adt(def, args) if def.is_enum() => { - let Some((&variant_valtree, fields)) = branched_val.split_first() else { - return false; - }; - let variant_index = variant_valtree.unwrap_leaf(); - let variant_index = VariantIdx::from_u32(variant_index.to_u32()); - fields - .iter() - .copied() - .zip( - def.variants()[variant_index] + /// Checks if a value of the given type is `Freeze`, or may be depending on the value. + fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.freeze_tys.entry(ty) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let e = e.insert(IsFreeze::Yes); + if ty.is_freeze(tcx, typing_env) { + return IsFreeze::Yes; + } + let is_freeze = match *ty.kind() { + ty::Adt(adt, _) if adt.is_unsafe_cell() => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + ty::Adt(adt, _) if self.ignore_tys.contains(&adt.did()) => return IsFreeze::Yes, + ty::Adt(adt, args) if adt.is_enum() => IsFreeze::from_variants(adt.variants().iter().map(|v| { + IsFreeze::from_fields( + v.fields + .iter() + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ) + })), + // Workaround for `ManuallyDrop`-like unions. + ty::Adt(adt, args) + if adt.is_union() + && adt.non_enum_variant().fields.iter().any(|f| { + tcx.layout_of(typing_env.as_query_input(f.ty(tcx, args))) + .is_ok_and(|l| l.layout.size().bytes() == 0) + }) => + { + return IsFreeze::Yes; + }, + // Rust doesn't have the concept of an active union field so we have + // to treat all fields as active. + ty::Adt(adt, args) => IsFreeze::from_fields( + adt.non_enum_variant() .fields .iter() - .map(|field| field.ty(cx.tcx, args)), - ) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) - }, - ty::Adt(def, args) => branched_val - .iter() - .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Tuple(tys) => branched_val - .iter() - .zip(tys) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) { - Ok(normalized_ty) if ty != normalized_ty => Self::is_value_unfrozen_raw_inner(cx, val, normalized_ty), - _ => false, + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ), + ty::Array(ty, _) | ty::Pat(ty, _) => self.is_ty_freeze(tcx, typing_env, ty), + ty::Tuple(tys) => { + IsFreeze::from_fields(tys.iter().map(|ty| self.is_ty_freeze(tcx, typing_env, ty))) + }, + // Treat type parameters as though they were `Freeze`. + ty::Param(_) | ty::Alias(..) => return IsFreeze::Yes, + // TODO: check other types. + _ => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + }; + if !is_freeze.is_freeze() { + self.freeze_tys.insert(ty, is_freeze); + } + is_freeze }, - _ => false, } } - fn is_value_unfrozen_raw( - cx: &LateContext<'tcx>, - result: Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>, + /// Checks if the given constant value is `Freeze`. Returns `Err` if the constant + /// cannot be read, but the result depends on the value. + fn is_value_freeze( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>, - ) -> bool { - result.map_or_else( - |err| { - // Consider `TooGeneric` cases as being unfrozen. - // This causes a false positive where an assoc const whose type is unfrozen - // have a value that is a frozen variant with a generic param (an example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). - // However, it prevents a number of false negatives that is, I think, important: - // 1. assoc consts in trait defs referring to consts of themselves (an example is - // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). - // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait - // defs (i.e. without substitute for `Self`). (e.g. borrowing - // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) - // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no - // enums. (An example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). - // One might be able to prevent these FNs correctly, and replace this with `false`; - // e.g. implementing `has_frozen_variant` described above, and not running this function - // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd - // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, - // similar to 2., but with a frozen variant) (e.g. borrowing - // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). - // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). - matches!(err, ErrorHandled::TooGeneric(..)) + val: ConstValue<'tcx>, + ) -> Result<bool, ()> { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => Ok(true), + IsFreeze::Maybe if matches!(ty.kind(), ty::Adt(..) | ty::Array(..) | ty::Tuple(..)) => { + for &(val, ty) in tcx + .try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + { + if !self.is_value_freeze(tcx, typing_env, ty, val)? { + return Ok(false); + } + } + Ok(true) }, - |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)), - ) - } - - fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { - let def_id = body_id.hir_id.owner.to_def_id(); - let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::new(def_id, args); - let cid = GlobalId { - instance, - promoted: None, - }; - let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id); - let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP); - Self::is_value_unfrozen_raw(cx, result, ty) - } - - fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { - let args = cx.typeck_results().node_args(hir_id); - - let result = Self::const_eval_resolve( - cx.tcx, - cx.typing_env(), - ty::UnevaluatedConst::new(def_id, args), - DUMMY_SP, - ); - Self::is_value_unfrozen_raw(cx, result, ty) + IsFreeze::Maybe | IsFreeze::No => Ok(false), + } } - pub fn const_eval_resolve( + /// Checks if the given expression creates a value which is `Freeze`. + /// + /// This will return `true` if the type is maybe `Freeze`, but it cannot be + /// determined for certain from the value. + /// + /// `typing_env` and `gen_args` are from the constant's use site. + /// `typeck` and `e` are from the constant's definition site. + fn is_init_expr_freeze( + &mut self, tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> EvalToValTreeResult<'tcx> { - match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) { - Ok(Some(instance)) => { - let cid = GlobalId { - instance, - promoted: None, - }; - tcx.const_eval_global_id_for_typeck(typing_env, cid, span) + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + gen_args: GenericArgsRef<'tcx>, + e: &'tcx Expr<'tcx>, + ) -> bool { + // Make sure to instantiate all types coming from `typeck` with `gen_args`. + let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args); + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => true, + IsFreeze::No => false, + IsFreeze::Maybe => match e.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(e) = b.expr => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e) + }, + ExprKind::Path(ref p) => { + let res = typeck.qpath_res(p, e.hir_id); + let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)).instantiate(tcx, gen_args); + match res { + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = + tcx.const_eval_resolve(typing_env, UnevaluatedConst::new(did, gen_args), DUMMY_SP) + && let Ok(is_freeze) = self.is_value_freeze(tcx, typing_env, ty, val) => + { + is_freeze + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((typeck, init)) = get_const_hir_value(tcx, typing_env, did, gen_args) => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, init) + }, + // Either this is a unit constructor, or some unknown value. + // In either case we consider the value to be `Freeze`. + _ => true, + } + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(p) = &callee.kind + && let res = typeck.qpath_res(p, callee.hir_id) + && matches!(res, Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)) => + { + args.iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)) + }, + ExprKind::Struct(_, fields, StructTailExpr::None) => fields + .iter() + .all(|f| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, f.expr)), + ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs + .iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)), + ExprKind::Repeat(e, _) => self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e), + _ => true, }, - Ok(None) => Err(ErrorHandled::TooGeneric(span)), - Err(err) => Err(ErrorHandled::Reported( - ReportedErrorInfo::non_const_eval_error(err), - span, - )), } } -} -impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(.., body_id) = it.kind { - let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); - if !ignored_macro(cx, it) - && self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_poly(cx, body_id, ty) - { - lint(cx, Source::Item { item: it.span, ty }); + /// Checks if the given expression (or a local projection of it) is both borrowed and + /// definitely a non-`Freeze` type. + fn is_non_freeze_expr_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + ) -> Option<BorrowSource<'tcx>> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + let ty = typeck.expr_ty(src_expr); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + let is_freeze = self.is_ty_freeze(tcx, typing_env, ty); + if is_freeze.is_freeze() { + return None; } - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind { - let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity(); - - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - if self.interior_mut.is_interior_mut_ty(cx, normalized) - // When there's no default value, lint it only according to its type; - // in other words, lint consts whose value *could* be unfrozen, not definitely is. - // This feels inconsistent with how the lint treats generic types, - // which avoids linting types which potentially become unfrozen. - // One could check whether an unfrozen type have a *frozen variant* - // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), - // and do the same as the case of generic types at impl items. - // Note that it isn't sufficient to check if it has an enum - // since all of that enum's variants can be unfrozen: - // i.e. having an enum doesn't necessary mean a type has a frozen variant. - // And, implementing it isn't a trivial task; it'll probably end up - // re-implementing the trait predicate evaluation specific to `Freeze`. - && body_id_opt.is_none_or(|body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized)) - { - lint(cx, Source::Assoc { item: trait_item.span }); + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| is_freeze.is_not_freeze()) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + match use_expr.kind { + ExprKind::Field(..) => {}, + ExprKind::Index(..) if ty.is_array() => {}, + ExprKind::AddrOf(..) if is_freeze.is_not_freeze() => { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, } + src_expr = use_expr; } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(_, body_id) = &impl_item.kind { - let item_def_id = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir_expect_item(item_def_id); - - match &item.kind { - ItemKind::Impl(Impl { - of_trait: Some(of_trait_ref), - .. - }) => { - if let Some(of_trait_def_id) = of_trait_ref.trait_def_id() - // Lint a trait impl item only when the definition is a generic type, - // assuming an assoc const is not meant to be an interior mutable type. - && let Some(of_assoc_item) = cx - .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id - && cx - .tcx - .layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input( - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound at the trait defs; - // and, in that case, the definition is *not* generic. - cx.tcx.normalize_erasing_regions( - ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id), - cx.tcx.type_of(of_assoc_item).instantiate_identity(), - ), - )) - .is_err() - // If there were a function like `has_frozen_variant` described above, - // we should use here as a frozen variant is a potential to be frozen - // similar to unknown layouts. - // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() - && let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty) - && self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. Returns `Err` if the constant cannot be read, but the + /// result depends on the value. + fn is_non_freeze_val_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut val: ConstValue<'tcx>, + ) -> Result<Option<BorrowSource<'tcx>>, ()> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + let mut ty = typeck.expr_ty(src_expr); + loop { + // Normalized as we need to check if this is an array later. + ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + let res = if let Some(cause) = does_adjust_borrow(adjust) + && !self.is_value_freeze(tcx, typing_env, ty, val)? + { + Some(BorrowSource::new(tcx, src_expr, cause)) + } else { + None + }; + return Ok(res); + } + // Check only the type here as the result gets cached for each type. + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return Ok(None); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return Ok(None); + }; + let next_val = match use_expr.kind { + ExprKind::Field(_, name) => { + if let Some(idx) = get_field_idx_by_name(ty, name.name) { + tcx.try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + .get(idx) + } else { + return Ok(None); } }, - ItemKind::Impl(Impl { of_trait: None, .. }) => { - let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); - // Normalize assoc types originated from generic params. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - - if self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); + ExprKind::Index(_, idx, _) if ty.is_array() => { + let val = tcx.try_destructure_mir_constant_for_user_output(val, ty).ok_or(())?; + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + val.fields.get(idx as usize) + } else { + // It's some value in the array so check all of them. + for &(val, _) in val.fields { + if let Some(src) = + self.is_non_freeze_val_borrowed(tcx, typing_env, typeck, use_expr, val)? + { + return Ok(Some(src)); + } + } + return Ok(None); } }, - _ => (), + ExprKind::AddrOf(..) if !self.is_value_freeze(tcx, typing_env, ty, val)? => { + return Ok(Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow))); + }, + // All other expressions use the value. + _ => return Ok(None), + }; + src_expr = use_expr; + if let Some(&(next_val, next_ty)) = next_val { + ty = next_ty; + val = next_val; + } else { + return Ok(None); } } } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Path(qpath) = &expr.kind { - // Only lint if we use the const item inside a function. - if is_in_const_context(cx) { - return; + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. + /// + /// `typing_env` and `init_args` are from the constant's use site. + /// `init_typeck` and `init_expr` are from the constant's definition site. + #[expect(clippy::too_many_arguments, clippy::too_many_lines)] + fn is_non_freeze_init_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut init_typeck: &'tcx TypeckResults<'tcx>, + mut init_args: GenericArgsRef<'tcx>, + mut init_expr: &'tcx Expr<'tcx>, + ) -> Option<BorrowSource<'tcx>> { + // Make sure to instantiate all types coming from `init_typeck` with `init_args`. + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + // First handle any adjustments since they are cheap to check. + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr)) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); } - // Make sure it is a const item. - let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { - return; - }; - - // Climb up to resolve any field access and explicit referencing. - let mut cur_expr = expr; - let mut dereferenced_expr = expr; - let mut needs_check_adjustment = true; + // Then read through constants and blocks on the init expression before + // applying the next use expression. loop { - let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id); - if parent_id == cur_expr.hir_id { - break; + match init_expr.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(next_init) = b.expr => + { + init_expr = next_init; + }, + ExprKind::Path(ref init_path) => { + let next_init_args = + EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)).instantiate(tcx, init_args); + match init_typeck.qpath_res(init_path, init_expr.hir_id) { + Res::Def(DefKind::Ctor(..), _) => return None, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = tcx.const_eval_resolve( + typing_env, + UnevaluatedConst::new(did, next_init_args), + DUMMY_SP, + ) && let Ok(res) = + self.is_non_freeze_val_borrowed(tcx, typing_env, init_typeck, src_expr, val) => + { + return res; + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((next_typeck, value)) = + get_const_hir_value(tcx, typing_env, did, next_init_args) => + { + init_typeck = next_typeck; + init_args = next_init_args; + init_expr = value; + }, + // There's no more that we can read from the init expression. Switch to a + // type based check. + _ => { + return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, src_expr); + }, + } + }, + _ => break, } - if let Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) { - match &parent_expr.kind { - ExprKind::AddrOf(..) => { - // `&e` => `e` must be referenced. - needs_check_adjustment = false; - }, - ExprKind::Field(..) => { - needs_check_adjustment = true; + } - // Check whether implicit dereferences happened; - // if so, no need to go further up - // because of the same reason as the `ExprKind::Unary` case. - if cx - .typeck_results() - .expr_adjustments(dereferenced_expr) - .iter() - .any(|adj| matches!(adj.kind, Adjust::Deref(_))) - { - break; - } + // Then a type check. Note we only check the type here as the result + // gets cached. + let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return None; + } - dereferenced_expr = parent_expr; - }, - ExprKind::Index(e, _, _) if ptr::eq(&**e, cur_expr) => { - // `e[i]` => desugared to `*Index::index(&e, i)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; - }, - ExprKind::Unary(UnOp::Deref, _) => { - // `*e` => desugared to `*Deref::deref(&e)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; + // Finally reduce the init expression using the next use expression. + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + init_expr = match &use_expr.kind { + ExprKind::Field(_, name) => match init_expr.kind { + ExprKind::Struct(_, fields, _) + if let Some(field) = fields.iter().find(|f| f.ident.name == name.name) => + { + field.expr + }, + ExprKind::Tup(fields) + if let Ok(idx) = name.as_str().parse::<usize>() + && let Some(field) = fields.get(idx) => + { + field + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(callee_path) = &callee.kind + && matches!( + init_typeck.qpath_res(callee_path, callee.hir_id), + Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_) + ) + && let Ok(idx) = name.as_str().parse::<usize>() + && let Some(arg) = args.get(idx) => + { + arg + }, + // Revert to a type based check as we don't know the field's value. + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::Index(_, idx, _) if ty.is_array() => match init_expr.kind { + ExprKind::Array(fields) => { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + // If the index is out of bounds it means the code + // unconditionally panics. In that case there is no borrow. + fields.get(idx as usize)? + } else { + // Unknown index, just run the check for all values. + return fields.iter().find_map(|f| { + self.is_non_freeze_init_borrowed( + tcx, + typing_env, + typeck, + use_expr, + init_typeck, + init_args, + f, + ) + }); + } + }, + // Just assume the index expression doesn't panic here. + ExprKind::Repeat(field, _) => field, + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::AddrOf(..) + if !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr) => + { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, + }; + src_expr = use_expr; + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Const(ident, .., body_id) = item.kind + && !ident.is_special() + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Yes => false, + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + }, + } + && !item.span.in_external_macro(cx.sess().source_map()) + // Only needed when compiling `std` + && !is_thread_local(cx, item) + { + span_lint_and_then( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + ident.span, + "named constant with interior mutability", + |diag| { + let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { + return; + }; + if implements_trait(cx, ty, sync_trait, &[]) { + diag.help("did you mean to make this a `static` item"); + } else { + diag.help("did you mean to make this a `thread_local!` item"); + } + }, + ); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Const(_, body_id_opt) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Maybe if let Some(body_id) = body_id_opt => { + match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => { + !is_freeze }, - _ => break, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), } - cur_expr = parent_expr; - } else { - break; - } + }, + IsFreeze::Yes | IsFreeze::Maybe => false, } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } - let ty = if needs_check_adjustment { - let adjustments = cx.typeck_results().expr_adjustments(dereferenced_expr); - if let Some(i) = adjustments - .iter() - .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_))) - { - if i == 0 { - cx.typeck_results().expr_ty(dereferenced_expr) + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Const(_, body_id) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::Yes => false, + IsFreeze::No => { + // If this is a trait impl, check if the trait definition is the source + // of the cell. + if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id()) + && let ItemKind::Impl(impl_block) = parent_item.kind + && let Some(of_trait) = impl_block.of_trait + && let Some(trait_id) = of_trait.trait_def_id() + { + // Replace all instances of `<Self as Trait>::AssocType` with the + // unit type and check again. If the result is the same then the + // trait definition is the cause. + let ty = (ReplaceAssocFolder { + tcx: cx.tcx, + trait_id, + self_ty: cx.tcx.type_of(parent_item.owner_id).instantiate_identity(), + }) + .fold_ty(cx.tcx.type_of(item.owner_id).instantiate_identity()); + // `ty` may not be normalizable, but that should be fine. + !self.is_ty_freeze(cx.tcx, cx.typing_env(), ty).is_not_freeze() } else { - adjustments[i - 1].target + true } + }, + // Even if this is from a trait, there are values which don't have + // interior mutability. + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + }, + } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(qpath) = &e.kind + && let typeck = cx.typeck_results() + && let Res::Def(DefKind::Const | DefKind::AssocConst, did) = typeck.qpath_res(qpath, e.hir_id) + // As of `1.80` constant contexts can't borrow any type with interior mutability + && !is_in_const_context(cx) + && !self.is_ty_freeze(cx.tcx, cx.typing_env(), typeck.expr_ty(e)).is_freeze() + && let Some(borrow_src) = { + // The extra block helps formatting a lot. + if let Ok(val) = cx.tcx.const_eval_resolve( + cx.typing_env(), + UnevaluatedConst::new(did, typeck.node_args(e.hir_id)), + DUMMY_SP, + ) && let Ok(src) = self.is_non_freeze_val_borrowed(cx.tcx, cx.typing_env(), typeck, e, val) + { + src + } else if let init_args = typeck.node_args(e.hir_id) + && let Some((init_typeck, init)) = get_const_hir_value(cx.tcx, cx.typing_env(), did, init_args) + { + self.is_non_freeze_init_borrowed(cx.tcx, cx.typing_env(), typeck, e, init_typeck, init_args, init) } else { - // No borrow adjustments means the entire const is moved. - return; + self.is_non_freeze_expr_borrowed(cx.tcx, cx.typing_env(), typeck, e) } - } else { - cx.typeck_results().expr_ty(dereferenced_expr) - }; - - if self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) - { - lint(cx, Source::Expr { expr: expr.span }); } + && !borrow_src.expr.span.in_external_macro(cx.sess().source_map()) + { + span_lint_and_then( + cx, + BORROW_INTERIOR_MUTABLE_CONST, + borrow_src.expr.span, + "borrow of a named constant with interior mutability", + |diag| { + if let Some(note) = borrow_src.cause.note() { + diag.note(note); + } + diag.help("this lint can be silenced by assigning the value to a local variable before borrowing"); + }, + ); + } + } +} + +struct ReplaceAssocFolder<'tcx> { + tcx: TyCtxt<'tcx>, + trait_id: DefId, + self_ty: Ty<'tcx>, +} +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAssocFolder<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(AliasTyKind::Projection, ty) = ty.kind() + && ty.trait_def_id(self.tcx) == self.trait_id + && ty.self_ty() == self.self_ty + { + self.tcx.types.unit + } else { + ty.super_fold_with(self) } } } -fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { +fn is_thread_local(cx: &LateContext<'_>, it: &Item<'_>) -> bool { macro_backtrace(it.span).any(|macro_call| { matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), @@ -507,3 +891,42 @@ fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { ) }) } + +/// Checks if the adjustment causes a borrow of the original value. Returns +/// `None` if the value is consumed instead of borrowed. +fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option<BorrowCause> { + match adjust.kind { + Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), + // Custom deref calls `<T as Deref>::deref(&x)` resulting in a borrow. + Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + // All other adjustments read the value. + _ => None, + } +} + +/// Attempts to get the value of a constant as a HIR expression. Also gets the +/// `TypeckResults` associated with the constant's body. +fn get_const_hir_value<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + did: DefId, + args: GenericArgsRef<'tcx>, +) -> Option<(&'tcx TypeckResults<'tcx>, &'tcx Expr<'tcx>)> { + let did = did.as_local()?; + let (did, body_id) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::Item(item) if let ItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(_) + if let Ok(Some(inst)) = Instance::try_resolve(tcx, typing_env, did.into(), args) + && let Some(did) = inst.def_id().as_local() => + { + match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(item) if let TraitItemKind::Const(.., Some(body_id)) = item.kind => (did, body_id), + _ => return None, + } + }, + _ => return None, + }; + Some((tcx.typeck(did), tcx.hir_body(body_id).value)) +} diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 852c3885f56..23a1622f30f 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -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.as_str() == "mode" + && ((path.ident.name == sym::mode && matches!( cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder) )) - || (path.ident.name.as_str() == "set_mode" + || (path.ident.name == sym::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/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index f6bc9428d65..f66b9519317 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -1,13 +1,14 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{def_path_def_ids, fn_def_id, is_no_std_crate, path_def_id}; +use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind}; +use rustc_hir::{self as hir, BodyId, Expr, ExprKind, HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -62,10 +63,7 @@ static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ pub struct NonStdLazyStatic { msrv: Msrv, - lazy_static_lazy_static: Vec<DefId>, - once_cell_crate: Vec<CrateNum>, - once_cell_sync_lazy: Vec<DefId>, - once_cell_sync_lazy_new: Vec<DefId>, + once_cell_crates: Vec<CrateNum>, sugg_map: FxIndexMap<DefId, Option<String>>, lazy_type_defs: FxIndexMap<DefId, LazyInfo>, uses_other_once_cell_types: bool, @@ -76,10 +74,7 @@ impl NonStdLazyStatic { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv, - lazy_static_lazy_static: Vec::new(), - once_cell_crate: Vec::new(), - once_cell_sync_lazy: Vec::new(), - once_cell_sync_lazy_new: Vec::new(), + once_cell_crates: Vec::new(), sugg_map: FxIndexMap::default(), lazy_type_defs: FxIndexMap::default(), uses_other_once_cell_types: false, @@ -95,17 +90,15 @@ fn can_use_lazy_cell(cx: &LateContext<'_>, msrv: Msrv) -> bool { impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_crate(&mut self, cx: &LateContext<'hir>) { - // Fetch def_ids for external paths - self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect(); - self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect(); - self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect(); - // And CrateNums for `once_cell` crate - self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect(); + // Add CrateNums for `once_cell` crate + self.once_cell_crates = find_crates(cx.tcx, sym::once_cell) + .iter() + .map(|def_id| def_id.krate) + .collect(); // Convert hardcoded fn replacement list into a map with def_id for (path, sugg) in FUNCTION_REPLACEMENTS { - let path_vec: Vec<&str> = path.split("::").collect(); - for did in def_path_def_ids(cx.tcx, &path_vec) { + for did in lookup_path_str(cx.tcx, PathNS::Value, path) { self.sugg_map.insert(did, sugg.map(ToOwned::to_owned)); } } @@ -114,7 +107,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) { if let ItemKind::Static(..) = item.kind && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span) - && self.lazy_static_lazy_static.contains(¯o_call.def_id) + && paths::LAZY_STATIC.matches(cx, macro_call.def_id) && can_use_lazy_cell(cx, self.msrv) { span_lint( @@ -130,7 +123,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { return; } - if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) + if let Some(lazy_info) = LazyInfo::from_item(cx, item) && can_use_lazy_cell(cx, self.msrv) { self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info); @@ -155,9 +148,9 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id() // Is from `once_cell` crate - && self.once_cell_crate.contains(&ty_def_id.krate) + && self.once_cell_crates.contains(&ty_def_id.krate) // And is NOT `once_cell::sync::Lazy` - && !self.once_cell_sync_lazy.contains(&ty_def_id) + && !paths::ONCE_CELL_SYNC_LAZY.matches(cx, ty_def_id) { self.uses_other_once_cell_types = true; } @@ -180,6 +173,8 @@ struct LazyInfo { /// // ^^^^ /// ``` ty_span_no_args: Span, + /// Item on which the lint must be generated. + item_hir_id: HirId, /// `Span` and `DefId` of calls on `Lazy` type. /// i.e.: /// ```ignore @@ -190,12 +185,12 @@ struct LazyInfo { } impl LazyInfo { - fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> { + fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> { // Check if item is a `once_cell:sync::Lazy` static. if let ItemKind::Static(_, ty, _, body_id) = item.kind && let Some(path_def_id) = path_def_id(cx, ty) && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && state.once_cell_sync_lazy.contains(&path_def_id) + && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { let ty_span_no_args = path_span_without_args(path); let body = cx.tcx.hir_body(body_id); @@ -204,7 +199,7 @@ impl LazyInfo { let mut new_fn_calls = FxIndexMap::default(); for_each_expr::<(), ()>(cx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) - && state.once_cell_sync_lazy_new.contains(&fn_did) + && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { new_fn_calls.insert(call_span, fn_did); } @@ -213,6 +208,7 @@ impl LazyInfo { Some(LazyInfo { ty_span_no_args, + item_hir_id: item.hir_id(), calls_span_and_id: new_fn_calls, }) } else { @@ -236,9 +232,10 @@ impl LazyInfo { } } - span_lint_and_then( + span_lint_hir_and_then( cx, NON_STD_LAZY_STATICS, + self.item_hir_id, self.ty_span_no_args, "this type has been superseded by `LazyLock` in the standard library", |diag| { diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index 635f5678e2a..1b8ab1bdedf 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_ast::ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit && let ExprKind::Path(qpath) = &func.kind && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && let ExprKind::MethodCall(rcv_path, receiver, [], _) = &arg.kind - && rcv_path.ident.name.as_str() == "get" + && rcv_path.ident.name == sym::get { let fn_name = cx.tcx.item_name(def_id); let target_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 08fcd17e555..a78a342d4fe 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -335,9 +335,12 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { return; } match &expr.kind { - hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { + hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op.node, lhs, rhs); }, + hir::ExprKind::AssignOp(op, lhs, rhs) => { + self.manage_bin_ops(cx, expr, op.node.into(), lhs, rhs); + }, hir::ExprKind::MethodCall(ps, receiver, args, _) => { self.manage_method_call(args, cx, expr, ps, receiver); }, diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index 1421893274f..d79101a687d 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -1,20 +1,18 @@ use clippy_utils::ast_utils::is_useless_with_eq_exprs; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; -use clippy_utils::{eq_expr_value, is_in_test_function}; +use clippy_utils::{eq_expr_value, is_in_test_function, sym}; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; use super::EQ_OP; pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { - let name = cx.tcx.item_name(macro_call.def_id); + if let Some(macro_call) = first_node_macro_backtrace(cx, e).find(|macro_call| { matches!( - name.as_str(), - "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) - .then(|| (macro_call, name)) }) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) && eq_expr_value(cx, lhs, rhs) && macro_call.is_local() @@ -24,7 +22,10 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - format!("identical args used in this `{macro_name}!` macro call"), + format!( + "identical args used in this `{}!` macro call", + cx.tcx.item_name(macro_call.def_id) + ), ); } } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 01dc6a27c33..ded161c8576 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; +use clippy_utils::{parent_item_name, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -34,7 +34,7 @@ pub(crate) fn check<'tcx>( return; } - if let Some(name) = get_item_name(cx, expr) { + if let Some(name) = parent_item_name(cx, expr) { let name = name.as_str(); if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") { return; @@ -106,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind - && method_name.ident.name.as_str() == "signum" + && method_name.ident.name == sym::signum // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) { diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs index 76eba7327cf..7b98afa9b40 100644 --- a/clippy_lints/src/operators/integer_division.rs +++ b/clippy_lints/src/operators/integer_division.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::symbol::sym; use super::INTEGER_DIVISION; @@ -13,7 +15,8 @@ pub(crate) fn check<'tcx>( ) { if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() - && cx.typeck_results().expr_ty(right).is_integral() + && let right_ty = cx.typeck_results().expr_ty(right) + && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index f758d08d366..d32c062cf56 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -913,9 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Operators { ); }, ExprKind::AssignOp(op, lhs, rhs) => { - self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); - misrefactored_assign_op::check(cx, e, op.node, lhs, rhs); - modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false); + let bin_op = op.node.into(); + self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs); + misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs); + modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false); }, ExprKind::Assign(lhs, rhs, _) => { assign_op_pattern::check(cx, e, lhs, rhs); diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index eebc62e2a5a..ee1d59490ce 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; @@ -69,10 +69,11 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir return ControlFlow::Continue(Descend::Yes); }; if !is_inside_always_const_context(cx.tcx, e.hir_id) - && matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "panic" | "assert" | "assert_eq" | "assert_ne" - ) + && (is_panic(cx, macro_call.def_id) + || matches!( + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) + )) { panics.push(macro_call.span); ControlFlow::Continue(Descend::No) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 39ae9967e01..8962f36db1e 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -113,8 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { ); return; } - match cx.tcx.item_name(macro_call.def_id).as_str() { - "todo" => { + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::todo_macro) => { span_lint( cx, TODO, @@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`todo` should not be present in production code", ); }, - "unimplemented" => { + Some(sym::unimplemented_macro) => { span_lint( cx, UNIMPLEMENTED, @@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`unimplemented` should not be present in production code", ); }, - "unreachable" => { + Some(sym::unreachable_macro) => { span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); }, _ => {}, diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 4c5b8a315d0..19d9acfc930 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -179,7 +179,7 @@ fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mut }; if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) && let [first, ..] = **adjustments - && let ty::Ref(.., mutability) = *first.kind() + && let ty::Ref(.., mutability) = *first.source.kind() { let level = if p.hir_id == pat.hir_id { Level::Top diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index dc142b6e157..da56a785007 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node - && path.ident.name.as_str() == "set_readonly" + && path.ident.name == sym::set_readonly && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) { span_lint_and_then( diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 5fb4d3a837b..9149406642d 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core}; -use hir::LifetimeName; +use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::hir_id::{HirId, HirIdMap}; @@ -432,7 +432,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } None }) { - if let LifetimeName::Param(param_def_id) = lifetime.res + if let LifetimeKind::Param(param_def_id) = lifetime.kind && !lifetime.is_anonymous() && fn_sig .output() @@ -498,29 +498,33 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) { - if let FnRetTy::Return(ty) = sig.decl.output - && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty) - { + let FnRetTy::Return(ty) = sig.decl.output else { return }; + for (out, mutability, out_span) in get_lifetimes(ty) { + if mutability != Some(Mutability::Mut) { + continue; + } let out_region = cx.tcx.named_bound_var(out.hir_id); - let args: Option<Vec<_>> = sig + // `None` if one of the types contains `&'a mut T` or `T<'a>`. + // Else, contains all the locations of `&'a T` types. + let args_immut_refs: Option<Vec<Span>> = sig .decl .inputs .iter() - .filter_map(get_ref_lm) + .flat_map(get_lifetimes) .filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region) - .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span)) + .map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span)) .collect(); - if let Some(args) = args - && !args.is_empty() + if let Some(args_immut_refs) = args_immut_refs + && !args_immut_refs.is_empty() && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value)) { span_lint_and_then( cx, MUT_FROM_REF, - ty.span, + out_span, "mutable borrow from immutable input(s)", |diag| { - let ms = MultiSpan::from_spans(args); + let ms = MultiSpan::from_spans(args_immut_refs); diag.span_note(ms, "immutable borrow here"); }, ); @@ -686,14 +690,38 @@ fn matches_preds<'tcx>( }) } -fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Ref(lt, ref m) = ty.kind { - Some((lt, m.mutbl, ty.span)) - } else { - None +struct LifetimeVisitor<'tcx> { + result: Vec<(&'tcx Lifetime, Option<Mutability>, Span)>, +} + +impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { + if let TyKind::Ref(lt, ref m) = ty.kind { + self.result.push((lt, Some(m.mutbl), ty.span)); + } + hir::intravisit::walk_ty(self, ty); + } + + fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { + if let GenericArg::Lifetime(lt) = generic_arg { + self.result.push((lt, None, generic_arg.span())); + } + hir::intravisit::walk_generic_arg(self, generic_arg); } } +/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not. +/// +/// The second field of the vector's elements indicate if the lifetime is attached to a +/// shared reference, a mutable reference, or neither. +fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option<Mutability>, Span)> { + use hir::intravisit::VisitorExt as _; + + let mut visitor = LifetimeVisitor { result: Vec::new() }; + visitor.visit_ty_unambig(ty); + visitor.result +} + fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::Call(pathexp, []) = expr.kind { path_def_id(cx, pathexp) @@ -715,9 +743,9 @@ fn check_ptr_eq<'tcx>( } // Remove one level of usize conversion if any - let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { - (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (left, right), + let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs, true), + _ => (left, right, false), }; // This lint concerns raw pointers @@ -726,7 +754,12 @@ fn check_ptr_eq<'tcx>( return; } - let (left_var, right_var) = (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) = + (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + + if !(usize_peeled || left_casts_peeled || right_casts_peeled) { + return; + } let mut app = Applicability::MachineApplicable; let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app); @@ -759,8 +792,9 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_> } } -// Peel raw casts if the remaining expression can be coerced to it -fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> &'tcx Expr<'tcx> { +// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been +// peeled or not. +fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) { if !expr.span.from_expansion() && let ExprKind::Cast(inner, _) = expr.kind && let ty::RawPtr(target_ty, _) = expr_ty.kind() @@ -768,8 +802,8 @@ fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() && target_ty == inner_target_ty { - peel_raw_casts(cx, inner, inner_ty) + (peel_raw_casts(cx, inner, inner_ty).0, true) } else { - expr + (expr, false) } } diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 7f74a2fff9f..d8d813f9846 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::fmt; declare_clippy_lint! { @@ -97,7 +97,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.as_str() == "wrapping_offset" { + if path_segment.ident.name == sym::wrapping_offset { return Some((arg_0, arg_1, Method::WrappingOffset)); } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index a80e1f79bbc..c02e5e0621c 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -5,11 +5,12 @@ use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, + span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -21,15 +22,14 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; use rustc_span::symbol::Symbol; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that could be replaced by the question mark operator. + /// Checks for expressions that could be replaced by the `?` operator. /// /// ### Why is this bad? - /// Question mark usage is more idiomatic. + /// Using the `?` operator is shorter and more idiomatic. /// /// ### Example /// ```ignore @@ -46,7 +46,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub QUESTION_MARK, style, - "checks for expressions that could be replaced by the question mark operator" + "checks for expressions that could be replaced by the `?` operator" } pub struct QuestionMark { @@ -144,7 +144,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { && !span_contains_comment(cx.tcx.sess.source_map(), els.span) { let mut applicability = Applicability::MaybeIncorrect; - let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability); + let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); // Take care when binding is `ref` let sugg = if let PatKind::Binding( BindingMode(ByRef::Yes(ref_mutability), binding_mutability), @@ -206,8 +206,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.as_str() == "is_none", - sym::Result => call_sym.as_str() == "is_err", + sym::Option => call_sym == sym::is_none, + sym::Result => call_sym == sym::is_err, _ => false, } }, @@ -279,7 +279,7 @@ fn expr_return_none_or_err( /// } /// ``` /// -/// If it matches, it will suggest to use the question mark operator instead +/// If it matches, it will suggest to use the `?` operator instead fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) && !is_else_clause(cx.tcx, expr) diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs index 0a974bf9d2f..96ea485d769 100644 --- a/clippy_lints/src/question_mark_used.rs +++ b/clippy_lints/src/question_mark_used.rs @@ -7,10 +7,10 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that use the question mark operator and rejects them. + /// Checks for expressions that use the `?` operator and rejects them. /// /// ### Why restrict this? - /// Sometimes code wants to avoid the question mark operator because for instance a local + /// Sometimes code wants to avoid the `?` operator because for instance a local /// block requires a macro to re-throw errors to attach additional information to the /// error. /// @@ -27,7 +27,7 @@ declare_clippy_lint! { #[clippy::version = "1.69.0"] pub QUESTION_MARK_USED, restriction, - "complains if the question mark operator is used" + "checks if the `?` operator is used" } declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]); @@ -40,15 +40,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { } #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - QUESTION_MARK_USED, - expr.span, - "question mark operator was used", - |diag| { - diag.help("consider using a custom macro or match expression"); - }, - ); + span_lint_and_then(cx, QUESTION_MARK_USED, expr.span, "the `?` operator was used", |diag| { + diag.help("consider using a custom macro or match expression"); + }); } } } diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index c6e6e782f9d..6a79cae32a5 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -138,7 +138,7 @@ impl RawStrings { ); }, ); - if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { + if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) { return; } } diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index 6bb7650a7e1..689a2ac4c6a 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::{last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -135,7 +135,7 @@ fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> { if let ty::Adt(adt, _) = *cx.typeck_results().expr_ty(expr).kind() && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::RcWeak | sym::ArcWeak)) { - return Some((Symbol::intern("Weak"), func.span)); + return Some((sym::Weak, func.span)); } } diff --git a/clippy_lints/src/redundant_test_prefix.rs b/clippy_lints/src/redundant_test_prefix.rs new file mode 100644 index 00000000000..84276e32165 --- /dev/null +++ b/clippy_lints/src/redundant_test_prefix.rs @@ -0,0 +1,161 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_test_function; +use clippy_utils::visitors::for_each_expr; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{self as hir, Body, ExprKind, FnDecl}; +use rustc_lexer::is_ident; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, Symbol, edition}; +use std::borrow::Cow; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for test functions (functions annotated with `#[test]`) that are prefixed + /// with `test_` which is redundant. + /// + /// ### Why is this bad? + /// This is redundant because test functions are already annotated with `#[test]`. + /// Moreover, it clutters the output of `cargo test` since test functions are expanded as + /// `module::tests::test_use_case` in the output. Without the redundant prefix, the output + /// becomes `module::tests::use_case`, which is more readable. + /// + /// ### Example + /// ```no_run + /// #[cfg(test)] + /// mod tests { + /// use super::*; + /// + /// #[test] + /// fn test_use_case() { + /// // test code + /// } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[cfg(test)] + /// mod tests { + /// use super::*; + /// + /// #[test] + /// fn use_case() { + /// // test code + /// } + /// } + /// ``` + #[clippy::version = "1.88.0"] + pub REDUNDANT_TEST_PREFIX, + restriction, + "redundant `test_` prefix in test function name" +} + +declare_lint_pass!(RedundantTestPrefix => [REDUNDANT_TEST_PREFIX]); + +impl<'tcx> LateLintPass<'tcx> for RedundantTestPrefix { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'_>, + _decl: &FnDecl<'_>, + body: &'tcx Body<'_>, + _span: Span, + fn_def_id: LocalDefId, + ) { + // Ignore methods and closures. + let FnKind::ItemFn(ref ident, ..) = kind else { + return; + }; + + // Skip the lint if the function is within a macro expansion. + if ident.span.from_expansion() { + return; + } + + // Skip if the function name does not start with `test_`. + if !ident.as_str().starts_with("test_") { + return; + } + + // If the function is not a test function, skip the lint. + if !is_test_function(cx.tcx, fn_def_id) { + return; + } + + span_lint_and_then( + cx, + REDUNDANT_TEST_PREFIX, + ident.span, + "redundant `test_` prefix in test function name", + |diag| { + let non_prefixed = Symbol::intern(ident.as_str().trim_start_matches("test_")); + if is_invalid_ident(non_prefixed) { + // If the prefix-trimmed name is not a valid function name, do not provide an + // automatic fix, just suggest renaming the function. + diag.help( + "consider function renaming (just removing `test_` prefix will produce invalid function name)", + ); + } else { + let (sugg, msg): (Cow<'_, str>, _) = if name_conflicts(cx, body, non_prefixed) { + // If `non_prefixed` conflicts with another function in the same module/scope, + // do not provide an automatic fix, but still emit a fix suggestion. + ( + format!("{non_prefixed}_works").into(), + "consider function renaming (just removing `test_` prefix will cause a name conflict)", + ) + } else { + // If `non_prefixed` is a valid identifier and does not conflict with another function, + // so we can suggest an auto-fix. + (non_prefixed.as_str().into(), "consider removing the `test_` prefix") + }; + diag.span_suggestion(ident.span, msg, sugg, Applicability::MaybeIncorrect); + } + }, + ); + } +} + +/// Checks whether removal of the `_test` prefix from the function name will cause a name conflict. +/// +/// There should be no other function with the same name in the same module/scope. Also, there +/// should not be any function call with the same name within the body of the function, to avoid +/// recursion. +fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: Symbol) -> bool { + let tcx = cx.tcx; + let id = body.id().hir_id; + + // Iterate over items in the same module/scope + let (module, _module_span, _module_hir) = tcx.hir_get_module(tcx.parent_module(id)); + if module + .item_ids + .iter() + .any(|item| matches!(tcx.hir_item(*item).kind, hir::ItemKind::Fn { ident, .. } if ident.name == fn_name)) + { + // Name conflict found + return true; + } + + // Also check that within the body of the function there is also no function call + // with the same name (since it will result in recursion) + for_each_expr(cx, body, |expr| { + if let ExprKind::Path(qpath) = &expr.kind + && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() + && let Some(name) = tcx.opt_item_name(def_id) + && name == fn_name + { + // Function call with the same name found + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} + +fn is_invalid_ident(ident: Symbol) -> bool { + // The identifier is either a reserved keyword, or starts with an invalid sequence. + ident.is_reserved(|| edition::LATEST_STABLE_EDITION) || !is_ident(ident.as_str()) +} diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 9443dca154e..89d945161f6 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -2,8 +2,9 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths::PathLookup; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths}; +use clippy_utils::{path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -76,7 +77,7 @@ declare_clippy_lint! { /// This is documented as an antipattern [on the regex documentation](https://docs.rs/regex/latest/regex/#avoid-re-compiling-regexes-especially-in-a-loop) /// /// ### Example - /// ```no_run + /// ```rust,ignore /// # let haystacks = [""]; /// # const MY_REGEX: &str = "a.b"; /// for haystack in haystacks { @@ -87,7 +88,7 @@ declare_clippy_lint! { /// } /// ``` /// can be replaced with - /// ```no_run + /// ```rust,ignore /// # let haystacks = [""]; /// # const MY_REGEX: &str = "a.b"; /// let regex = regex::Regex::new(MY_REGEX).unwrap(); @@ -121,17 +122,9 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS] impl<'tcx> LateLintPass<'tcx> for Regex { fn check_crate(&mut self, cx: &LateContext<'tcx>) { - // We don't use `match_def_path` here because that relies on matching the exact path, which changed - // between regex 1.8 and 1.9 - // - // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only - // perform the operation once and store the results - let regex_crates = find_crates(cx.tcx, sym!(regex)); - let mut resolve = |path: &[&str], kind: RegexKind| { - for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) { - if let Some(id) = res.opt_def_id() { - self.definitions.insert(id, kind); - } + let mut resolve = |path: &PathLookup, kind: RegexKind| { + for &id in path.get(cx) { + self.definitions.insert(id, kind); } }; diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 4cb73df8b48..122d97fdf81 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::for_each_expr; use clippy_utils::{ binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, + span_find_starting_semi, sym, }; use core::ops::ControlFlow; use rustc_ast::MetaItemInner; @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span, sym}; +use rustc_span::{BytePos, Pos, Span}; use std::borrow::Cow; use std::fmt::Display; @@ -404,15 +404,15 @@ fn check_final_expr<'tcx>( match cx.tcx.hir_attrs(expr.hir_id) { [] => {}, [attr] => { - if matches!(Level::from_attr(attr), Some(Level::Expect(_))) + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) && let metas = attr.meta_item_list() && let Some(lst) = metas && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() && let [tool, lint_name] = meta_item.path.segments.as_slice() && tool.ident.name == sym::clippy && matches!( - lint_name.ident.name.as_str(), - "needless_return" | "style" | "all" | "warnings" + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings ) { // This is an expectation of the `needless_return` lint diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 552135b15fd..226e8ff6adb 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::AssocKind; +use rustc_middle::ty::AssocItem; use rustc_session::declare_lint_pass; use rustc_span::Span; use rustc_span::symbol::Symbol; @@ -85,8 +85,8 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { cx.tcx .associated_items(did) .in_definition_order() - .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn)) - .map(|assoc_item| assoc_item.name) + .filter(|assoc_item| assoc_item.is_fn()) + .map(AssocItem::name) .collect() } else { BTreeSet::new() diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index a8c6518b592..a64b9b22378 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{get_trait_def_id, paths}; +use clippy_utils::paths; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,9 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) - && did == visit_did - { + if paths::SERDE_DE_VISITOR.matches(cx, did) { let mut seen_str = None; let mut seen_string = None; for item in *items { diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index 1185d67b125..ff6e6ef214b 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -3,12 +3,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while}; +use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert { then: then_expr, .. }) = higher::If::hir(expr) - && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) + && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym::contains)//try_parse_contains(cx, cond_expr) && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr) { span_lint( @@ -118,7 +118,7 @@ fn find_insert_calls<'tcx>( expr: &'tcx Expr<'_>, ) -> Option<OpExpr<'tcx>> { for_each_expr(cx, expr, |e| { - if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert)) + if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value) { diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 13d5d4d8d4c..ccb1209c6fc 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -144,7 +144,10 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { Self { cx, type_cache } } - fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { + if !self.cx.tcx.recursion_limit().value_within_limit(depth) { + return false; + } let ty = self .cx .tcx @@ -156,12 +159,12 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { e.insert(false); }, } - let value = self.has_sig_drop_attr_uncached(ty); + let value = self.has_sig_drop_attr_uncached(ty, depth + 1); self.type_cache.insert(ty, value); value } - fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { if let Some(adt) = ty.ty_adt_def() { let mut iter = get_attr( self.cx.sess(), @@ -176,13 +179,13 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Adt(a, b) => { for f in a.all_fields() { let ty = f.ty(self.cx.tcx, b); - if self.has_sig_drop_attr(ty) { + if self.has_sig_drop_attr(ty, depth) { return true; } } for generic_arg in *b { if let GenericArgKind::Type(ty) = generic_arg.unpack() - && self.has_sig_drop_attr(ty) + && self.has_sig_drop_attr(ty, depth) { return true; } @@ -192,7 +195,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Array(ty, _) | rustc_middle::ty::RawPtr(ty, _) | rustc_middle::ty::Ref(_, ty, _) - | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty), + | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty, depth), _ => false, } } @@ -268,7 +271,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { apa.has_expensive_expr_after_last_attr = false; }; let mut ac = AttrChecker::new(self.cx, self.type_cache); - if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) { + if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr), 0) { if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index 1fb54950612..cc497c97a47 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// param * 2 /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub SINGLE_OPTION_MAP, nursery, "Checks for functions with method calls to `.map(_)` on an arg of type `Option` as the outermost expression." diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index 2d989b1cf0b..54d09ff9ee4 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, is_no_std_crate}; +use clippy_utils::{is_no_std_crate, paths}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; @@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { && let Some(start_snippet) = start.span.get_source_text(cx) && let Some(end_snippet) = end.span.get_source_text(cx) { - let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) + let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx) && implements_trait(cx, ty, step_def_id, &[]) { true diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index d26288adb39..30a5fe4db27 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -3,14 +3,13 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, - span_contains_comment, + span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -248,7 +247,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.as_str() == "extend" + && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { self.slow_expression = Some(InitializationType::Extend(expr)); @@ -260,7 +259,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.as_str() == "resize" + && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) { @@ -282,7 +281,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.as_str() == "take" + && take_path.ident.name == sym::take // Check that take is applied to `repeat(0)` && self.is_repeat_zero(recv) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index dd819510f84..73a9fe71e00 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, - peel_blocks, + peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -12,7 +12,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::sym; use std::ops::ControlFlow; @@ -262,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind && let ExprKind::Index(left, right, _) = args.kind && let (method_names, expressions, _) = method_calls(left, 1) - && method_names == [sym!(as_bytes)] + && method_names == [sym::as_bytes] && expressions.len() == 1 && expressions[0].1.is_empty() @@ -287,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if !e.span.in_external_macro(cx.sess().source_map()) && let ExprKind::MethodCall(path, receiver, ..) = &e.kind - && path.ident.name.as_str() == "as_bytes" + && path.ident.name == sym::as_bytes && let ExprKind::Lit(lit) = &receiver.kind && let LitKind::Str(lit_content, _) = &lit.node { @@ -333,9 +332,9 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } if let ExprKind::MethodCall(path, recv, [], _) = &e.kind - && path.ident.name.as_str() == "into_bytes" + && path.ident.name == sym::into_bytes && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind - && matches!(path.ident.name.as_str(), "to_owned" | "to_string") + && matches!(path.ident.name, sym::to_owned | sym::to_string) && let ExprKind::Lit(lit) = &recv.kind && let LitKind::Str(lit_content, _) = &lit.node && lit_content.as_str().is_ascii() @@ -557,7 +556,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.as_str() == "split_whitespace" + && path.ident.name == sym::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/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index fb426e91bf0..83241f97a99 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -5,6 +5,7 @@ use core::ops::ControlFlow; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -56,8 +57,20 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind - && let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node) + match expr.kind { + hir::ExprKind::Binary(op, _, _) => { + check_expr_inner(cx, expr, op.node, op.span); + }, + hir::ExprKind::AssignOp(op, _, _) => { + check_expr_inner(cx, expr, op.node.into(), op.span); + }, + _ => {}, + } + } +} + +fn check_expr_inner<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, binop: hir::BinOpKind, span: Span) { + if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop) && let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang) && let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang) @@ -78,18 +91,17 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { .iter() .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))) && count_binops(body.value) == 1 - { - span_lint( - cx, - lint, - binop.span, - format!( - "suspicious use of `{}` in `{}` impl", - binop.node.as_str(), - cx.tcx.item_name(trait_id) - ), - ); - } + { + span_lint( + cx, + lint, + span, + format!( + "suspicious use of `{}` in `{}` impl", + binop.as_str(), + cx.tcx.item_name(trait_id) + ), + ); } } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index caab4e66a70..e3ecd6508bf 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{AssignOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -306,7 +306,7 @@ fn extract_sides_of_xor_assign<'a, 'hir>( if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::AssignOp( Spanned { - node: BinOpKind::BitXor, + node: AssignOpKind::BitXorAssign, .. }, lhs, diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 9993e6ae18b..7d7d74f27b3 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,11 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_def_path; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_in_const_context, paths, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -33,34 +34,35 @@ declare_clippy_lint! { "`char.is_digit()` is clearer" } -declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); +impl_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); + +pub(crate) struct ToDigitIsSome { + msrv: Msrv, +} + +impl ToDigitIsSome { + pub(crate) fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind - && is_some_path.ident.name.as_str() == "is_some" + && is_some_path.ident.name == sym::is_some { - let match_result = match &to_digit_expr.kind { + let match_result = match to_digit_expr.kind { hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { - if to_digits_path.ident.name.as_str() == "to_digit" - && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) - && *char_arg_ty.kind() == ty::Char + if to_digits_path.ident.name == sym::to_digit + && cx.typeck_results().expr_ty_adjusted(char_arg).is_char() { - Some((true, *char_arg, radix_arg)) + Some((true, char_arg, radix_arg)) } else { None } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind - && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) - && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path( - cx, - to_digits_def_id, - &["core", "char", "methods", "<impl char>", "to_digit"], - ) - { + if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) { Some((false, char_arg, radix_arg)) } else { None @@ -69,7 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { _ => None, }; - if let Some((is_method_call, char_arg, radix_arg)) = match_result { + if let Some((is_method_call, char_arg, radix_arg)) = match_result + && (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_CHAR_IS_DIGIT)) + { let mut applicability = Applicability::MachineApplicable; let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 8aac3a59102..45e54302e32 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -15,7 +15,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -282,18 +282,18 @@ impl TraitBounds { .iter() .copied() .chain(p.bounds.iter()) - .filter_map(get_trait_info_from_bound) - .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) + .map(|bound| snippet_with_applicability(cx, bound.span(), "_", &mut applicability)) .join(" + "); let hint_string = format!( "consider combining the bounds: `{}: {trait_bounds}`", snippet(cx, p.bounded_ty.span, "_"), ); + let ty_name = snippet(cx, p.bounded_ty.span, "_"); span_lint_and_help( cx, TYPE_REPETITION_IN_BOUNDS, bound.span, - "this type has already been used as a bound predicate", + format!("type `{ty_name}` has already been used as a bound predicate"), None, hint_string, ); @@ -395,15 +395,7 @@ impl Hash for ComparableTraitRef<'_, '_> { fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> { if let GenericBound::Trait(t) = bound { let trait_path = t.trait_ref.path; - let trait_span = { - let path_span = trait_path.span; - if let BoundPolarity::Maybe(_) = t.modifiers.polarity { - path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?` - } else { - path_span - } - }; - Some((trait_path.res, trait_path.segments, trait_span)) + Some((trait_path.res, trait_path.segments, t.span)) } else { None } diff --git a/clippy_lints/src/transmute/eager_transmute.rs b/clippy_lints/src/transmute/eager_transmute.rs index 81c0a57083e..535c044f49e 100644 --- a/clippy_lints/src/transmute/eager_transmute.rs +++ b/clippy_lints/src/transmute/eager_transmute.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_normalizable; -use clippy_utils::{eq_expr_value, path_to_local}; +use clippy_utils::{eq_expr_value, path_to_local, sym}; use rustc_abi::WrappingRange; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; @@ -43,7 +42,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.as_str() == "contains" + if path.ident.name == sym::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,11 +80,9 @@ 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.as_str() == "then_some" + && path.ident.name == sym::then_some && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) - && is_normalizable(cx, cx.param_env, from_ty) - && is_normalizable(cx, cx.param_env, to_ty) // we only want to lint if the target type has a niche that is larger than the one of the source type // e.g. `u8` to `NonZero<u8>` should lint, but `NonZero<u8>` to `u8` should not && let Ok(from_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(from_ty)) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index f2da8d35cb4..d5112e2c3f9 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -1,13 +1,9 @@ mod crosspointer_transmute; mod eager_transmute; mod missing_transmute_annotations; -mod transmute_float_to_int; mod transmute_int_to_bool; -mod transmute_int_to_char; -mod transmute_int_to_float; mod transmute_int_to_non_zero; mod transmute_null_to_fn; -mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; mod transmute_ref_to_ref; @@ -143,40 +139,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for transmutes from an integer to a `char`. - /// - /// ### Why is this bad? - /// Not every integer is a Unicode scalar value. - /// - /// ### Known problems - /// - [`from_u32`] which this lint suggests using is slower than `transmute` - /// as it needs to validate the input. - /// If you are certain that the input is always a valid Unicode scalar value, - /// use [`from_u32_unchecked`] which is as fast as `transmute` - /// but has a semantically meaningful name. - /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`. - /// - /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html - /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html - /// - /// ### Example - /// ```no_run - /// let x = 1_u32; - /// unsafe { - /// let _: char = std::mem::transmute(x); // where x: u32 - /// } - /// - /// // should be: - /// let _ = std::char::from_u32(x).unwrap(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TRANSMUTE_INT_TO_CHAR, - complexity, - "transmutes from an integer to a `char`" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for transmutes from a `&[u8]` to a `&str`. /// /// ### Why is this bad? @@ -234,29 +196,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for transmutes from an integer to a float. - /// - /// ### Why is this bad? - /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive - /// and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: f32 = std::mem::transmute(1_u32); // where x: u32 - /// } - /// - /// // should be: - /// let _: f32 = f32::from_bits(1_u32); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TRANSMUTE_INT_TO_FLOAT, - complexity, - "transmutes from an integer to a float" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for transmutes from `T` to `NonZero<T>`, and suggests the `new_unchecked` /// method instead. /// @@ -282,52 +221,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for transmutes from a float to an integer. - /// - /// ### Why is this bad? - /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive - /// and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: u32 = std::mem::transmute(1f32); - /// } - /// - /// // should be: - /// let _: u32 = 1f32.to_bits(); - /// ``` - #[clippy::version = "1.41.0"] - pub TRANSMUTE_FLOAT_TO_INT, - complexity, - "transmutes from a float to an integer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from a number to an array of `u8` - /// - /// ### Why this is bad? - /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes` - /// is intuitive and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let x: [u8; 8] = std::mem::transmute(1i64); - /// } - /// - /// // should be - /// let x: [u8; 8] = 0i64.to_ne_bytes(); - /// ``` - #[clippy::version = "1.58.0"] - pub TRANSMUTE_NUM_TO_BYTES, - complexity, - "transmutes from a number to an array of `u8`" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for transmutes from a pointer to a pointer, or /// from a reference to a reference. /// @@ -581,13 +474,9 @@ impl_lint_pass!(Transmute => [ TRANSMUTE_PTR_TO_PTR, USELESS_TRANSMUTE, WRONG_TRANSMUTE, - TRANSMUTE_INT_TO_CHAR, TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, - TRANSMUTE_INT_TO_FLOAT, TRANSMUTE_INT_TO_NON_ZERO, - TRANSMUTE_FLOAT_TO_INT, - TRANSMUTE_NUM_TO_BYTES, UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, @@ -632,14 +521,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) - | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) - | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) || transmute_undefined_repr::check(cx, e, from_ty, to_ty)) | (eager_transmute::check(cx, e, arg, from_ty, to_ty)); diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs deleted file mode 100644 index df2f681a162..00000000000 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::TRANSMUTE_FLOAT_TO_INT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, UnOp}; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_float_to_int` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - mut arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) - if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => - { - span_lint_and_then( - cx, - TRANSMUTE_FLOAT_TO_INT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let mut sugg = sugg::Sugg::hir(cx, arg, ".."); - - if let ExprKind::Unary(UnOp::Neg, inner_expr) = &arg.kind { - arg = inner_expr; - } - - if let ExprKind::Lit(lit) = &arg.kind - // if the expression is a float literal and it is unsuffixed then - // add a suffix so the suggestion is valid and unambiguous - && let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node - { - let op = format!("{sugg}{}", float_ty.name_str()).into(); - match sugg { - sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), - _ => sugg = sugg::Sugg::NonParen(op), - } - } - - sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_paren()).into()); - - // cast the result of `to_bits` if `to_ty` is signed - sugg = if let ty::Int(int_ty) = to_ty.kind() { - sugg.as_ty(int_ty.name_str().to_string()) - } else { - sugg - }; - - diag.span_suggestion(e.span, "consider using", sugg, Applicability::Unspecified); - }, - ); - true - }, - _ => false, - } -} diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs deleted file mode 100644 index 81d10a7d5bd..00000000000 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::TRANSMUTE_INT_TO_CHAR; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{std_or_core, sugg}; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_int_to_char` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_CHAR, - e.span, - format!("transmute from a `{from_ty}` to a `char`"), - |diag| { - let Some(top_crate) = std_or_core(cx) else { return }; - let arg = sugg::Sugg::hir(cx, arg, ".."); - let arg = if let ty::Int(_) = from_ty.kind() { - arg.as_ty(ast::UintTy::U32.name_str()) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("{top_crate}::char::from_u32({arg}).unwrap()"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs deleted file mode 100644 index aaa95396b4b..00000000000 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_INT_TO_FLOAT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_int_to_float` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_FLOAT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - let arg = if let ty::Int(int_ty) = from_ty.kind() { - arg.as_ty(format!( - "u{}", - int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) - )) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("{to_ty}::from_bits({arg})"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs deleted file mode 100644 index d72be270b73..00000000000 --- a/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_NUM_TO_BYTES; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty, UintTy}; - -/// Checks for `transmute_int_to_float` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => { - if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) { - return false; - } - if matches!(from_ty.kind(), ty::Float(_)) && const_context && !msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) - { - return false; - } - - span_lint_and_then( - cx, - TRANSMUTE_NUM_TO_BYTES, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - diag.span_suggestion( - e.span, - "consider using `to_ne_bytes()`", - format!("{arg}.to_ne_bytes()"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 3147058b4cd..c1c7cc51656 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -385,7 +385,7 @@ declare_clippy_lint! { /// ```no_run /// let right: std::borrow::Cow<'_, [u8]>; /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.87.0"] pub OWNED_COW, style, "needlessly owned Cow type" diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 769244c675e..f13042a6fa6 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -19,61 +19,58 @@ pub(super) fn check<'tcx>( def_id: DefId, box_size_threshold: u64, ) -> bool { - if cx.tcx.is_diagnostic_item(sym::Vec, def_id) { - if let Some(last) = last_path_segment(qpath).args - // Get the _ part of Vec<_> - && let Some(GenericArg::Type(ty)) = last.args.first() - // extract allocator from the Vec for later - && let vec_alloc_ty = last.args.get(1) - // ty is now _ at this point - && let TyKind::Path(ref ty_qpath) = ty.kind - && let res = cx.qpath_res(ty_qpath, ty.hir_id) - && let Some(def_id) = res.opt_def_id() - && Some(def_id) == cx.tcx.lang_items().owned_box() - // At this point, we know ty is Box<T>, now get T - && let Some(last) = last_path_segment(ty_qpath).args - && let Some(GenericArg::Type(boxed_ty)) = last.args.first() - // extract allocator from the Box for later - && let boxed_alloc_ty = last.args.get(1) - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) - && !ty_ty.has_escaping_bound_vars() - && ty_ty.is_sized(cx.tcx, cx.typing_env()) - && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) - && ty_ty_size < box_size_threshold - // https://github.com/rust-lang/rust-clippy/issues/7114 - && match (vec_alloc_ty, boxed_alloc_ty) { - (None, None) => true, - // this is in the event that we have something like - // Vec<_, Global>, in which case is equivalent to - // Vec<_> - (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { - if let TyKind::Path(path) = inner.kind - && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { - cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) - } else { - false - } - }, - (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), - _ => false - } - { - span_lint_and_sugg( - cx, - VEC_BOX, - hir_ty.span, - "`Vec<T>` is already on the heap, the boxing is unnecessary", - "try", - format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), - Applicability::Unspecified, - ); - true - } else { - false + if cx.tcx.is_diagnostic_item(sym::Vec, def_id) + && let Some(last) = last_path_segment(qpath).args + // Get the _ part of Vec<_> + && let Some(GenericArg::Type(ty)) = last.args.first() + // extract allocator from the Vec for later + && let vec_alloc_ty = last.args.get(1) + // ty is now _ at this point + && let TyKind::Path(ref ty_qpath) = ty.kind + && let res = cx.qpath_res(ty_qpath, ty.hir_id) + && let Some(def_id) = res.opt_def_id() + && Some(def_id) == cx.tcx.lang_items().owned_box() + // At this point, we know ty is Box<T>, now get T + && let Some(last) = last_path_segment(ty_qpath).args + && let Some(GenericArg::Type(boxed_ty)) = last.args.first() + // extract allocator from the Box for later + && let boxed_alloc_ty = last.args.get(1) + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) + && !ty_ty.has_escaping_bound_vars() + && ty_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) + && ty_ty_size < box_size_threshold + // https://github.com/rust-lang/rust-clippy/issues/7114 + && match (vec_alloc_ty, boxed_alloc_ty) { + (None, None) => true, + // this is in the event that we have something like + // Vec<_, Global>, in which case is equivalent to + // Vec<_> + (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { + if let TyKind::Path(path) = inner.kind + && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { + cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) + } else { + false + } + }, + (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), + _ => false } + { + span_lint_and_sugg( + cx, + VEC_BOX, + hir_ty.span, + "`Vec<T>` is already on the heap, the boxing is unnecessary", + "try", + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), + Applicability::Unspecified, + ); + true } else { false } diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index e07257f4aef..d321c48f6af 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -10,7 +10,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::symbol::{Ident, kw}; use rustc_span::{Span, sym}; @@ -344,7 +344,7 @@ impl UnconditionalRecursion { .in_definition_order() // We're not interested in foreign implementations of the `Default` trait. .find(|item| { - item.kind == AssocKind::Fn && item.def_id.is_local() && item.name == kw::Default + item.is_fn() && item.def_id.is_local() && item.name() == kw::Default }) && let Some(body_node) = cx.tcx.hir_get_if_local(assoc_item.def_id) && let Some(body_id) = body_node.body_id() diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 7803d5115c9..cee4a53f03c 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { @@ -187,7 +187,7 @@ fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) - && path.ident.name.as_str() == "reserve" + && path.ident.name == sym::reserve } /// Returns self if the expression is `Vec::set_len()` @@ -209,7 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); if is_type_diagnostic_item(cx, self_type, sym::Vec) - && path.ident.name.as_str() == "set_len" + && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 6dcc1195a70..48b532968cb 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -7,11 +8,12 @@ use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(macro_call) = root_macro_call_first_node(cx, expr) { - let macro_name = cx.tcx.item_name(macro_call.def_id); - let result = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + let result = match diag_name { + sym::assert_eq_macro | sym::debug_assert_eq_macro => "succeed", + sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail", _ => return, }; let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -24,7 +26,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - format!("`{macro_name}` of unit values detected. This will always {result}"), + format!( + "`{}` of unit values detected. This will always {result}", + cx.tcx.item_name(macro_call.def_id) + ), ); } return; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index bcd05cceca9..54a7efc090a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; @@ -78,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { fn_kind: FnKind<'tcx>, fn_decl: &FnDecl<'tcx>, body: &Body<'tcx>, - span: Span, + _span: Span, def_id: LocalDefId, ) { // Abort if public function/method or closure. @@ -147,19 +149,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { "remove the return type...".to_string(), // FIXME: we should instead get the span including the `->` and suggest an // empty string for this case. - "()".to_string(), - "...and then remove returned values", + Cow::Borrowed("()"), + Cow::Borrowed("...and then remove returned values"), ) } else { + let wrapper = if lang_item == OptionSome { "Some" } else { "Ok" }; ( format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), format!("remove `{return_type_label}` from the return type..."), - inner_type.to_string(), - "...and then change returning expressions", + Cow::Owned(inner_type.to_string()), + Cow::Owned(format!( + "...and then remove the surrounding `{wrapper}()` from returning expressions" + )), ) }; - span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg, |diag| { + span_lint_and_then(cx, UNNECESSARY_WRAPS, cx.tcx.def_span(def_id), lint_msg, |diag| { diag.span_suggestion( fn_decl.output.span(), return_type_sugg_msg, diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 1e2d7db7431..9ad184450de 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -231,6 +231,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`. let changed = match &mut focus_kind { + Missing => unreachable!(), // These pattern forms are "leafs" and do not have sub-patterns. // Therefore they are not some form of constructor `C`, // with which a pattern `C(p_0)` may be formed, diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 1c1c841e964..8ceaa3dc58e 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource}; +use rustc_hir::{Body, Defaultness, Expr, ExprKind, FnDecl, HirId, Node, TraitItem, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; @@ -116,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, def_id: LocalDefId, ) { - if !span.from_expansion() && fn_kind.asyncness().is_async() && !is_def_id_trait_method(cx, def_id) { + if !span.from_expansion() + && fn_kind.asyncness().is_async() + && !is_def_id_trait_method(cx, def_id) + && !is_default_trait_impl(cx, def_id) + { let mut visitor = AsyncFnVisitor { cx, found_await: false, @@ -189,3 +193,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { } } } + +fn is_default_trait_impl(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + matches!( + cx.tcx.hir_node_by_def_id(def_id), + Node::TraitItem(TraitItem { + defaultness: Defaultness::Default { .. }, + .. + }) + ) +} diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 2d88c490b1a..5e1cb9e54f5 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/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_def_path, match_trait_method, paths, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, paths, peel_blocks}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -93,14 +93,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { return; } - let async_paths: [&[&str]; 4] = [ + let async_paths = [ &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)) { + if async_paths.into_iter().any(|path| path.matches(cx, trait_id)) { return; } } @@ -226,7 +226,7 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if is_panic(cx, macro_call.def_id) { return !cx.tcx.hir_is_inside_const_context(expr.hir_id); } - matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable") + cx.tcx.is_diagnostic_item(sym::unreachable_macro, macro_call.def_id) } fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { @@ -291,19 +291,28 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> { }, }; - match ( - is_trait_method(cx, call, sym::IoRead), - is_trait_method(cx, call, sym::IoWrite), - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT), - match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) - || match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT), - ) { - (true, _, _, _) => Some(IoOp::SyncRead(vectorized)), - (_, true, _, _) => Some(IoOp::SyncWrite(vectorized)), - (_, _, true, _) => Some(IoOp::AsyncRead(vectorized)), - (_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)), - _ => None, + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id) + && let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id) + { + if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) { + match diag_name { + sym::IoRead => Some(IoOp::SyncRead(vectorized)), + sym::IoWrite => Some(IoOp::SyncWrite(vectorized)), + _ => None, + } + } else if paths::FUTURES_IO_ASYNCREADEXT.matches(cx, trait_def_id) + || paths::TOKIO_IO_ASYNCREADEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncRead(vectorized)) + } else if paths::TOKIO_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + || paths::FUTURES_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncWrite(vectorized)) + } else { + None + } + } else { + None } } diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 582aa6e6001..12da891a71b 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::sym; use clippy_utils::visitors::is_local_used; use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -61,12 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let contains_todo = |cx, body: &'_ Body<'_>| -> bool { clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| { - if let Some(macro_call) = root_macro_call_first_node(cx, e) { - if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) + { + ControlFlow::Break(()) } else { ControlFlow::Continue(()) } @@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { .is_some() }; if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind - && assoc_item.fn_has_self_parameter + && assoc_item.is_method() && let ImplItemKind::Fn(.., body_id) = &impl_item.kind && (!cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api) && let body = cx.tcx.hir_body(*body_id) diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index d5309aade7a..9859ddfdf7b 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,11 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; -use rustc_ast::visit::FnKind; -use rustc_ast::{ClosureBinder, ast}; +use clippy_utils::{is_never_expr, is_unit_expr}; +use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term, + Ty, TyKind, +}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::edition::Edition; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -34,27 +41,89 @@ declare_clippy_lint! { declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); -impl EarlyLintPass for UnusedUnit { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output - && let ast::TyKind::Tup(ref vals) = ty.kind - && vals.is_empty() - && !ty.span.from_expansion() - && get_def(span) == get_def(ty.span) +impl<'tcx> LateLintPass<'tcx> for UnusedUnit { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + span: Span, + def_id: LocalDefId, + ) { + if let FnRetTy::Return(hir_ty) = decl.output + && is_unit_ty(hir_ty) + && !hir_ty.span.from_expansion() + && get_def(span) == get_def(hir_ty.span) { // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + if let FnKind::Closure = kind + && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id) + && let ExprKind::Closure(closure) = expr.kind + && !closure.bound_generic_params.is_empty() + { + return; + } + + // unit never type fallback is no longer supported since Rust 2024. For more information, + // see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html> + if cx.tcx.sess.edition() >= Edition::Edition2024 + && let ExprKind::Block(block, _) = body.value.kind + && let Some(expr) = block.expr + && is_never_expr(cx, expr).is_some() + { return; } - lint_unneeded_unit_return(cx, ty, span); + lint_unneeded_unit_return(cx, hir_ty.span, span); } } - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() - && let ast::StmtKind::Expr(ref expr) = stmt.kind + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Ret(Some(expr)) | ExprKind::Break(_, Some(expr)) = expr.kind && is_unit_expr(expr) + && !expr.span.from_expansion() + { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + + fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { + let segments = &poly.trait_ref.path.segments; + + if segments.len() == 1 + && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) + && let Some(args) = segments[0].args + && args.parenthesized == GenericArgsParentheses::ParenSugar + && let constraints = &args.constraints + && constraints.len() == 1 + && constraints[0].ident.name == sym::Output + && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind + && args.span_ext.hi() != poly.span.hi() + && !hir_ty.span.from_expansion() + && is_unit_ty(hir_ty) + { + lint_unneeded_unit_return(cx, hir_ty.span, poly.span); + } + } +} + +impl EarlyLintPass for UnusedUnit { + /// Check for unit expressions in blocks. This is left in the early pass because some macros + /// expand its inputs as-is, making it invisible to the late pass. See #4076. + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + if let Some(stmt) = block.stmts.last() + && let StmtKind::Expr(expr) = &stmt.kind + && let rustc_ast::ExprKind::Tup(inner) = &expr.kind + && inner.is_empty() && let ctxt = block.span.ctxt() && stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt @@ -72,39 +141,10 @@ impl EarlyLintPass for UnusedUnit { ); } } +} - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - match e.kind { - ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { - if is_unit_expr(expr) && !expr.span.from_expansion() { - span_lint_and_sugg( - cx, - UNUSED_UNIT, - expr.span, - "unneeded `()`", - "remove the `()`", - String::new(), - Applicability::MachineApplicable, - ); - } - }, - _ => (), - } - } - - fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef) { - let segments = &poly.trait_ref.path.segments; - - if segments.len() == 1 - && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) - && let Some(args) = &segments[0].args - && let ast::GenericArgs::Parenthesized(generic_args) = &**args - && let ast::FnRetTy::Ty(ty) = &generic_args.output - && ty.kind.is_unit() - { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } - } +fn is_unit_ty(ty: &Ty<'_>) -> bool { + matches!(ty.kind, TyKind::Tup([])) } // get the def site @@ -117,24 +157,15 @@ fn get_def(span: Span) -> Option<Span> { } } -// is this expr a `()` unit? -fn is_unit_expr(expr: &ast::Expr) -> bool { - if let ast::ExprKind::Tup(ref vals) = expr.kind { - vals.is_empty() - } else { - false - } -} - -fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { +fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) { let (ret_span, appl) = - span.with_hi(ty.span.hi()) + span.with_hi(ty_span.hi()) .get_source_text(cx) - .map_or((ty.span, Applicability::MaybeIncorrect), |src| { - position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + .map_or((ty_span, Applicability::MaybeIncorrect), |src| { + position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| { ( #[expect(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, ) }) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index b466a8e127a..c641d4e55b9 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{higher, path_to_local}; +use clippy_utils::{higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -12,7 +12,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -111,7 +111,7 @@ struct UnwrapInfo<'tcx> { /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, /// The check's name, like `is_ok` - check_name: &'tcx PathSegment<'tcx>, + check_name: Symbol, /// The branch where the check takes place, like `if x.is_ok() { .. }` branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). @@ -133,12 +133,12 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec<UnwrapInfo<'tcx>> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && ["is_some", "is_none"].contains(&method_name) + fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) } - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && ["is_ok", "is_err"].contains(&method_name) + fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) } if let ExprKind::Binary(op, left, right) = &expr.kind { @@ -155,14 +155,10 @@ fn collect_unwrap_info<'tcx>( } else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind && let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) - && let name = method_name.ident.as_str() + && let name = method_name.ident.name && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) { - let unwrappable = match name { - "is_some" | "is_ok" => true, - "is_err" | "is_none" => false, - _ => unreachable!(), - }; + let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { UnwrappableKind::Option @@ -174,7 +170,7 @@ fn collect_unwrap_info<'tcx>( local_id, if_expr, check: expr, - check_name: method_name, + check_name: name, branch, safe_to_unwrap, kind, @@ -184,12 +180,12 @@ fn collect_unwrap_info<'tcx>( Vec::new() } -/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated, -/// *except* for if `Option::as_mut` is called. +/// A HIR visitor delegate that checks if a local variable of type `Option` or `Result` is mutated, +/// *except* for if `.as_mut()` is called. /// The reason for why we allow that one specifically is that `.as_mut()` cannot change -/// the option to `None`, and that is important because this lint relies on the fact that +/// the variant, and that is important because this lint relies on the fact that /// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if -/// the option is changed to None between `is_some` and `unwrap`. +/// the option is changed to None between `is_some` and `unwrap`, ditto for `Result`. /// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) struct MutationVisitor<'tcx> { is_mutated: bool, @@ -198,17 +194,17 @@ struct MutationVisitor<'tcx> { } /// Checks if the parent of the expression pointed at by the given `HirId` is a call to -/// `Option::as_mut`. +/// `.as_mut()`. /// /// Used by the mutation visitor to specifically allow `.as_mut()` calls. /// In particular, the `HirId` that the visitor receives is the id of the local expression /// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent /// expression: that will be where the actual method call is. -fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { +fn is_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind { - path.ident.name.as_str() == "as_mut" + path.ident.name == sym::as_mut } else { false } @@ -218,14 +214,16 @@ 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::Mutable = bk && is_potentially_local_place(self.local_id, &cat.place) - && !is_option_as_mut_use(self.tcx, diag_expr_id) + && !is_as_mut_use(self.tcx, diag_expr_id) { self.is_mutated = true; } } - fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) { - self.is_mutated = true; + fn mutate(&mut self, cat: &PlaceWithHirId<'tcx>, _: HirId) { + if is_potentially_local_place(self.local_id, &cat.place) { + self.is_mutated = true; + } } fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} @@ -276,12 +274,10 @@ enum AsRefKind { /// If it isn't, the expression itself is returned. fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option<AsRefKind>) { if let ExprKind::MethodCall(path, recv, [], _) = expr.kind { - if path.ident.name == sym::as_ref { - (recv, Some(AsRefKind::AsRef)) - } else if path.ident.name.as_str() == "as_mut" { - (recv, Some(AsRefKind::AsMut)) - } else { - (expr, None) + match path.ident.name { + sym::as_ref => (recv, Some(AsRefKind::AsRef)), + sym::as_mut => (recv, Some(AsRefKind::AsMut)), + _ => (expr, None), } } else { (expr, None) @@ -296,6 +292,10 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { return; } + // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. + if matches!(expr.kind, ExprKind::Closure(_)) { + return; + } if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) { walk_expr(self, cond); self.visit_branch(expr, cond, then, false); @@ -307,8 +307,8 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) && let Some(id) = path_to_local(self_arg) - && [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name) - && let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name) + && matches!(method_name.ident.name, sym::unwrap | sym::expect | sym::unwrap_err) + && let call_to_unwrap = matches!(method_name.ident.name, sym::unwrap | sym::expect) && let Some(unwrappable) = self.unwrappables.iter() .find(|u| u.local_id == id) // Span contexts should not differ with the conditional branch @@ -332,8 +332,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { expr.span, format!( "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", - method_name.ident.name, - unwrappable.check_name.ident.as_str(), + method_name.ident.name, unwrappable.check_name, ), |diag| { if is_entire_condition { diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 3a9c997a579..c04fcf622b9 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -7,7 +7,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; @@ -298,6 +298,33 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. if same_type_and_consts(a, b) && !is_copy(cx, b) { + // Below we check if the parent method call meets the following conditions: + // 1. First parameter is `&mut self` (requires mutable reference) + // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) + // For methods satisfying these conditions (like any), .into_iter() must be preserved. + if let Some(parent) = get_parent_expr(cx, e) + && let ExprKind::MethodCall(_, recv, _, _) = parent.kind + && recv.hir_id == e.hir_id + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let sig = cx.tcx.fn_sig(def_id).skip_binder().skip_binder() + && let inputs = sig.inputs() + && inputs.len() >= 2 + && let Some(self_ty) = inputs.first() + && let ty::Ref(_, _, Mutability::Mut) = self_ty.kind() + && let Some(second_ty) = inputs.get(1) + && let predicates = cx.tcx.param_env(def_id).caller_bounds() + && predicates.iter().any(|pred| { + if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() { + trait_pred.self_ty() == *second_ty + && cx.tcx.lang_items().fn_mut_trait() == Some(trait_pred.def_id()) + } else { + false + } + }) + { + return; + } + let sugg = snippet(cx, recv.span, "<expr>").into_owned(); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 4309cd2c9ab..812c4df4ddd 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,16 +1,18 @@ -use clippy_utils::{get_attr, higher}; +use clippy_utils::{MaybePath, get_attr, higher, path_def_id}; +use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; use rustc_hir::{ self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, - FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; -use std::fmt::{Display, Formatter, Write as _}; +use std::fmt::{Display, Formatter}; declare_lint_pass!( /// ### What it does @@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, } } +fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String { + cx.get_def_path(id) + .iter() + .map(Symbol::as_str) + .filter(|s| !s.starts_with('<')) + .join("_") + .to_uppercase() +} + struct Binding<T> { name: String, value: T, @@ -257,11 +268,44 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath(&self, qpath: &Binding<&QPath<'_>>) { + fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Ok(path) = path_to_string(qpath.value) { - chain!(self, "match_qpath({qpath}, &[{}])", path); + } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + && !def_id.is_local() + { + bind!(self, def_id); + chain!( + self, + "let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()" + ); + if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) { + chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})"); + } else { + chain!( + self, + "paths::{}.matches(cx, {def_id}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, def_id.value) + ); + } + } + } + + fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { + if let Some(id) = path_def_id(self.cx, path.value) + && !id.is_local() + { + if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { + chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name()); + } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { + chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); + } else { + chain!( + self, + "paths::{}.matches_path(cx, {path}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, id) + ); + } } } @@ -270,7 +314,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ConstArgKind::Path(ref qpath) => { bind!(self, qpath); chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind"); - self.qpath(qpath); }, ConstArgKind::Anon(anon_const) => { bind!(self, anon_const); @@ -394,12 +437,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, let_expr); kind!("Let({let_expr})"); 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(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); + if let Some(ty) = let_expr.value.ty { + bind!(self, ty); + chain!(self, "let Some({ty}) = {let_expr}.ty"); + self.maybe_path(ty); } self.expr(field!(let_expr.init)); }, @@ -451,11 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Cast(expr, cast_ty) => { bind!(self, expr, cast_ty); kind!("Cast({expr}, {cast_ty})"); - if let TyKind::Path(ref qpath) = cast_ty.value.kind { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); - self.qpath(qpath); - } + self.maybe_path(cast_ty); self.expr(expr); }, ExprKind::Type(expr, _ty) => { @@ -561,10 +598,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(object); self.expr(index); }, - ExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); + ExprKind::Path(_) => { + self.maybe_path(expr); }, ExprKind::AddrOf(kind, mutability, inner) => { bind!(self, inner); @@ -608,7 +643,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath); + self.qpath(qpath, expr); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -648,7 +683,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } - fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); @@ -657,15 +692,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatExprKind::Lit { lit, negated } => { bind!(self, lit); bind!(self, negated); - kind!("Lit{{ref {lit}, {negated} }}"); + kind!("Lit {{ ref {lit}, {negated} }}"); self.lit(lit); }, PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), - PatExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, + PatExprKind::Path(_) => self.maybe_path(pat), } } @@ -676,6 +707,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } match pat.value.kind { + PatKind::Missing => unreachable!(), PatKind::Wild => kind!("Wild"), PatKind::Never => kind!("Never"), PatKind::Binding(ann, _, name, sub) => { @@ -696,7 +728,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Struct(ref qpath, fields, ignore) => { bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -710,7 +742,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::TupleStruct(ref qpath, fields, skip_pos) => { bind!(self, qpath, fields); kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { @@ -742,13 +774,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Expr(lit_expr) => { bind!(self, lit_expr); kind!("Expr({lit_expr})"); - self.pat_expr(lit_expr); + self.pat_expr(lit_expr, pat); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.pat_expr(e)); - end.if_some(|e| self.pat_expr(e)); + start.if_some(|e| self.pat_expr(e, pat)); + end.if_some(|e| self.pat_expr(e, pat)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); @@ -796,32 +828,3 @@ fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); get_attr(cx.sess(), attrs, "author").count() > 0 } - -fn path_to_string(path: &QPath<'_>) -> Result<String, ()> { - fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> { - match *path { - QPath::Resolved(_, path) => { - for (i, segment) in path.segments.iter().enumerate() { - if i > 0 { - *s += ", "; - } - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - } - }, - QPath::TypeRelative(ty, segment) => match &ty.kind { - TyKind::Path(inner_path) => { - inner(s, inner_path)?; - *s += ", "; - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - }, - other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), - }, - QPath::LangItem(..) => return Err(()), - } - - Ok(()) - } - let mut s = String::new(); - inner(&mut s, path)?; - Ok(s) -} diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs deleted file mode 100644 index deb983b6971..00000000000 --- a/clippy_lints/src/utils/internal_lints.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod almost_standard_lint_formulation; -pub mod collapsible_calls; -pub mod interning_defined_symbol; -pub mod invalid_paths; -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/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs deleted file mode 100644 index 044894f8279..00000000000 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ /dev/null @@ -1,241 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::match_type; -use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::ConstValue; -use rustc_middle::ty; -use rustc_session::impl_lint_pass; -use rustc_span::sym; -use rustc_span::symbol::Symbol; - -use std::borrow::Cow; - -declare_clippy_lint! { - /// ### What it does - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// ### Why is this bad? - /// It's faster and easier to use the symbol constant. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for unnecessary conversion from Symbol to a string. - /// - /// ### Why is this bad? - /// It's faster use symbols directly instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap<u32, DefId>, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - for def_id in def_path_def_ids(cx.tcx, module) { - for item in cx.tcx.module_children(def_id) { - if let Res::Def(DefKind::Const, item_def_id) = item.res - && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() - && match_type(cx, ty, &paths::SYMBOL) - && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) - && let Some(value) = value.to_u32().discard_err() - { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Call(func, [arg]) = &expr.kind - && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() - && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) - && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) - && let value = Symbol::intern(&arg).as_u32() - && let Some(&def_id) = self.symbol_map.get(&value) - { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } - if let ExprKind::Binary(op, left, right) = expr.kind - && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) - { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } -} - -impl InterningDefinedSymbol { - fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[&paths::SYMBOL_AS_STR, &paths::SYMBOL_TO_IDENT_STRING]; - let call = if let ExprKind::AddrOf(_, _, e) = expr.kind - && let ExprKind::Unary(UnOp::Deref, e) = e.kind - { - e - } else { - expr - }; - if let ExprKind::MethodCall(_, item, [], _) = call.kind - // is a method call - && let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id) - && let ty = cx.typeck_results().expr_ty(item) - // ...on either an Ident or a Symbol - && let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { - Some(false) - } else if match_type(cx, ty, &paths::IDENT) { - Some(true) - } else { - None - } - // ...which converts it to a string - && let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS } - && let Some(is_to_owned) = paths - .iter() - .find_map(|path| if match_def_path(cx, did, path) { - Some(path == &paths::SYMBOL_TO_IDENT_STRING) - } else { - None - }) - .or_else(|| if cx.tcx.is_diagnostic_item(sym::to_string_method, did) { - Some(true) - } else { - None - }) - { - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - // is a string constant - if let Some(Constant::Str(s)) = ConstEvalCtxt::new(cx).eval_simple(expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs deleted file mode 100644 index 5e1872ad975..00000000000 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ /dev/null @@ -1,106 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::def_path_res; -use clippy_utils::diagnostics::span_lint; -use rustc_hir as hir; -use rustc_hir::Item; -use rustc_hir::def::DefKind; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, FloatTy}; -use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; - -declare_clippy_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub INVALID_PATHS, - internal, - "invalid path" -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if mod_name.as_str() == "paths" - && let hir::ItemKind::Const(.., body_id) = item.kind - && let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env( - cx.tcx, - ty::TypingEnv::post_analysis(cx.tcx, item.owner_id), - cx.tcx.typeck(item.owner_id), - ) - .eval_simple(cx.tcx.hir_body(body_id).value) - && let Some(path) = path - .iter() - .map(|x| { - if let Constant::Str(s) = x { - Some(s.as_str()) - } else { - None - } - }) - .collect::<Option<Vec<&str>>>() - && !check_path(cx, &path[..]) - { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if !def_path_res(cx.tcx, path).is_empty() { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedType::Float(FloatTy::F32), - SimplifiedType::Float(FloatTy::F64), - SimplifiedType::Slice, - SimplifiedType::Str, - SimplifiedType::Bool, - SimplifiedType::Char, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) - .copied(); - for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(item_def_id); - if path_syms.starts_with(&lang_item_path) - && let [item] = &path_syms[lang_item_path.len()..] - { - if matches!( - cx.tcx.def_kind(item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - - false -} diff --git a/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs deleted file mode 100644 index b8bcb9b3756..00000000000 --- a/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs +++ /dev/null @@ -1,74 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::match_type; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; - -declare_clippy_lint! { - /// ### What it does - /// - /// Detects symbol comparison using `Symbol::intern`. - /// - /// ### Why is this bad? - /// - /// Comparison via `Symbol::as_str()` is faster if the interned symbols are not reused. - /// - /// ### Example - /// - /// None, see suggestion. - pub SLOW_SYMBOL_COMPARISONS, - internal, - "detects slow comparisons 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 ExprKind::Call(fun, args) = op2.kind - && let ExprKind::Path(ref qpath) = fun.kind - && cx - .tcx - .is_diagnostic_item(sym::SymbolIntern, cx.qpath_res(qpath, fun.hir_id).opt_def_id()?) - && let [symbol_name_expr] = args - && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr) - { - Some((op1.span, symbol_name)) - } 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/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs deleted file mode 100644 index 3790805df35..00000000000 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ /dev/null @@ -1,301 +0,0 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; -use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; -use rustc_middle::ty::{self, Ty}; -use rustc_session::impl_lint_pass; -use rustc_span::Span; -use rustc_span::symbol::Symbol; - -use std::str; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used. - /// - /// ### Why is this bad? - /// The path for an item is subject to change and is less efficient to look up than a - /// diagnostic item or a `LangItem`. - /// - /// ### Example - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) - /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" -} - -impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[derive(Default)] -pub struct UnnecessaryDefPath { - array_def_ids: FxIndexSet<(DefId, Span)>, - linted_def_ids: FxHashSet<DefId>, -} - -impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - match expr.kind { - ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), - ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), - _ => {}, - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for &(def_id, span) in &self.array_def_ids { - if self.linted_def_ids.contains(&def_id) { - continue; - } - - let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { - ("diagnostic item", format!("sym::{sym}")) - } else if let Some(sym) = get_lang_item_name(cx, def_id) { - ("language item", format!("LangItem::{sym}")) - } else { - continue; - }; - - span_lint_and_help( - cx, - UNNECESSARY_DEF_PATH, - span, - format!("hardcoded path to a {msg}"), - None, - format!("convert all references to use `{sugg}`"), - ); - } - } -} - -impl UnnecessaryDefPath { - #[allow(clippy::too_many_lines)] - fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { - enum Item { - LangItem(&'static str), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if let [cx_arg, def_arg, args @ ..] = args - && let ExprKind::Path(path) = &func.kind - && let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && let Some(which_path) = match_any_def_paths(cx, id, PATHS) - && let item_arg = if which_path == 4 { &args[1] } else { &args[0] } - // Extract the path to the matched type - && let Some(segments) = path_to_matched_type(cx, item_arg) - && let segments = segments.iter().map(|sym| &**sym).collect::<Vec<_>>() - && let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next() - { - // Check if the target item is a diagnostic item or LangItem. - #[rustfmt::skip] - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(item_name) = get_lang_item_name(cx, def_id) { - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; - - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - _ => false, - }; - - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => ( - format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), - has_ctor, - ), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), - has_ctor, - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => { - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) - }, - // match_type - (2, Item::DiagnosticItem(item)) => ( - format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (2, Item::LangItem(item)) => ( - format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), - false, - ), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), - false, - ), - (3, Item::DiagnosticItem(item)) => ( - format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { - diag.span_suggestion(span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead", - ); - } - }); - - self.linted_def_ids.insert(def_id); - } - } - - fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { - let Some(path) = path_from_array(elements) else { return }; - - for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { - self.array_def_ids.insert((def_id, span)); - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Vec<String>> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - if let Node::LetStmt(LetStmt { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id).instantiate_identity(), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => { - let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory(); - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => path_from_array(exprs), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().ptrs().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .ptrs() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok() - .map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> { - exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind - && let LitKind::Str(sym, _) = lit.node - { - return Some((*sym.as_str()).to_owned()); - } - - None - }) - .collect() -} - -fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { - if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { - Some(lang_item.variant_name()) - } else { - None - } -} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4476cd1005e..16066dd96c0 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -2,6 +2,3 @@ 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/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 5b3f60ad6ab..45a5dbabeb4 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -154,7 +154,7 @@ impl LateLintPass<'_> for WildcardImports { (span, false) }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); + let mut imports: Vec<_> = used_imports.iter().map(ToString::to_string).collect(); let imports_string = if imports.len() == 1 { imports.pop().unwrap() } else if braced_glob { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 11c14c14777..a8758b65c91 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::is_in_test; use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node}; use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::{is_in_test, sym}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -12,7 +12,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -359,7 +359,7 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { + let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else { return; }; @@ -401,7 +401,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma return; }; - if format_args.template.len() == 1 && last.as_str() == "\n" { + if format_args.template.len() == 1 && last == sym::LF { // print!("\n"), write!(f, "\n") diag.multipart_suggestion( @@ -427,9 +427,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma } fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..] - && literal.as_str() == "\n" - { + if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] { let mut span = format_args.span; let lint = if name == "writeln" { @@ -500,9 +498,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { None => return, }, LitKind::Char => ( - match lit.symbol.as_str() { - "\"" => "\\\"", - "\\'" => "'", + match lit.symbol { + sym::DOUBLE_QUOTE => "\\\"", + sym::BACKSLASH_SINGLE_QUOTE => "'", _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) { Some(stripped) => stripped, None => return, diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index a97643e0eac..24b1381ba45 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; @@ -54,8 +54,6 @@ impl LateLintPass<'_> for ZeroSizedMapValues { // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 && !ty.has_escaping_bound_vars() - // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. - && is_normalizable(cx, cx.param_env, ty) && let Ok(layout) = cx.layout_of(ty) && layout.is_zst() { diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 39c1aab8967..09f1084fe70 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -4,6 +4,7 @@ use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -68,6 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { let mut vis = WaitFinder { cx, local_id, + body_id: cx.tcx.hir_enclosing_body_owner(expr.hir_id), state: VisitorState::WalkUpToLocal, early_return: None, missing_wait_branch: None, @@ -129,6 +131,7 @@ struct MaybeWait(Span); struct WaitFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, local_id: HirId, + body_id: LocalDefId, state: VisitorState, early_return: Option<Span>, // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targeted help @@ -186,7 +189,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { } } else { match ex.kind { - ExprKind::Ret(e) => { + ExprKind::Ret(e) if self.cx.tcx.hir_enclosing_body_owner(ex.hir_id) == self.body_id => { visit_opt!(self, visit_expr, e); if self.early_return.is_none() { self.early_return = Some(ex.span); diff --git a/clippy_lints_internal/Cargo.toml b/clippy_lints_internal/Cargo.toml new file mode 100644 index 00000000000..a8293a1ad39 --- /dev/null +++ b/clippy_lints_internal/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "clippy_lints_internal" +version = "0.0.1" +edition = "2024" + +[dependencies] +clippy_config = { path = "../clippy_config" } +clippy_utils = { path = "../clippy_utils" } +regex = { version = "1.5" } +rustc-semver = "1.1" + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)] +rustc_private = true diff --git a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/clippy_lints_internal/src/almost_standard_lint_formulation.rs index 0a01a364a75..4fd5ea459a5 100644 --- a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/clippy_lints_internal/src/almost_standard_lint_formulation.rs @@ -1,11 +1,12 @@ -use crate::utils::internal_lints::lint_without_lint_pass::is_lint_ref_type; +use crate::lint_without_lint_pass::is_lint_ref_type; use clippy_utils::diagnostics::span_lint_and_help; use regex::Regex; use rustc_hir::{Attribute, Item, ItemKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::impl_lint_pass; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks if lint formulations have a standardized format. /// @@ -14,9 +15,10 @@ declare_clippy_lint! { /// /// ### Example /// `Checks for use...` can be written as `Checks for usage...` . - pub ALMOST_STANDARD_LINT_FORMULATION, - internal, - "lint formulations must have a standardized format." + pub clippy::ALMOST_STANDARD_LINT_FORMULATION, + Warn, + "lint formulations must have a standardized format.", + report_in_external_macro: true } impl_lint_pass!(AlmostStandardFormulation => [ALMOST_STANDARD_LINT_FORMULATION]); diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints_internal/src/collapsible_calls.rs index 2e6fb7c4ce4..407deb45db0 100644 --- a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/clippy_lints_internal/src/collapsible_calls.rs @@ -1,15 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt}; +use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; use rustc_span::Span; use std::borrow::{Borrow, Cow}; -declare_clippy_lint! { +use crate::internal_paths; + +declare_tool_lint! { /// ### What it does /// Lints `span_lint_and_then` function calls, where the /// closure argument has only one statement and that statement is a method @@ -64,9 +67,10 @@ declare_clippy_lint! { /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" + pub clippy::COLLAPSIBLE_SPAN_LINT_CALLS, + Warn, + "found collapsible `span_lint_and_then` calls", + report_in_external_macro: true } declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); @@ -78,7 +82,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"]) + && internal_paths::SPAN_LINT_AND_THEN.matches_path(cx, func) && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind && let body = cx.tcx.hir_body(body) && let only_expr = peel_blocks_with_stmt(body.value) diff --git a/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs b/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs new file mode 100644 index 00000000000..a1dacd359a0 --- /dev/null +++ b/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::paths; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::{AttrStyle, DelimArgs}; +use rustc_hir::def::Res; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{ + AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; +use rustc_middle::ty::TyCtxt; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_tool_lint! { + /// ### What it does + /// Checks for structs or enums that derive `serde::Deserialize` and that + /// do not have a `#[serde(deny_unknown_fields)]` attribute. + /// + /// ### Why is this bad? + /// If the struct or enum is used in [`clippy_config::conf::Conf`] and a + /// user inserts an unknown field by mistake, the user's error will be + /// silently ignored. + /// + /// ### Example + /// ```rust + /// #[derive(serde::Deserialize)] + /// pub struct DisallowedPath { + /// path: String, + /// reason: Option<String>, + /// replacement: Option<String>, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// #[derive(serde::Deserialize)] + /// #[serde(deny_unknown_fields)] + /// pub struct DisallowedPath { + /// path: String, + /// reason: Option<String>, + /// replacement: Option<String>, + /// } + /// ``` + pub clippy::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + Allow, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + report_in_external_macro: true +} + +declare_lint_pass!(DeriveDeserializeAllowingUnknown => [DERIVE_DESERIALIZE_ALLOWING_UNKNOWN]); + +impl<'tcx> LateLintPass<'tcx> for DeriveDeserializeAllowingUnknown { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + // Is this an `impl` (of a certain form)? + let ItemKind::Impl(Impl { + of_trait: + Some(TraitRef { + path: + Path { + res: Res::Def(_, trait_def_id), + .. + }, + .. + }), + self_ty: + Ty { + kind: + TyKind::Path(QPath::Resolved( + None, + Path { + res: Res::Def(_, self_ty_def_id), + .. + }, + )), + .. + }, + .. + }) = item.kind + else { + return; + }; + + // Is it an `impl` of the trait `serde::Deserialize`? + if !paths::SERDE_DESERIALIZE.get(cx).contains(trait_def_id) { + return; + } + + // Is it derived? + if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) { + return; + } + + // Is `self_ty` local? + let Some(local_def_id) = self_ty_def_id.as_local() else { + return; + }; + + // Does `self_ty` have a variant with named fields? + if !has_variant_with_named_fields(cx.tcx, local_def_id) { + return; + } + + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + + // Does `self_ty` have `#[serde(deny_unknown_fields)]`? + if let Some(tokens) = find_serde_attr_item(cx.tcx, hir_id) + && tokens.iter().any(is_deny_unknown_fields_token) + { + return; + } + + span_lint( + cx, + DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + item.span, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + ); + } +} + +// Determines whether `def_id` corresponds to an ADT with at least one variant with named fields. A +// variant has named fields if its `ctor` field is `None`. +fn has_variant_with_named_fields(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let ty = tcx.type_of(def_id).skip_binder(); + + let rustc_middle::ty::Adt(adt_def, _) = ty.kind() else { + return false; + }; + + adt_def.variants().iter().any(|variant_def| variant_def.ctor.is_none()) +} + +fn find_serde_attr_item(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&TokenStream> { + tcx.hir_attrs(hir_id).iter().find_map(|attribute| { + if let Attribute::Unparsed(attr_item) = attribute + && let AttrItem { + path: AttrPath { segments, .. }, + args: AttrArgs::Delimited(DelimArgs { tokens, .. }), + style: AttrStyle::Outer, + .. + } = &**attr_item + && segments.len() == 1 + && segments[0].as_str() == "serde" + { + Some(tokens) + } else { + None + } + }) +} + +fn is_deny_unknown_fields_token(tt: &TokenTree) -> bool { + if let TokenTree::Token(token, _) = tt + && token + .ident() + .is_some_and(|(token, _)| token.as_str() == "deny_unknown_fields") + { + true + } else { + false + } +} diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs new file mode 100644 index 00000000000..dc1e30ab2bd --- /dev/null +++ b/clippy_lints_internal/src/internal_paths.rs @@ -0,0 +1,17 @@ +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{sym, type_path, value_path}; + +// Paths inside rustc +pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); +pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw); +pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint); +pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol); +pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str); +pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym); +pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext); + +// Paths in clippy itself +pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); +pub static MSRV_STACK: PathLookup = type_path!(clippy_utils::msrvs::MsrvStack); +pub static PATH_LOOKUP_NEW: PathLookup = value_path!(clippy_utils::paths::PathLookup::new); +pub static SPAN_LINT_AND_THEN: PathLookup = value_path!(clippy_utils::diagnostics::span_lint_and_then); diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs new file mode 100644 index 00000000000..43cde86504f --- /dev/null +++ b/clippy_lints_internal/src/lib.rs @@ -0,0 +1,77 @@ +#![feature(rustc_private)] +#![allow( + clippy::missing_docs_in_private_items, + clippy::must_use_candidate, + clippy::symbol_as_str, + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic +)] +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications, + rustc::internal +)] +// Disable this rustc lint for now, as it was also done in rustc +#![allow(rustc::potential_query_instability)] +// None of these lints need a version. +#![allow(clippy::missing_clippy_version_attribute)] + +extern crate rustc_ast; +extern crate rustc_attr_parsing; +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_lint_defs; +extern crate rustc_middle; +extern crate rustc_session; +extern crate rustc_span; + +mod almost_standard_lint_formulation; +mod collapsible_calls; +mod derive_deserialize_allowing_unknown; +mod internal_paths; +mod lint_without_lint_pass; +mod msrv_attr_impl; +mod outer_expn_data_pass; +mod produce_ice; +mod symbols; +mod unnecessary_def_path; +mod unsorted_clippy_utils_paths; + +use rustc_lint::{Lint, LintStore}; + +static LINTS: &[&Lint] = &[ + almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, + collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, + derive_deserialize_allowing_unknown::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + lint_without_lint_pass::DEFAULT_LINT, + lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, + lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, + lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, + msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, + outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, + produce_ice::PRODUCE_ICE, + symbols::INTERNING_LITERALS, + symbols::SYMBOL_AS_STR, + unnecessary_def_path::UNNECESSARY_DEF_PATH, + unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, +]; + +pub fn register_lints(store: &mut LintStore) { + store.register_lints(LINTS); + + store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); + store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); + store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| Box::new(derive_deserialize_allowing_unknown::DeriveDeserializeAllowingUnknown)); + store.register_late_pass(|_| Box::<symbols::Symbols>::default()); + store.register_late_pass(|_| Box::<lint_without_lint_pass::LintWithoutLintPass>::default()); + store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); + store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); + store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); +} diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints_internal/src/lint_without_lint_pass.rs index bee05e52ed2..655d8fb8d1b 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -1,6 +1,7 @@ +use crate::internal_paths; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::is_lint_allowed; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{is_lint_allowed, match_def_path, paths}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; @@ -9,13 +10,14 @@ use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Ensures every lint is associated to a `LintPass`. /// @@ -37,12 +39,14 @@ declare_clippy_lint! { /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); /// // missing FORGOTTEN_LINT /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" + pub clippy::LINT_WITHOUT_LINT_PASS, + Warn, + "declaring a lint without associating it in a LintPass", + report_in_external_macro: true + } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for cases of an auto-generated lint without an updated description, /// i.e. `default lint description`. @@ -59,30 +63,32 @@ declare_clippy_lint! { /// ```rust,ignore /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" + pub clippy::DEFAULT_LINT, + Warn, + "found 'default lint description' in a lint declaration", + report_in_external_macro: true } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for invalid `clippy::version` attributes. /// /// Valid values are: /// * "pre 1.29.0" /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" + pub clippy::INVALID_CLIPPY_VERSION_ATTRIBUTE, + Warn, + "found an invalid `clippy::version` attribute", + report_in_external_macro: true } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" + pub clippy::MISSING_CLIPPY_VERSION_ATTRIBUTE, + Warn, + "found clippy lint without `clippy::version` attribute", + report_in_external_macro: true } #[derive(Clone, Debug, Default)] @@ -100,10 +106,6 @@ impl_lint_pass!(LintWithoutLintPass => [ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) { - return; - } - if let hir::ItemKind::Static(ident, ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { check_invalid_clippy_version_attribute(cx, item); @@ -208,10 +210,10 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { && let TyKind::Path(ref path) = inner.kind && let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); + internal_paths::LINT.matches(cx, def_id) + } else { + false } - - false } fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints_internal/src/msrv_attr_impl.rs index 558acacb972..d48d8dc57b2 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints_internal/src/msrv_attr_impl.rs @@ -1,20 +1,20 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::match_type; -use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint_defs::declare_tool_lint; use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; use rustc_session::declare_lint_pass; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" + pub clippy::MISSING_MSRV_ATTR_IMPL, + Warn, + "checking if all necessary steps were taken when adding a MSRV to a lint", + report_in_external_macro: true } declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); @@ -30,7 +30,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .tcx .impl_trait_ref(item.owner_id) .map(EarlyBinder::instantiate_identity) - && match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS) + && internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id) && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind() && self_ty_def.is_struct() && self_ty_def.all_fields().any(|f| { @@ -39,7 +39,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .instantiate_identity() .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV_STACK)) + .any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty())) }) && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes") { diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints_internal/src/outer_expn_data_pass.rs index 326e1721461..40951443a48 100644 --- a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs +++ b/clippy_lints_internal/src/outer_expn_data_pass.rs @@ -1,13 +1,14 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::match_type; -use clippy_utils::{is_lint_allowed, method_calls, paths}; +use clippy_utils::{is_lint_allowed, method_calls}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for calls to `cx.outer().expn_data()` and suggests to use /// the `cx.outer_expn_data()` @@ -24,9 +25,10 @@ declare_clippy_lint! { /// ```rust,ignore /// expr.span.ctxt().outer_expn_data() /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" + pub clippy::OUTER_EXPN_EXPN_DATA, + Warn, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`", + report_in_external_macro: true } declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); @@ -43,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { && let (self_arg, args) = arg_lists[1] && args.is_empty() && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && match_type(cx, self_ty, &paths::SYNTAX_CONTEXT) + && internal_paths::SYNTAX_CONTEXT.matches_ty(cx, self_ty) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints_internal/src/produce_ice.rs index 2bcb7b8ef14..14e93dc6d5f 100644 --- a/clippy_lints/src/utils/internal_lints/produce_ice.rs +++ b/clippy_lints_internal/src/produce_ice.rs @@ -1,10 +1,11 @@ use rustc_ast::ast::NodeId; use rustc_ast::visit::FnKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; use rustc_span::Span; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Not an actual lint. This lint is only meant for testing our customized internal compiler /// error message by calling `panic`. @@ -16,9 +17,10 @@ declare_clippy_lint! { /// ```rust,ignore /// 🍦🍦🍦🍦🍦 /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" + pub clippy::PRODUCE_ICE, + Warn, + "this message should not appear anywhere as we ICE before and don't emit the lint", + report_in_external_macro: true } declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); diff --git a/clippy_lints_internal/src/symbols.rs b/clippy_lints_internal/src/symbols.rs new file mode 100644 index 00000000000..5aee545fb0f --- /dev/null +++ b/clippy_lints_internal/src/symbols.rs @@ -0,0 +1,207 @@ +use crate::internal_paths; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::LitKind; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind, Lit, Node, Pat, PatExprKind, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; +use rustc_middle::mir::ConstValue; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, sym}; + +declare_tool_lint! { + /// ### What it does + /// Checks for interning string literals as symbols + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs` + /// + /// ### Example + /// ```rust,ignore + /// let _ = Symbol::intern("f32"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub clippy::INTERNING_LITERALS, + Warn, + "interning a symbol that is a literal", + report_in_external_macro: true +} + +declare_tool_lint! { + /// ### What it does + /// Checks for calls to `Symbol::as_str` + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs` + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "foo" + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::foo + /// ``` + pub clippy::SYMBOL_AS_STR, + Warn, + "calls to `Symbol::as_str`", + report_in_external_macro: true +} + +#[derive(Default)] +pub struct Symbols { + // Maps the symbol to the import path + symbol_map: FxHashMap<u32, (&'static str, Symbol)>, +} + +impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); + +impl Symbols { + fn lit_suggestion(&self, lit: &Lit) -> Option<(Span, String)> { + if let LitKind::Str(name, _) = lit.node { + let sugg = if let Some((prefix, name)) = self.symbol_map.get(&name.as_u32()) { + format!("{prefix}::{name}") + } else { + format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")) + }; + Some((lit.span, sugg)) + } else { + None + } + } + + fn expr_suggestion(&self, expr: &Expr<'_>) -> Option<(Span, String)> { + if let ExprKind::Lit(lit) = expr.kind { + self.lit_suggestion(lit) + } else { + None + } + } + + fn pat_suggestions(&self, pat: &Pat<'_>, suggestions: &mut Vec<(Span, String)>) { + pat.walk_always(|pat| { + if let PatKind::Expr(pat_expr) = pat.kind + && let PatExprKind::Lit { lit, .. } = pat_expr.kind + { + suggestions.extend(self.lit_suggestion(lit)); + } + }); + } +} + +impl<'tcx> LateLintPass<'tcx> for Symbols { + fn check_crate(&mut self, cx: &LateContext<'_>) { + let modules = [ + ("kw", &internal_paths::KW_MODULE), + ("sym", &internal_paths::SYM_MODULE), + ("sym", &internal_paths::CLIPPY_SYM_MODULE), + ]; + for (prefix, module) in modules { + for def_id in module.get(cx) { + // When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will + // still lint but the suggestion may suggest the incorrect name for symbols such as `sym::CRLF` + if def_id.is_local() { + continue; + } + + for item in cx.tcx.module_children(def_id) { + if let Res::Def(DefKind::Const, item_def_id) = item.res + && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() + && internal_paths::SYMBOL.matches_ty(cx, ty) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) + && let Some(value) = value.to_u32().discard_err() + { + self.symbol_map.insert(value, (prefix, item.ident.name)); + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Call(func, [arg]) = &expr.kind + && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() + && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) + && let Some((_, sugg)) = self.expr_suggestion(arg) + { + span_lint_and_then( + cx, + INTERNING_LITERALS, + expr.span, + "interning a string literal", + |diag| { + diag.span_suggestion_verbose( + expr.span, + "use a preinterned symbol instead", + sugg, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbol to `clippy_utils/src/sym.rs` if needed"); + }, + ); + } + + if let Some(as_str) = as_str_span(cx, expr) + && let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) + { + let mut suggestions = Vec::new(); + + match parent.kind { + ExprKind::Binary(_, lhs, rhs) => { + suggestions.extend(self.expr_suggestion(lhs)); + suggestions.extend(self.expr_suggestion(rhs)); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.pat_suggestions(arm.pat, &mut suggestions); + } + }, + _ => {}, + } + + if suggestions.is_empty() { + return; + } + + span_lint_and_then( + cx, + SYMBOL_AS_STR, + expr.span, + "converting a Symbol to a string", + |diag| { + suggestions.push((as_str, String::new())); + diag.multipart_suggestion( + "use preinterned symbols instead", + suggestions, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbols to `clippy_utils/src/sym.rs` if needed"); + }, + ); + } + } +} + +/// ```ignore +/// symbol.as_str() +/// // ^^^^^^^^ +/// ``` +fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> { + if let ExprKind::MethodCall(_, recv, [], _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && internal_paths::SYMBOL_AS_STR.matches(cx, method_def_id) + { + Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi())) + } else { + None + } +} diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs new file mode 100644 index 00000000000..8877f1faf0e --- /dev/null +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -0,0 +1,104 @@ +use crate::internal_paths; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::{PathNS, lookup_path}; +use clippy_utils::{path_def_id, peel_ref_operators}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::{declare_lint_pass, declare_tool_lint}; +use rustc_middle::mir::ConstValue; +use rustc_span::symbol::Symbol; + +declare_tool_lint! { + /// ### What it does + /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used. + /// + /// ### Why is this bad? + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. + /// + /// ### Example + /// ```rust,ignore + /// pub static VEC: PathLookup = path!(alloc::vec::Vec); + /// + /// VEC.contains_ty(cx, ty) + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// is_type_diagnostic_item(cx, ty, sym::Vec) + /// ``` + pub clippy::UNNECESSARY_DEF_PATH, + Warn, + "using a def path when a diagnostic item or a `LangItem` is available", + report_in_external_macro: true +} + +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Call(ctor, [_, path]) = expr.kind + && internal_paths::PATH_LOOKUP_NEW.matches_path(cx, ctor) + && let ExprKind::Array(segments) = peel_ref_operators(cx, path).kind + && let Some(macro_id) = expr.span.ctxt().outer_expn_data().macro_def_id + { + let ns = match cx.tcx.item_name(macro_id).as_str() { + "type_path" => PathNS::Type, + "value_path" => PathNS::Value, + "macro_path" => PathNS::Macro, + _ => unreachable!(), + }; + + let path: Vec<Symbol> = segments + .iter() + .map(|segment| { + if let Some(const_def_id) = path_def_id(cx, segment) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) + && let Some(value) = value.to_u32().discard_err() + { + Symbol::new(value) + } else { + panic!("failed to resolve path {:?}", expr.span); + } + }) + .collect(); + + for def_id in lookup_path(cx.tcx, ns, &path) { + if let Some(name) = cx.tcx.get_diagnostic_name(def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a diagnostic name exists for this path: sym::{name}"), + |diag| { + diag.help( + "remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead", + ); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils"); + }, + ); + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a language item exists for this path: LangItem::{item_name}"), + |diag| { + diag.help("remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead"); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils"); + }, + ); + } + } + } + } +} + +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { + Some(lang_item.variant_name()) + } else { + None + } +} diff --git a/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs b/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs index bf60df5119d..8e281ecb2ee 100644 --- a/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs +++ b/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Crate, ItemKind, ModKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks that [`clippy_utils::paths`] is sorted lexically /// @@ -12,9 +13,10 @@ declare_clippy_lint! { /// /// ### Example /// Wrong ordering of the util::paths constants. - pub UNSORTED_CLIPPY_UTILS_PATHS, - internal, - "various things that will negatively affect your clippy experience" + pub clippy::UNSORTED_CLIPPY_UTILS_PATHS, + Warn, + "various things that will negatively affect your clippy experience", + report_in_external_macro: true } declare_lint_pass!(UnsortedClippyUtilsPaths => [UNSORTED_CLIPPY_UTILS_PATHS]); diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index b98e9901750..ac970e1c4b0 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index bbf9d793556..d4080d06d3c 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: <!-- begin autogenerated nightly --> ``` -nightly-2025-04-03 +nightly-2025-05-14 ``` <!-- end autogenerated nightly --> diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index f017397f219..8996b694ed8 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -33,6 +33,7 @@ pub fn eq_id(l: Ident, r: Ident) -> bool { pub fn eq_pat(l: &Pat, r: &Pat) -> bool { use PatKind::*; match (&l.kind, &r.kind) { + (Missing, _) | (_, Missing) => unreachable!(), (Paren(l), _) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 212aecbf552..b9928b8eed4 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -15,7 +15,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, + BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, }; use rustc_lexer::tokenize; use rustc_lint::LateContext; @@ -508,7 +508,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, left, right) => self.binop(op, left, right), + ExprKind::Binary(op, left, right) => self.binop(op.node, left, right), ExprKind::Call(callee, []) => { // We only handle a few const functions for now. if let ExprKind::Path(qpath) = &callee.kind @@ -746,7 +746,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> { + fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { @@ -759,7 +759,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and // the right-hand argument is -1 always panics, even with overflow-checks disabled - if let BinOpKind::Div | BinOpKind::Rem = op.node + if let BinOpKind::Div | BinOpKind::Rem = op && l == ty_min_value && r == -1 { @@ -767,7 +767,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity)); - match op.node { + match op { // When +, * or binary - create a value greater than the maximum value, or less than // the minimum value that can be stored, it panics. BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(zext), @@ -794,7 +794,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ty::Uint(ity) => { let bits = ity.bits(); - match op.node { + match op { BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), BinOpKind::Sub => l.checked_sub(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), BinOpKind::Mul => l.checked_mul(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), @@ -817,7 +817,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => None, }, // FIXME(f16_f128): add these types when binary operations are available on all platforms - (Constant::F32(l), Some(Constant::F32(r))) => match op.node { + (Constant::F32(l), Some(Constant::F32(r))) => match op { BinOpKind::Add => Some(Constant::F32(l + r)), BinOpKind::Sub => Some(Constant::F32(l - r)), BinOpKind::Mul => Some(Constant::F32(l * r)), @@ -831,7 +831,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::Gt => Some(Constant::Bool(l > r)), _ => None, }, - (Constant::F64(l), Some(Constant::F64(r))) => match op.node { + (Constant::F64(l), Some(Constant::F64(r))) => match op { BinOpKind::Add => Some(Constant::F64(l + r)), BinOpKind::Sub => Some(Constant::F64(l - r)), BinOpKind::Mul => Some(Constant::F64(l * r)), @@ -845,7 +845,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::Gt => Some(Constant::Bool(l > r)), _ => None, }, - (l, r) => match (op.node, l, r) { + (l, r) => match (op, l, r) { (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)), (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)), (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => { diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index d4e66ebd8e1..dbb99348290 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,14 +3,14 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::is_expn_of; use crate::ty::is_type_diagnostic_item; +use crate::{is_expn_of, sym}; use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StructTailExpr}; use rustc_lint::LateContext; -use rustc_span::{Span, sym, symbol}; +use rustc_span::{Span, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. @@ -474,7 +474,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::New); } else if name.ident.name == symbol::kw::Default { return Some(VecInitKind::Default); - } else if name.ident.name.as_str() == "with_capacity" { + } else if name.ident.name == sym::with_capacity { let arg = args.first()?; return match ConstEvalCtxt::new(cx).eval_simple(arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 4b16896c298..c37231d0931 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -8,7 +8,7 @@ use rustc_hir::MatchSource::TryDesugar; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, - ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, + ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, }; @@ -483,7 +483,7 @@ impl HirEqInterExpr<'_, '_, '_> { } fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool { - left.res == right.res + left.kind == right.kind } fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool { @@ -1117,6 +1117,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_const_arg(s); self.hash_const_arg(e); }, + TyPatKind::Or(variants) => { + for variant in variants { + self.hash_ty_pat(variant); + } + }, TyPatKind::Err(_) => {}, } } @@ -1124,6 +1129,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match &pat.kind { + PatKind::Missing => unreachable!(), PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => { std::mem::discriminant(by_ref).hash(&mut self.s); std::mem::discriminant(mutability).hash(&mut self.s); @@ -1244,8 +1250,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { lifetime.ident.name.hash(&mut self.s); - std::mem::discriminant(&lifetime.res).hash(&mut self.s); - if let LifetimeName::Param(param_id) = lifetime.res { + std::mem::discriminant(&lifetime.kind).hash(&mut self.s); + if let LifetimeKind::Param(param_id) = lifetime.kind { param_id.hash(&mut self.s); } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index bba4c3179e4..accfb720eef 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,8 +1,6 @@ -#![feature(array_chunks)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(macro_metavar_expr_concat)] -#![feature(let_chains)] +#![feature(macro_metavar_expr)] #![feature(never_type)] #![feature(rustc_private)] #![feature(assert_matches)] @@ -27,6 +25,7 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate indexmap; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_attr_parsing; @@ -50,9 +49,6 @@ extern crate rustc_span; extern crate rustc_trait_selection; extern crate smallvec; -#[macro_use] -pub mod sym_helper; - pub mod ast_utils; pub mod attrs; mod check_proc_macro; @@ -72,6 +68,7 @@ pub mod qualify_min_const_fn; pub mod source; pub mod str_utils; pub mod sugg; +pub mod sym; pub mod ty; pub mod usage; pub mod visitors; @@ -85,7 +82,6 @@ pub use self::hir_utils::{ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; -use std::hash::BuildHasherDefault; use std::iter::{once, repeat_n}; use std::sync::{Mutex, MutexGuard, OnceLock}; @@ -95,35 +91,36 @@ use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_attr_parsing::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; -use rustc_data_structures::unhash::UnhashMap; +use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ - self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, - GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, - Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, - Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, + self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, + CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, + Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + TraitItemKind, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; +use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, - TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, + TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; -use rustc_span::{InnerSpan, Span, sym}; +use rustc_span::{InnerSpan, Span}; use source::walk_span_to_context; use visitors::{Visitable, for_each_unconsumed_temporary}; @@ -131,7 +128,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; -use rustc_middle::hir::nested_filter; #[macro_export] macro_rules! extract_msrv_attr { @@ -239,7 +235,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool { /// * const blocks (or inline consts) /// * associated constants pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { - use ConstContext::{Const, ConstFn, Static}; + use rustc_hir::ConstContext::{Const, ConstFn, Static}; let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else { return false; }; @@ -347,14 +343,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the method call given in `expr` belongs to the given trait. -/// This is a deprecated function, consider using [`is_trait_method`]. -pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { - let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - let trt_id = cx.tcx.trait_of_item(def_id); - trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path)) -} - /// Checks if the given method call expression calls an inherent method. pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { @@ -438,44 +426,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc }) } -/// THIS METHOD IS DEPRECATED. Matches a `QPath` against a slice of segment string literals. -/// -/// This method is deprecated and will eventually be removed since it does not match against the -/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from -/// `QPath::Resolved.1.res.opt_def_id()`. -/// -/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a -/// `rustc_hir::QPath`. -/// -/// # Examples -/// ```rust,ignore -/// match_qpath(path, &["std", "rt", "begin_unwind"]) -/// ``` -pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { - match *path { - QPath::Resolved(_, path) => match_path(path, segments), - QPath::TypeRelative(ty, segment) => match ty.kind { - TyKind::Path(ref inner_path) => { - if let [prefix @ .., end] = segments - && match_qpath(inner_path, prefix) - { - return segment.ident.name.as_str() == *end; - } - false - }, - _ => false, - }, - QPath::LangItem(..) => false, - } -} - -/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. -/// -/// Please use `is_path_diagnostic_item` if the target is a diagnostic item. -pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { - path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments)) -} - /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given lang item. pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { @@ -492,34 +442,6 @@ pub fn is_path_diagnostic_item<'tcx>( path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) } -/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals. -/// -/// This method is deprecated and will eventually be removed since it does not match against the -/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from -/// `QPath::Resolved.1.res.opt_def_id()`. -/// -/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a -/// `rustc_hir::Path`. -/// -/// # Examples -/// -/// ```rust,ignore -/// if match_path(&trait_ref.path, &paths::HASH) { -/// // This is the `std::hash::Hash` trait. -/// } -/// -/// if match_path(ty_path, &["rustc", "lint", "Lint"]) { -/// // This is a `rustc_middle::lint::Lint`. -/// } -/// ``` -pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { - path.segments - .iter() - .rev() - .zip(segments.iter().rev()) - .all(|(a, b)| a.ident.name.as_str() == *b) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> { if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind @@ -586,201 +508,6 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx { - let ty = match name { - "bool" => SimplifiedType::Bool, - "char" => SimplifiedType::Char, - "str" => SimplifiedType::Str, - "array" => SimplifiedType::Array, - "slice" => SimplifiedType::Slice, - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - "const_ptr" => SimplifiedType::Ptr(Mutability::Not), - "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), - "isize" => SimplifiedType::Int(IntTy::Isize), - "i8" => SimplifiedType::Int(IntTy::I8), - "i16" => SimplifiedType::Int(IntTy::I16), - "i32" => SimplifiedType::Int(IntTy::I32), - "i64" => SimplifiedType::Int(IntTy::I64), - "i128" => SimplifiedType::Int(IntTy::I128), - "usize" => SimplifiedType::Uint(UintTy::Usize), - "u8" => SimplifiedType::Uint(UintTy::U8), - "u16" => SimplifiedType::Uint(UintTy::U16), - "u32" => SimplifiedType::Uint(UintTy::U32), - "u64" => SimplifiedType::Uint(UintTy::U64), - "u128" => SimplifiedType::Uint(UintTy::U128), - "f32" => SimplifiedType::Float(FloatTy::F32), - "f64" => SimplifiedType::Float(FloatTy::F64), - _ => { - return [].iter().copied(); - }, - }; - - tcx.incoherent_impls(ty).iter().copied() -} - -fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .filter(|item| item.ident.name == name) - .map(|child| child.res.expect_non_local()) - .collect(), - DefKind::Impl { .. } => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) - .collect(), - _ => Vec::new(), - } -} - -fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> { - let root_mod; - let item_kind = match tcx.hir_node_by_def_id(local_id) { - Node::Crate(r#mod) => { - root_mod = ItemKind::Mod(Ident::dummy(), r#mod); - &root_mod - }, - Node::Item(item) => &item.kind, - _ => return Vec::new(), - }; - - let res = |ident: Ident, owner_id: OwnerId| { - if ident.name == name { - let def_id = owner_id.to_def_id(); - Some(Res::Def(tcx.def_kind(def_id), def_id)) - } else { - None - } - }; - - match item_kind { - ItemKind::Mod(_, r#mod) => r#mod - .item_ids - .iter() - .filter_map(|&item_id| { - let ident = tcx.hir_item(item_id).kind.ident()?; - res(ident, item_id.owner_id) - }) - .collect(), - ItemKind::Impl(r#impl) => r#impl - .items - .iter() - .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - ItemKind::Trait(.., trait_item_refs) => trait_item_refs - .iter() - .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - _ => Vec::new(), - } -} - -fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> { - if let Some(local_id) = def_id.as_local() { - local_item_children_by_name(tcx, local_id, name) - } else { - non_local_item_children_by_name(tcx, def_id, name) - } -} - -/// Finds the crates called `name`, may be multiple due to multiple major versions. -pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .map(CrateNum::as_def_id) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .collect() -} - -/// Resolves a def path like `std::vec::Vec`. -/// -/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. -/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. -/// -/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` -/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. -/// -/// 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 { - [primitive] => { - return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; - }, - [base, path @ ..] => (base, path), - _ => return Vec::new(), - }; - - let base_sym = Symbol::intern(base); - - let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym { - Some(LOCAL_CRATE.as_def_id()) - } else { - None - }; - - let crates = find_primitive_impls(tcx, base) - .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .chain(find_crates(tcx, base_sym)) - .collect(); - - def_path_res_with_base(tcx, crates, path) -} - -/// Resolves a def path like `vec::Vec` with the base `std`. -/// -/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up -/// items from the same crate repeatedly, although should still be used sparingly. -pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> { - while let [segment, rest @ ..] = path { - path = rest; - let segment = Symbol::intern(segment); - - base = base - .into_iter() - .filter_map(|res| res.opt_def_id()) - .flat_map(|def_id| { - // When the current def_id is e.g. `struct S`, check the impl items in - // `impl S { ... }` - let inherent_impl_children = tcx - .inherent_impls(def_id) - .iter() - .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); - - let direct_children = item_children_by_name(tcx, def_id, segment); - - inherent_impl_children.chain(direct_children) - }) - .collect(); - } - - base -} - -/// 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> + use<> { - def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id()) -} - -/// Convenience function to get the `DefId` of a trait by path. -/// It could be a trait or trait alias. -/// -/// This function is expensive and should be used sparingly. -pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> { - def_path_res(tcx, path).into_iter().find_map(|res| match res { - Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), - _ => None, - }) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: @@ -1412,7 +1139,7 @@ pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } /// Gets the name of the item the expression is in, if available. -pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> { +pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> { let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id; match cx.tcx.hir_node_by_def_id(parent_id) { Node::Item(item) => item.kind.ident().map(|ident| ident.name), @@ -1853,6 +1580,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { } match pat.kind { + PatKind::Missing => unreachable!(), PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)), PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), @@ -1972,14 +1700,14 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I let mut suppress_lint = false; for id in ids { - let (level, _) = cx.tcx.lint_level_at_node(lint, id); - if let Some(expectation) = level.get_expectation_id() { + let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id); + if let Some(expectation) = lint_id { cx.fulfill_expectation(expectation); } match level { - Level::Allow | Level::Expect(_) => suppress_lint = true, - Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {}, + Level::Allow | Level::Expect => suppress_lint = true, + Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {}, } } @@ -1994,7 +1722,7 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that /// expectations at the checked nodes will be fulfilled. pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool { - cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow + cx.tcx.lint_level_at_node(lint, id).level == Level::Allow } pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { @@ -2064,30 +1792,12 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { }) } -/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if -/// any. -/// -/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items. -pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> { - let search_path = cx.get_def_path(did); - paths - .iter() - .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied())) -} - -/// Checks if the given `DefId` matches the path. -pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool { - // We should probably move to Symbols in Clippy as well rather than interning every time. - let path = cx.get_def_path(did); - syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied()) -} - /// Checks if the given `DefId` matches the `libc` item. pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { let path = cx.get_def_path(did); // libc is meant to be used as a flat list of names, but they're all actually defined in different // modules based on the target platform. Ignore everything but crate name and the item name. - path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name) + path.first().is_some_and(|s| *s == sym::libc) && path.last().is_some_and(|s| s.as_str() == name) } /// Returns the list of condition expressions and the list of blocks in a @@ -2363,14 +2073,14 @@ pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { cx.tcx .hir_attrs(hir::CRATE_HIR_ID) .iter() - .any(|attr| attr.name_or_empty() == sym::no_std) + .any(|attr| attr.has_name(sym::no_std)) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { cx.tcx .hir_attrs(hir::CRATE_HIR_ID) .iter() - .any(|attr| attr.name_or_empty() == sym::no_core) + .any(|attr| attr.has_name(sym::no_core)) } /// Check if parent of a hir node is a trait implementation block. @@ -2486,45 +2196,46 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S None } -/// Returns list of all pairs `(a, b)` where `eq(a, b) == true` -/// and `a` is before `b` in `exprs` for all `a` and `b` in -/// `exprs` +/// Returns a list of groups where elements in each group are equal according to `eq` +/// +/// - Within each group the elements are sorted by the order they appear in `exprs` +/// - The groups themselves are sorted by their first element's appearence in `exprs` /// /// Given functions `eq` and `hash` such that `eq(a, b) == true` /// implies `hash(a) == hash(b)` -pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)> +pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>> where Hash: FnMut(&T) -> u64, Eq: FnMut(&T, &T) -> bool, { match exprs { - [a, b] if eq(a, b) => return vec![(a, b)], + [a, b] if eq(a, b) => return vec![vec![a, b]], _ if exprs.len() <= 2 => return vec![], _ => {}, } - let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - - let mut map: UnhashMap<u64, Vec<&_>> = - UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default(); for expr in exprs { - match map.entry(hash(expr)) { - Entry::Occupied(mut o) => { - for o in o.get() { - if eq(o, expr) { - match_expr_list.push((o, expr)); - } + match buckets.entry(hash(expr)) { + indexmap::map::Entry::Occupied(mut o) => { + let bucket = o.get_mut(); + match bucket.iter_mut().find(|group| eq(expr, group[0])) { + Some(group) => group.push(expr), + None => bucket.push(vec![expr]), } - o.get_mut().push(expr); }, - Entry::Vacant(v) => { - v.insert(vec![expr]); + indexmap::map::Entry::Vacant(v) => { + v.insert(vec![vec![expr]]); }, } } - match_expr_list + buckets + .into_values() + .flatten() + .filter(|group| group.len() > 1) + .collect() } /// Peels off all references on the pattern. Returns the underlying pattern and the number of @@ -2641,7 +2352,9 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new(); -fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { +/// Apply `f()` to the set of test item names. +/// The names are sorted using the default `Symbol` ordering. +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap(); let value = map.entry(module); @@ -2695,6 +2408,25 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { }) } +/// Checks if `fn_def_id` has a `#[test]` attribute applied +/// +/// This only checks directly applied attributes. To see if a node has a parent function marked with +/// `#[test]` use [`is_in_test_function`]. +/// +/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function +pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool { + let id = tcx.local_def_id_to_hir_id(fn_def_id); + if let Node::Item(item) = tcx.hir_node(id) + && let ItemKind::Fn { ident, .. } = item.kind + { + with_test_item_names(tcx, tcx.parent_module(id), |names| { + names.binary_search(&ident.name).is_ok() + }) + } else { + false + } +} + /// Checks if `id` has a `#[cfg(test)]` attribute applied /// /// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent @@ -3079,7 +2811,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { sm.span_take_while(span, |&ch| ch == ' ' || ch == ';') } -/// Returns whether the given let pattern and else body can be turned into a question mark +/// Returns whether the given let pattern and else body can be turned into the `?` operator /// /// For this example: /// ```ignore @@ -3102,8 +2834,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// ``` /// /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because -/// the question mark operator is applicable here. Callers have to check whether we are in a -/// constant or not. +/// the `?` operator is applicable here. Callers have to check whether we are in a constant or not. pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( cx: &LateContext<'_>, pat: &'a Pat<'hir>, @@ -3502,7 +3233,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St // a::b::c ::d::sym refers to // e::f::sym:: :: // result should be super::super::super::super::e::f - if let DefPathData::TypeNs(Some(s)) = l { + if let DefPathData::TypeNs(s) = l { path.push(s.to_string()); } if let DefPathData::TypeNs(_) = r { @@ -3513,7 +3244,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St // a::b::sym:: :: refers to // c::d::e ::f::sym // when looking at `f` - Left(DefPathData::TypeNs(Some(sym))) => path.push(sym.to_string()), + Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()), // consider: // a::b::c ::d::sym refers to // e::f::sym:: :: @@ -3527,7 +3258,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St // `super` chain would be too long, just use the absolute path instead once(String::from("crate")) .chain(to.data.iter().filter_map(|el| { - if let DefPathData::TypeNs(Some(sym)) = el.data { + if let DefPathData::TypeNs(sym) = el.data { Some(sym.to_string()) } else { None diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index e199a383ca6..223a8649eb3 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -1,10 +1,10 @@ +use crate::sym; use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; - use rustc_attr_parsing::{RustcVersion, parse_version}; use rustc_lint::LateContext; use rustc_session::Session; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use serde::Deserialize; use smallvec::SmallVec; use std::iter::once; @@ -22,7 +22,8 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } + 1,88,0 { LET_CHAINS } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT } 1,85,0 { UINT_FLOAT_MIDPOINT } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } @@ -38,7 +39,7 @@ msrv_aliases! { 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } - 1,63,0 { CLONE_INTO } + 1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN } 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } @@ -64,10 +65,11 @@ msrv_aliases! { 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } 1,34,0 { TRY_FROM } 1,33,0 { UNDERSCORE_IMPORTS } + 1,32,0 { CONST_IS_POWER_OF_TWO } 1,31,0 { OPTION_REPLACE } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } - 1,28,0 { FROM_BOOL, REPEAT_WITH } + 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } 1,24,0 { IS_ASCII_DIGIT } @@ -183,8 +185,7 @@ impl MsrvStack { } fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option<RustcVersion> { - let sym_msrv = Symbol::intern("msrv"); - let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); + let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym::msrv])); if let Some(msrv_attr) = msrv_attrs.next() { if let Some(duplicate) = msrv_attrs.next_back() { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a960a65ead2..e5179e479cc 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,61 +4,343 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information. -// Paths inside rustc -pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; -pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ - ["rustc_lint_defs", "Applicability", "Unspecified"], - ["rustc_lint_defs", "Applicability", "HasPlaceholders"], - ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], - ["rustc_lint_defs", "Applicability", "MachineApplicable"], -]; -pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"]; -pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; -pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; -pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; -pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; -pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; -pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; -pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; -pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; -pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; -pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; +use crate::{MaybePath, path_def_id, sym}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; +use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef, UseKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; +use std::sync::OnceLock; -// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. -pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"]; -pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"]; -pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"]; -pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"]; +/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an +/// arbitrary namespace +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum PathNS { + Type, + Value, + Macro, + + /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return + /// either the macro or the module but **not** both + /// + /// Must only be used when the specific resolution is unimportant such as in + /// `missing_enforced_import_renames` + Arbitrary, +} + +impl PathNS { + fn matches(self, ns: Option<Namespace>) -> bool { + let required = match self { + PathNS::Type => TypeNS, + PathNS::Value => ValueNS, + PathNS::Macro => MacroNS, + PathNS::Arbitrary => return true, + }; + + ns == Some(required) + } +} + +/// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`]. +/// +/// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub struct PathLookup { + ns: PathNS, + path: &'static [Symbol], + once: OnceLock<Vec<DefId>>, +} + +impl PathLookup { + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self { + Self { + ns, + path, + once: OnceLock::new(), + } + } + + /// Returns the list of [`DefId`]s that the path resolves to + pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + } -// Paths in clippy itself -pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"]; + /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into + /// stdlib crates to avoid the issue of multiple [`DefId`]s being returned + /// + /// May return [`None`] in `no_std`/`no_core` environments + pub fn only(&self, cx: &LateContext<'_>) -> Option<DefId> { + let ids = self.get(cx); + debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0])); + debug_assert!(ids.len() <= 1, "{ids:?}"); + ids.first().copied() + } + + /// Checks if the path resolves to the given `def_id` + pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { + self.get(cx).contains(&def_id) + } + + /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool { + path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) + } + + /// Checks if the path resolves to `ty`'s definition, must be an `Adt` + pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + } +} + +macro_rules! path_macros { + ($($name:ident: $ns:expr,)*) => { + $( + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + #[macro_export] + macro_rules! $name { + ($$($$seg:ident $$(::)?)*) => { + PathLookup::new($ns, &[$$(sym::$$seg,)*]) + }; + } + )* + }; +} + +path_macros! { + type_path: PathNS::Type, + value_path: PathNS::Value, + macro_path: PathNS::Macro, +} + +// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. +pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of); +pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit); +pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new); +pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other); +pub static ITER_STEP: PathLookup = type_path!(core::iter::Step); +pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref); // Paths in external crates -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; -pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; -pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; -pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"]; -pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"]; -pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"]; -pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"]; -pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"]; -pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"]; -pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"]; -pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; -pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; -pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"]; +pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); +pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); +pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple); +pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [ + type_path!(lock_api::mutex::MutexGuard), + type_path!(lock_api::rwlock::RwLockReadGuard), + type_path!(lock_api::rwlock::RwLockWriteGuard), +]; +pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new); +pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new); +pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new); +pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new); +pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new); +pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new); +pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize); +pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor); +pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options); +pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt); +pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt); +pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions); +pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new); +pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static); +pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy); +pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new); + +// Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs` + +/// Equivalent to a [`lookup_path`] after splitting the input string on `::` +/// +/// This function is expensive and should be used sparingly. +pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec<DefId> { + let path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect(); + lookup_path(tcx, ns, &path) +} + +/// Resolves a def path like `std::vec::Vec`. +/// +/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// This function is expensive and should be used sparingly. +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec<DefId> { + let (root, rest) = match *path { + [] | [_] => return Vec::new(), + [root, ref rest @ ..] => (root, rest), + }; + + let mut out = Vec::new(); + for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { + lookup_with_base(tcx, base, ns, rest, &mut out); + } + out +} + +/// Finds the crates called `name`, may be multiple due to multiple major versions. +pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { + static BY_NAME: OnceLock<FxHashMap<Symbol, Vec<DefId>>> = OnceLock::new(); + let map = BY_NAME.get_or_init(|| { + let mut map = FxHashMap::default(); + map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); + for &num in tcx.crates(()) { + map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); + } + map + }); + match map.get(&name) { + Some(def_ids) => def_ids, + None => &[], + } +} + +fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { + let ty = match name { + sym::bool => SimplifiedType::Bool, + sym::char => SimplifiedType::Char, + sym::str => SimplifiedType::Str, + sym::array => SimplifiedType::Array, + sym::slice => SimplifiedType::Slice, + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), + sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), + sym::isize => SimplifiedType::Int(IntTy::Isize), + sym::i8 => SimplifiedType::Int(IntTy::I8), + sym::i16 => SimplifiedType::Int(IntTy::I16), + sym::i32 => SimplifiedType::Int(IntTy::I32), + sym::i64 => SimplifiedType::Int(IntTy::I64), + sym::i128 => SimplifiedType::Int(IntTy::I128), + sym::usize => SimplifiedType::Uint(UintTy::Usize), + sym::u8 => SimplifiedType::Uint(UintTy::U8), + sym::u16 => SimplifiedType::Uint(UintTy::U16), + sym::u32 => SimplifiedType::Uint(UintTy::U32), + sym::u64 => SimplifiedType::Uint(UintTy::U64), + sym::u128 => SimplifiedType::Uint(UintTy::U128), + sym::f32 => SimplifiedType::Float(FloatTy::F32), + sym::f64 => SimplifiedType::Float(FloatTy::F64), + _ => return &[], + }; + + tcx.incoherent_impls(ty) +} + +/// Resolves a def path like `vec::Vec` with the base `std`. +fn lookup_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec<DefId>) { + loop { + match *path { + [segment] => { + out.extend(item_child_by_name(tcx, base, ns, segment)); + + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + let inherent_impl_children = tcx + .inherent_impls(base) + .iter() + .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); + out.extend(inherent_impl_children); + + return; + }, + [segment, ref rest @ ..] => { + path = rest; + let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { + return; + }; + base = child; + }, + [] => unreachable!(), + } + } +} + +fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> { + if let Some(local_id) = def_id.as_local() { + local_item_child_by_name(tcx, local_id, ns, name) + } else { + non_local_item_child_by_name(tcx, def_id, ns, name) + } +} + +fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option<DefId> { + let root_mod; + let item_kind = match tcx.hir_node_by_def_id(local_id) { + Node::Crate(r#mod) => { + root_mod = ItemKind::Mod(Ident::dummy(), r#mod); + &root_mod + }, + Node::Item(item) => &item.kind, + _ => return None, + }; + + let res = |ident: Ident, owner_id: OwnerId| { + if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { + Some(owner_id.to_def_id()) + } else { + None + } + }; + + match item_kind { + ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { + let item = tcx.hir_item(item_id); + if let ItemKind::Use(path, UseKind::Single(ident)) = item.kind { + if ident.name == name { + path.res + .iter() + .find(|res| ns.matches(res.ns())) + .and_then(Res::opt_def_id) + } else { + None + } + } else { + res(item.kind.ident()?, item_id.owner_id) + } + }), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), + _ => None, + } +} + +fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { + if child.ident.name == name && ns.matches(child.res.ns()) { + child.res.opt_def_id() + } else { + None + } + }), + DefKind::Impl { .. } => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), + _ => None, + } +} diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 3aa72cf5eaf..8645d5730fe 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -142,6 +142,7 @@ pub trait SpanRangeExt: SpanRange { map_range(cx.sess().source_map(), self.into_range(), f) } + #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] /// Extends the range to include all preceding whitespace characters, unless there /// are non-whitespace characters left on the same line after `self`. /// diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 4825bf12f24..93dec113d31 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -357,7 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { match op { AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Assign => format!("{lhs} = {rhs}"), - AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()), + AssocOp::AssignOp(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Cast => format!("{lhs} as {rhs}"), AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()), } diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs new file mode 100644 index 00000000000..9428262b99a --- /dev/null +++ b/clippy_utils/src/sym.rs @@ -0,0 +1,200 @@ +#![allow(non_upper_case_globals)] + +use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT; + +#[doc(no_inline)] +pub use rustc_span::sym::*; + +macro_rules! val { + ($name:ident) => { + stringify!($name) + }; + ($name:ident $value:literal) => { + $value + }; +} + +macro_rules! generate { + ($($name:ident $(: $value:literal)? ,)*) => { + /// To be supplied to `rustc_interface::Config` + pub const EXTRA_SYMBOLS: &[&str] = &[ + $( + val!($name $($value)?), + )* + ]; + + $( + pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); + )* + }; +} + +generate! { + abs, + align_of, + ambiguous_glob_reexports, + as_bytes, + as_deref_mut, + as_deref, + as_mut, + AsyncReadExt, + AsyncWriteExt, + BACKSLASH_SINGLE_QUOTE: r"\'", + Binary, + build_hasher, + bytes, + cargo_clippy: "cargo-clippy", + Cargo_toml: "Cargo.toml", + cast, + chars, + CLIPPY_ARGS, + CLIPPY_CONF_DIR, + clippy_utils, + clone_into, + cloned, + collect, + const_ptr, + contains, + copied, + CRLF: "\r\n", + Current, + de, + Deserialize, + diagnostics, + disallowed_types, + DOUBLE_QUOTE: "\"", + EarlyLintPass, + ends_with, + enum_glob_use, + error, + ErrorKind, + exp, + extend, + finish_non_exhaustive, + finish, + flat_map, + for_each, + from_bytes_with_nul_unchecked, + from_bytes_with_nul, + from_ptr, + from_raw, + from_ref, + from_str_radix, + fs, + futures_util, + get, + hidden_glob_reexports, + hygiene, + insert, + int_roundings, + into_bytes, + into_owned, + IntoIter, + io, + is_ascii, + is_empty, + is_err, + is_none, + is_ok, + is_some, + itertools, + Itertools, + kw, + last, + lazy_static, + Lazy, + LF: "\n", + Lint, + ln, + lock_api, + log, + LowerExp, + LowerHex, + macro_use_imports, + map_or_else, + map_or, + max, + MAX, + mem, + min, + MIN, + mode, + module_name_repetitions, + msrv, + msrvs, + MsrvStack, + mut_ptr, + mutex, + needless_return, + next_tuple, + Octal, + once_cell, + OpenOptions, + or_default, + Other, + parse, + PathLookup, + paths, + powf, + powi, + push, + redundant_pub_crate, + regex, + Regex, + RegexBuilder, + RegexSet, + reserve, + resize, + restriction, + rustc_lint_defs, + rustc_lint, + rustc_span, + rustfmt_skip, + rwlock, + serde, + set_len, + set_mode, + set_readonly, + signum, + single_component_path_imports, + span_lint_and_then, + split_whitespace, + split, + sqrt, + Start, + Step, + style, + symbol, + Symbol, + SyntaxContext, + take, + TBD, + then_some, + to_ascii_lowercase, + to_ascii_uppercase, + to_digit, + to_lowercase, + to_owned, + to_uppercase, + tokio, + unreachable_pub, + unsafe_removed_from_name, + unused_braces, + unused_extern_crates, + unused_import_braces, + unused_trait_names, + unused, + unwrap_err, + unwrap_or_default, + unwrap_or_else, + UpperExp, + UpperHex, + V4, + V6, + Visitor, + warnings, + Weak, + wildcard_imports, + with_capacity, + wrapping_offset, +} diff --git a/clippy_utils/src/sym_helper.rs b/clippy_utils/src/sym_helper.rs deleted file mode 100644 index f47dc80ebad..00000000000 --- a/clippy_utils/src/sym_helper.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[macro_export] -/// Convenience wrapper around rustc's `Symbol::intern` -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index a72414f2aff..c50ad17bfad 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -19,8 +19,8 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; 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, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; @@ -32,7 +32,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; -use crate::{def_path_def_ids, match_def_path, path_res}; +use crate::path_res; +use crate::paths::{PathNS, lookup_path_str}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -156,7 +157,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { cx.tcx .get_diagnostic_item(sym::Iterator) - .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item")) + .and_then(|iter_did| cx.get_associated_type(ty, iter_did, sym::Item)) } /// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type @@ -229,9 +230,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< /// Checks whether a type implements a trait. /// The function returns false in case the type contains an inference variable. /// -/// See: -/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`]. -/// * [Common tools for writing lints] for an example how to use this function and other options. +/// See [Common tools for writing lints] for an example how to use this function and other options. /// /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait pub fn implements_trait<'tcx>( @@ -359,56 +358,6 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize -// this function can be removed once the `normalize` method does not panic when normalization does -// not succeed -/// Checks if `Ty` is normalizable. This function is useful -/// to avoid crashes on `layout_of`. -pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - is_normalizable_helper(cx, param_env, ty, 0, &mut FxHashMap::default()) -} - -fn is_normalizable_helper<'tcx>( - cx: &LateContext<'tcx>, - param_env: ParamEnv<'tcx>, - ty: Ty<'tcx>, - depth: usize, - cache: &mut FxHashMap<Ty<'tcx>, bool>, -) -> bool { - if let Some(&cached_result) = cache.get(&ty) { - return cached_result; - } - if !cx.tcx.recursion_limit().value_within_limit(depth) { - return false; - } - // Prevent recursive loops by answering `true` to recursive requests with the same - // type. This will be adjusted when the outermost call analyzes all the type - // components. - cache.insert(ty, true); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let cause = ObligationCause::dummy(); - let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, args) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), depth + 1, cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, depth + 1, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false - }; - cache.insert(ty, result); - result -} - /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any /// integer or floating-point number type). /// @@ -474,17 +423,6 @@ pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) } -/// Checks if type is struct, enum or union type with the given def path. -/// -/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { - match ty.kind() { - ty::Adt(adt, _) => match_def_path(cx, adt.did(), path), - _ => false, - } -} - /// Checks if the drop order for a type matters. /// /// Some std types implement drop solely to deallocate memory. For these types, and composites @@ -915,7 +853,7 @@ pub fn for_each_top_level_late_bound_region<B>( ControlFlow::Continue(()) } } - fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { + fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { self.index += 1; let res = t.super_visit_with(self); self.index -= 1; @@ -993,9 +931,6 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option< /// account the layout of type parameters. pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { use rustc_middle::ty::layout::LayoutOf; - if !is_normalizable(cx, cx.param_env, ty) { - return 0; - } 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(), @@ -1109,10 +1044,10 @@ pub fn make_projection<'tcx>( assoc_ty: Symbol, args: GenericArgsRef<'tcx>, ) -> Option<AliasTy<'tcx>> { - let Some(assoc_item) = tcx.associated_items(container_id).find_by_name_and_kind( + let Some(assoc_item) = tcx.associated_items(container_id).find_by_ident_and_kind( tcx, Ident::with_dummy_span(assoc_ty), - AssocKind::Type, + AssocTag::Type, container_id, ) else { debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); @@ -1184,10 +1119,7 @@ impl<'tcx> InteriorMut<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self { let ignored_def_ids = ignore_interior_mutability .iter() - .flat_map(|ignored_ty| { - let path: Vec<&str> = ignored_ty.split("::").collect(); - def_path_def_ids(tcx, path.as_slice()) - }) + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) .collect(); Self { @@ -1345,7 +1277,7 @@ pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_n .associated_items(did) .filter_by_name_unhygienic(method_name) .next() - .filter(|item| item.kind == AssocKind::Fn) + .filter(|item| item.as_tag() == AssocTag::Fn) }) } else { None @@ -1376,3 +1308,67 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t _ => None, } } + +/// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if +/// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze` +/// types, or `PhantomData` types containing any of the previous. This can be used to check whether +/// skipping iterating over an iterator will change its behavior. +pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>) -> bool { + fn normalize_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty) + } + + /// Check if `ty` contains mutable references or equivalent, which includes: + /// - A mutable reference/pointer. + /// - A reference/pointer to a non-`Freeze` type. + /// - A `PhantomData` type containing any of the previous. + fn has_non_owning_mutable_access_inner<'tcx>( + cx: &LateContext<'tcx>, + phantoms: &mut FxHashSet<Ty<'tcx>>, + ty: Ty<'tcx>, + ) -> bool { + match ty.kind() { + ty::Adt(adt_def, args) if adt_def.is_phantom_data() => { + phantoms.insert(ty) + && args + .types() + .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)) + }, + ty::Adt(adt_def, args) => adt_def.all_fields().any(|field| { + has_non_owning_mutable_access_inner(cx, phantoms, normalize_ty(cx, field.ty(cx.tcx, args))) + }), + ty::Array(elem_ty, _) | ty::Slice(elem_ty) => has_non_owning_mutable_access_inner(cx, phantoms, *elem_ty), + ty::RawPtr(pointee_ty, mutability) | ty::Ref(_, pointee_ty, mutability) => { + mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env()) + }, + ty::Closure(_, closure_args) => { + matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures)) + }, + ty::Tuple(tuple_args) => tuple_args + .iter() + .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)), + _ => false, + } + } + + let mut phantoms = FxHashSet::default(); + has_non_owning_mutable_access_inner(cx, &mut phantoms, iter_ty) +} + +/// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec<T>`. +pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_slice() + || ty.is_array() + || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) +} + +/// Gets the index of a field by name. +pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> { + match *ty.kind() { + ty::Adt(def, _) if def.is_union() || def.is_struct() => { + def.non_enum_variant().fields.iter().position(|f| f.name == name) + }, + ty::Tuple(_) => name.as_str().parse::<usize>().ok(), + _ => None, + } +} diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 3398ff8af2f..6e358662327 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -11,14 +11,14 @@ //! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should //! be considered a bug. -use crate::def_path_res; +use crate::paths::{PathNS, lookup_path}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; mod certainty; use certainty::{Certainty, Meet, join, meet}; @@ -194,7 +194,7 @@ fn path_segment_certainty( path_segment: &PathSegment<'_>, resolves_to_type: bool, ) -> Certainty { - let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) { + let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) { // A definition's type is certain if it refers to something without generics (e.g., a crate or module, or // an unparameterized type), or the generics are instantiated with arguments that are certain. // @@ -267,17 +267,24 @@ fn path_segment_certainty( /// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`. /// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`. -fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option<Res> { +fn update_res( + cx: &LateContext<'_>, + parent_certainty: Certainty, + path_segment: &PathSegment<'_>, + resolves_to_type: bool, +) -> Option<Res> { if path_segment.res == Res::Err && let Some(def_id) = parent_certainty.to_def_id() { let mut def_path = cx.get_def_path(def_id); def_path.push(path_segment.ident.name); - let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>()); - if let [res] = reses.as_slice() { Some(*res) } else { None } - } else { - None + let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value }; + if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() { + return Some(Res::Def(cx.tcx.def_kind(id), id)); + } } + + None } #[allow(clippy::cast_possible_truncation)] diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index fe488ef89da..d4bf6cd48a1 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -6,7 +6,6 @@ // positives. #![feature(iter_collect_into)] -#![feature(let_chains)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index eb246030d01..da41bdd27bc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-04-03" +channel = "nightly-2025-05-14" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/driver.rs b/src/driver.rs index c6b3c06baa4..f8acf88cf81 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,7 +1,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] -#![feature(let_chains)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints @@ -14,6 +13,7 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::sym; use rustc_interface::interface; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; @@ -78,7 +78,7 @@ fn track_clippy_args(psess: &mut ParseSess, args_env_var: Option<&str>) { psess .env_depinfo .get_mut() - .insert((Symbol::intern("CLIPPY_ARGS"), args_env_var.map(Symbol::intern))); + .insert((sym::CLIPPY_ARGS, args_env_var.map(Symbol::intern))); } /// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy @@ -89,7 +89,7 @@ fn track_files(psess: &mut ParseSess) { // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` // with the current directory set to `CARGO_MANIFEST_DIR` so a relative path is fine if Path::new("Cargo.toml").exists() { - file_depinfo.insert(Symbol::intern("Cargo.toml")); + file_depinfo.insert(sym::Cargo_toml); } // `clippy.toml` will be automatically tracked as it's loaded with `sess.source_map().load_file()` @@ -145,7 +145,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { // Trigger a rebuild if CLIPPY_CONF_DIR changes. The value must be a valid string so // changes between dirs that are invalid UTF-8 will not trigger rebuilds psess.env_depinfo.get_mut().insert(( - Symbol::intern("CLIPPY_CONF_DIR"), + sym::CLIPPY_CONF_DIR, env::var("CLIPPY_CONF_DIR").ok().map(|dir| Symbol::intern(&dir)), )); })); @@ -158,8 +158,10 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_config::Conf::read(sess, &conf_path); clippy_lints::register_lints(lint_store, conf); - clippy_lints::register_pre_expansion_lints(lint_store, conf); + #[cfg(feature = "internal")] + clippy_lints_internal::register_lints(lint_store); })); + config.extra_symbols = sym::EXTRA_SYMBOLS.into(); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be // run on the unoptimized MIR. On the other hand this results in some false negatives. If diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0f75914335b..78b27e2f613 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, let_chains)] +#![feature(rustc_private)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] @@ -14,6 +14,7 @@ use pulldown_cmark::{Options, Parser, html}; use serde::Deserialize; use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; +use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; @@ -156,6 +157,7 @@ impl TestContext { ..Config::rustc(Path::new("tests").join(test_dir)) }; let defaults = config.comment_defaults.base(); + defaults.set_custom("edition", Edition("2024".into())); defaults.exit_status = None.into(); if mandatory_annotations { defaults.require_annotations = Some(Spanned::dummy(true)).into(); diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 7927a88fa4b..4ac2bd53285 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -36,6 +36,7 @@ fn dogfood() { for package in [ "./", "clippy_dev", + "clippy_lints_internal", "clippy_lints", "clippy_utils", "clippy_config", @@ -43,8 +44,8 @@ fn dogfood() { "rustc_tools_util", ] { println!("linting {package}"); - if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) { - failed_packages.push(if package.is_empty() { "root" } else { package }); + if !run_clippy_for_package(package) { + failed_packages.push(package); } } @@ -56,7 +57,7 @@ fn dogfood() { } #[must_use] -fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { +fn run_clippy_for_package(project: &str) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -78,18 +79,17 @@ fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { } } - command.arg("--").args(args); + command.arg("--"); command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - command.args(["-D", "clippy::dbg_macro"]); - - if cfg!(feature = "internal") { - // internal lints only exist if we build with the internal feature - command.args(["-D", "clippy::internal"]); - } else { + command.args(["-D", "clippy::all", "-D", "clippy::pedantic", "-D", "clippy::dbg_macro"]); + if !cfg!(feature = "internal") { // running a clippy built without internal lints on the clippy source - // that contains e.g. `allow(clippy::invalid_paths)` + // that contains e.g. `allow(clippy::symbol_as_str)` command.args(["-A", "unknown_lints"]); } + // Workaround for not being a workspace, add the crate's directory back to the path + command.args(["--remap-path-prefix", &format!("={project}")]); + command.status().unwrap().success() } diff --git a/tests/integration.rs b/tests/integration.rs index 13cf36823c5..cb7d61eee24 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -30,7 +30,7 @@ fn integration_test() { let repo_dir = tempfile::tempdir() .expect("couldn't create temp dir") - .into_path() + .keep() .join(crate_name); let st = Command::new("git") diff --git a/tests/ui-internal/auxiliary/paths.rs b/tests/ui-internal/auxiliary/paths.rs deleted file mode 100644 index 52fcaec4df3..00000000000 --- a/tests/ui-internal/auxiliary/paths.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub static OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/tests/ui-internal/check_clippy_version_attribute.rs b/tests/ui-internal/check_clippy_version_attribute.rs index e5f6001b74d..897002949e6 100644 --- a/tests/ui-internal/check_clippy_version_attribute.rs +++ b/tests/ui-internal/check_clippy_version_attribute.rs @@ -1,5 +1,5 @@ -#![deny(clippy::internal)] #![feature(rustc_private)] +#![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] #[macro_use] extern crate rustc_middle; @@ -86,6 +86,15 @@ mod internal_clippy_lints { } use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE; -declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]); +declare_lint_pass!(Pass2 => [ + VALID_ONE, + VALID_TWO, + VALID_THREE, + INVALID_ONE, + INVALID_TWO, + MISSING_ATTRIBUTE_ONE, + MISSING_ATTRIBUTE_TWO, + ALLOW_MISSING_ATTRIBUTE_ONE, +]); fn main() {} diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 1129c35d1d0..952bc944030 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -12,11 +12,10 @@ LL | | } | = help: please use a valid semantic version, see `doc/adding_lints.md` note: the lint level is defined here - --> tests/ui-internal/check_clippy_version_attribute.rs:1:9 + --> tests/ui-internal/check_clippy_version_attribute.rs:2:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute @@ -47,7 +46,11 @@ LL | | } | |_^ | = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` - = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` +note: the lint level is defined here + --> tests/ui-internal/check_clippy_version_attribute.rs:2:51 + | +LL | #![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value diff --git a/tests/ui-internal/check_formulation.rs b/tests/ui-internal/check_formulation.rs index 8265a78769d..bcbb0d78319 100644 --- a/tests/ui-internal/check_formulation.rs +++ b/tests/ui-internal/check_formulation.rs @@ -1,4 +1,5 @@ -#![warn(clippy::almost_standard_lint_formulation)] +#![deny(clippy::almost_standard_lint_formulation)] +#![allow(clippy::lint_without_lint_pass)] #![feature(rustc_private)] #[macro_use] diff --git a/tests/ui-internal/check_formulation.stderr b/tests/ui-internal/check_formulation.stderr index b16e1bf8687..9aeb9e1f2d4 100644 --- a/tests/ui-internal/check_formulation.stderr +++ b/tests/ui-internal/check_formulation.stderr @@ -1,15 +1,18 @@ error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:23:5 + --> tests/ui-internal/check_formulation.rs:24:5 | LL | /// Check for lint formulations that are correct | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using `Checks for` - = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` +note: the lint level is defined here + --> tests/ui-internal/check_formulation.rs:1:9 + | +LL | #![deny(clippy::almost_standard_lint_formulation)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:34:5 + --> tests/ui-internal/check_formulation.rs:35:5 | LL | /// Detects uses of incorrect formulations | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed index 918e33345a7..76f68686ee2 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.fixed +++ b/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::collapsible_span_lint_calls)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs index 2f289ae2b48..214c8783a66 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::collapsible_span_lint_calls)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index a2be1f1cd36..9c83538947c 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -10,9 +10,8 @@ LL | | }); note: the lint level is defined here --> tests/ui-internal/collapsible_span_lint_calls.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::collapsible_span_lint_calls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call is collapsible --> tests/ui-internal/collapsible_span_lint_calls.rs:39:9 diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 71819fe3707..c7e92b1bf16 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -6,7 +6,7 @@ //@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc <version> running on <target>" //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" -#![deny(clippy::internal)] +#![deny(clippy::produce_ice)] #![allow(clippy::missing_clippy_version_attribute)] fn it_looks_like_you_are_trying_to_kill_clippy() {} diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index 589e1190a90..884d3d035a2 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -8,7 +8,7 @@ error: internal compiler error: Would you like some help with that? LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: delayed at clippy_lints/src/utils/internal_lints/produce_ice.rs - disabled backtrace +note: delayed at clippy_lints_internal/src/produce_ice.rs - disabled backtrace --> tests/ui-internal/custom_ice_message.rs:12:1 | LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} diff --git a/tests/ui-internal/default_lint.rs b/tests/ui-internal/default_lint.rs index 959bfd27e38..809f2c4d080 100644 --- a/tests/ui-internal/default_lint.rs +++ b/tests/ui-internal/default_lint.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::default_lint)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/default_lint.stderr b/tests/ui-internal/default_lint.stderr index 9d4c2e15349..2c700ec82dc 100644 --- a/tests/ui-internal/default_lint.stderr +++ b/tests/ui-internal/default_lint.stderr @@ -13,9 +13,8 @@ LL | | } note: the lint level is defined here --> tests/ui-internal/default_lint.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::default_lint)] + | ^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui-internal/derive_deserialize_allowing_unknown.rs b/tests/ui-internal/derive_deserialize_allowing_unknown.rs new file mode 100644 index 00000000000..9dc8e9e8f4c --- /dev/null +++ b/tests/ui-internal/derive_deserialize_allowing_unknown.rs @@ -0,0 +1,60 @@ +#![deny(clippy::derive_deserialize_allowing_unknown)] + +use serde::{Deserialize, Deserializer}; + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +struct Struct { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +enum Enum { + A(bool), + B { limit: u64 }, +} + +// negative tests + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct StructWithDenyUnknownFields { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithDenyUnknownFields { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +#[serde(untagged, deny_unknown_fields)] +enum MultipleSerdeAttributes { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +struct TupleStruct(u64, bool); + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithOnlyTupleVariants { + A(bool), + B(u64), +} + +struct ManualSerdeImplementation; + +impl<'de> Deserialize<'de> for ManualSerdeImplementation { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let () = <() as Deserialize>::deserialize(deserializer)?; + Ok(ManualSerdeImplementation) + } +} diff --git a/tests/ui-internal/derive_deserialize_allowing_unknown.stderr b/tests/ui-internal/derive_deserialize_allowing_unknown.stderr new file mode 100644 index 00000000000..93d64826c99 --- /dev/null +++ b/tests/ui-internal/derive_deserialize_allowing_unknown.stderr @@ -0,0 +1,23 @@ +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:5:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:1:9 + | +LL | #![deny(clippy::derive_deserialize_allowing_unknown)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:11:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui-internal/disallow_span_lint.rs b/tests/ui-internal/disallow_span_lint.rs index 3fed38cab64..36e4158f6e6 100644 --- a/tests/ui-internal/disallow_span_lint.rs +++ b/tests/ui-internal/disallow_span_lint.rs @@ -1,4 +1,5 @@ #![feature(rustc_private)] +#![deny(clippy::disallowed_methods)] extern crate rustc_errors; extern crate rustc_hir; diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index 9a7a7ecbbff..f03a745963e 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -1,15 +1,18 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` - --> tests/ui-internal/disallow_span_lint.rs:14:8 + --> tests/ui-internal/disallow_span_lint.rs:15:8 | LL | cx.span_lint(lint, span, |lint| { | ^^^^^^^^^ | = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` +note: the lint level is defined here + --> tests/ui-internal/disallow_span_lint.rs:2:9 + | +LL | #![deny(clippy::disallowed_methods)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:21:9 + --> tests/ui-internal/disallow_span_lint.rs:22:9 | LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed deleted file mode 100644 index 92d3b1537e0..00000000000 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ /dev/null @@ -1,40 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] -#![feature(rustc_private)] - -extern crate rustc_span; - -use rustc_span::symbol::Symbol; - -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} - -fn main() { - // Direct use of Symbol::intern - let _ = rustc_span::sym::f32; - //~^ interning_defined_symbol - - // Using a sym macro - let _ = rustc_span::sym::f32; - //~^ interning_defined_symbol - - // Correct suggestion when symbol isn't stringified constant name - let _ = rustc_span::sym::proc_dash_macro; - //~^ interning_defined_symbol - - // interning a keyword - let _ = rustc_span::kw::SelfLower; - //~^ interning_defined_symbol - - // Interning a symbol that is not defined - let _ = Symbol::intern("xyz123"); - let _ = sym!(xyz123); - - // Using a different `intern` function - let _ = intern("f32"); -} - -fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs deleted file mode 100644 index d1e6f9cb1c4..00000000000 --- a/tests/ui-internal/interning_defined_symbol.rs +++ /dev/null @@ -1,40 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] -#![feature(rustc_private)] - -extern crate rustc_span; - -use rustc_span::symbol::Symbol; - -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} - -fn main() { - // Direct use of Symbol::intern - let _ = Symbol::intern("f32"); - //~^ interning_defined_symbol - - // Using a sym macro - let _ = sym!(f32); - //~^ interning_defined_symbol - - // Correct suggestion when symbol isn't stringified constant name - let _ = Symbol::intern("proc-macro"); - //~^ interning_defined_symbol - - // interning a keyword - let _ = Symbol::intern("self"); - //~^ interning_defined_symbol - - // Interning a symbol that is not defined - let _ = Symbol::intern("xyz123"); - let _ = sym!(xyz123); - - // Using a different `intern` function - let _ = intern("f32"); -} - -fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr deleted file mode 100644 index c84a566436a..00000000000 --- a/tests/ui-internal/interning_defined_symbol.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:17:13 - | -LL | let _ = Symbol::intern("f32"); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` - | -note: the lint level is defined here - --> tests/ui-internal/interning_defined_symbol.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:21:13 - | -LL | let _ = sym!(f32); - | ^^^^^^^^^ help: try: `rustc_span::sym::f32` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:25:13 - | -LL | let _ = Symbol::intern("proc-macro"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:29:13 - | -LL | let _ = Symbol::intern("self"); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::kw::SelfLower` - -error: aborting due to 4 previous errors - diff --git a/tests/ui-internal/interning_literals.fixed b/tests/ui-internal/interning_literals.fixed new file mode 100644 index 00000000000..03e97768b99 --- /dev/null +++ b/tests/ui-internal/interning_literals.fixed @@ -0,0 +1,31 @@ +#![allow(clippy::let_unit_value)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn main() { + let _ = sym::f32; + //~^ interning_literals + + // Correct suggestion when symbol isn't stringified constant name + let _ = sym::proc_dash_macro; + //~^ interning_literals + + // Interning a keyword + let _ = kw::SelfLower; + //~^ interning_literals + + // Defined in clippy_utils + let _ = sym::msrv; + //~^ interning_literals + let _ = sym::Cargo_toml; + //~^ interning_literals + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_literals.rs b/tests/ui-internal/interning_literals.rs new file mode 100644 index 00000000000..561fd5702a5 --- /dev/null +++ b/tests/ui-internal/interning_literals.rs @@ -0,0 +1,31 @@ +#![allow(clippy::let_unit_value)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn main() { + let _ = Symbol::intern("f32"); + //~^ interning_literals + + // Correct suggestion when symbol isn't stringified constant name + let _ = Symbol::intern("proc-macro"); + //~^ interning_literals + + // Interning a keyword + let _ = Symbol::intern("self"); + //~^ interning_literals + + // Defined in clippy_utils + let _ = Symbol::intern("msrv"); + //~^ interning_literals + let _ = Symbol::intern("Cargo.toml"); + //~^ interning_literals + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_literals.stderr b/tests/ui-internal/interning_literals.stderr new file mode 100644 index 00000000000..9ff4194e542 --- /dev/null +++ b/tests/ui-internal/interning_literals.stderr @@ -0,0 +1,69 @@ +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:10:13 + | +LL | let _ = Symbol::intern("f32"); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::interning-literals` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("f32"); +LL + let _ = sym::f32; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:14:13 + | +LL | let _ = Symbol::intern("proc-macro"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("proc-macro"); +LL + let _ = sym::proc_dash_macro; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:18:13 + | +LL | let _ = Symbol::intern("self"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("self"); +LL + let _ = kw::SelfLower; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:22:13 + | +LL | let _ = Symbol::intern("msrv"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("msrv"); +LL + let _ = sym::msrv; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:24:13 + | +LL | let _ = Symbol::intern("Cargo.toml"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("Cargo.toml"); +LL + let _ = sym::Cargo_toml; + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui-internal/interning_literals_unfixable.rs b/tests/ui-internal/interning_literals_unfixable.rs new file mode 100644 index 00000000000..43872e95a58 --- /dev/null +++ b/tests/ui-internal/interning_literals_unfixable.rs @@ -0,0 +1,16 @@ +//@no-rustfix: paths that don't exist yet +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::Symbol; + +fn main() { + // Not yet defined + let _ = Symbol::intern("xyz123"); + //~^ interning_literals + let _ = Symbol::intern("with-dash"); + //~^ interning_literals + let _ = Symbol::intern("with.dot"); + //~^ interning_literals +} diff --git a/tests/ui-internal/interning_literals_unfixable.stderr b/tests/ui-internal/interning_literals_unfixable.stderr new file mode 100644 index 00000000000..879d9e633c2 --- /dev/null +++ b/tests/ui-internal/interning_literals_unfixable.stderr @@ -0,0 +1,43 @@ +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:10:13 + | +LL | let _ = Symbol::intern("xyz123"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::interning-literals` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("xyz123"); +LL + let _ = sym::xyz123; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:12:13 + | +LL | let _ = Symbol::intern("with-dash"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("with-dash"); +LL + let _ = sym::with_dash; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:14:13 + | +LL | let _ = Symbol::intern("with.dot"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("with.dot"); +LL + let _ = sym::with_dot; + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui-internal/invalid_msrv_attr_impl.fixed b/tests/ui-internal/invalid_msrv_attr_impl.fixed index 6804e2bbae8..238ef9ae6d0 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::missing_msrv_attr_impl)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/invalid_msrv_attr_impl.rs b/tests/ui-internal/invalid_msrv_attr_impl.rs index c625a5d9a45..7753dcaad71 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::missing_msrv_attr_impl)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/invalid_msrv_attr_impl.stderr b/tests/ui-internal/invalid_msrv_attr_impl.stderr index 0a7636313ef..d5928d8c0c2 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.stderr +++ b/tests/ui-internal/invalid_msrv_attr_impl.stderr @@ -7,9 +7,8 @@ LL | impl EarlyLintPass for Pass { note: the lint level is defined here --> tests/ui-internal/invalid_msrv_attr_impl.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::missing_msrv_attr_impl)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::missing_msrv_attr_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `extract_msrv_attr!()` to the `EarlyLintPass` implementation | LL ~ impl EarlyLintPass for Pass { diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs deleted file mode 100644 index abfb111f938..00000000000 --- a/tests/ui-internal/invalid_paths.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![warn(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] - -mod paths { - // Good path - pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; - - // Path to method on inherent impl of a primitive type - pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"]; - - // Path to method on inherent impl - pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; - - // Path with empty segment - pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - //~^ invalid_paths - - // Path with bad crate - pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - //~^ invalid_paths - - // Path with bad module - pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - //~^ invalid_paths - - // Path to method on an enum inherent impl - pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; -} - -fn main() {} diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr deleted file mode 100644 index 7bde37667be..00000000000 --- a/tests/ui-internal/invalid_paths.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: invalid path - --> tests/ui-internal/invalid_paths.rs:15:5 - | -LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-paths` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:19:5 - | -LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:23:5 - | -LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui-internal/lint_without_lint_pass.rs b/tests/ui-internal/lint_without_lint_pass.rs index 69591523432..6b649132aca 100644 --- a/tests/ui-internal/lint_without_lint_pass.rs +++ b/tests/ui-internal/lint_without_lint_pass.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::lint_without_lint_pass)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/lint_without_lint_pass.stderr b/tests/ui-internal/lint_without_lint_pass.stderr index 9cca96ca160..3798293f4c1 100644 --- a/tests/ui-internal/lint_without_lint_pass.stderr +++ b/tests/ui-internal/lint_without_lint_pass.stderr @@ -13,9 +13,8 @@ LL | | } note: the lint level is defined here --> tests/ui-internal/lint_without_lint_pass.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::lint_without_lint_pass)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui-internal/outer_expn_data.fixed b/tests/ui-internal/outer_expn_data.fixed index cb7680b8bb1..900ca5b2ab9 100644 --- a/tests/ui-internal/outer_expn_data.fixed +++ b/tests/ui-internal/outer_expn_data.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::outer_expn_expn_data)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/outer_expn_data.rs b/tests/ui-internal/outer_expn_data.rs index 41d735110b5..bcfc42aa2ac 100644 --- a/tests/ui-internal/outer_expn_data.rs +++ b/tests/ui-internal/outer_expn_data.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::outer_expn_expn_data)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/tests/ui-internal/outer_expn_data.stderr b/tests/ui-internal/outer_expn_data.stderr index 33ac91e4fb0..b86138a5d45 100644 --- a/tests/ui-internal/outer_expn_data.stderr +++ b/tests/ui-internal/outer_expn_data.stderr @@ -7,9 +7,8 @@ LL | let _ = expr.span.ctxt().outer_expn().expn_data(); note: the lint level is defined here --> tests/ui-internal/outer_expn_data.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::outer_expn_expn_data)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui-internal/slow_symbol_comparisons.fixed b/tests/ui-internal/slow_symbol_comparisons.fixed deleted file mode 100644 index 2cbd646a0fd..00000000000 --- a/tests/ui-internal/slow_symbol_comparisons.fixed +++ /dev/null @@ -1,24 +0,0 @@ -#![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/tests/ui-internal/slow_symbol_comparisons.rs b/tests/ui-internal/slow_symbol_comparisons.rs deleted file mode 100644 index 0cea3c3fcff..00000000000 --- a/tests/ui-internal/slow_symbol_comparisons.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![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/tests/ui-internal/slow_symbol_comparisons.stderr b/tests/ui-internal/slow_symbol_comparisons.stderr deleted file mode 100644 index 72cb20a7fed..00000000000 --- a/tests/ui-internal/slow_symbol_comparisons.stderr +++ /dev/null @@ -1,23 +0,0 @@ -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/tests/ui-internal/symbol_as_str.fixed b/tests/ui-internal/symbol_as_str.fixed new file mode 100644 index 00000000000..6a71b16c604 --- /dev/null +++ b/tests/ui-internal/symbol_as_str.fixed @@ -0,0 +1,28 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s == sym::f32; + //~^ symbol_as_str + s == sym::proc_dash_macro; + //~^ symbol_as_str + s == kw::SelfLower; + //~^ symbol_as_str + s == sym::msrv; + //~^ symbol_as_str + s == sym::Cargo_toml; + //~^ symbol_as_str + sym::get == s; + //~^ symbol_as_str + + let _ = match s { + //~^ symbol_as_str + sym::unwrap_err => 1, + sym::unwrap_or_default | sym::unwrap_or_else => 2, + _ => 3, + }; +} diff --git a/tests/ui-internal/symbol_as_str.rs b/tests/ui-internal/symbol_as_str.rs new file mode 100644 index 00000000000..43136504bf1 --- /dev/null +++ b/tests/ui-internal/symbol_as_str.rs @@ -0,0 +1,28 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s.as_str() == "f32"; + //~^ symbol_as_str + s.as_str() == "proc-macro"; + //~^ symbol_as_str + s.as_str() == "self"; + //~^ symbol_as_str + s.as_str() == "msrv"; + //~^ symbol_as_str + s.as_str() == "Cargo.toml"; + //~^ symbol_as_str + "get" == s.as_str(); + //~^ symbol_as_str + + let _ = match s.as_str() { + //~^ symbol_as_str + "unwrap_err" => 1, + "unwrap_or_default" | "unwrap_or_else" => 2, + _ => 3, + }; +} diff --git a/tests/ui-internal/symbol_as_str.stderr b/tests/ui-internal/symbol_as_str.stderr new file mode 100644 index 00000000000..3eeead4aa8c --- /dev/null +++ b/tests/ui-internal/symbol_as_str.stderr @@ -0,0 +1,97 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:9:5 + | +LL | s.as_str() == "f32"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: use preinterned symbols instead + | +LL - s.as_str() == "f32"; +LL + s == sym::f32; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:11:5 + | +LL | s.as_str() == "proc-macro"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "proc-macro"; +LL + s == sym::proc_dash_macro; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:13:5 + | +LL | s.as_str() == "self"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "self"; +LL + s == kw::SelfLower; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:15:5 + | +LL | s.as_str() == "msrv"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "msrv"; +LL + s == sym::msrv; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:17:5 + | +LL | s.as_str() == "Cargo.toml"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "Cargo.toml"; +LL + s == sym::Cargo_toml; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:19:14 + | +LL | "get" == s.as_str(); + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - "get" == s.as_str(); +LL + sym::get == s; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:22:19 + | +LL | let _ = match s.as_str() { + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL ~ let _ = match s { +LL | +LL ~ sym::unwrap_err => 1, +LL ~ sym::unwrap_or_default | sym::unwrap_or_else => 2, + | + +error: aborting due to 7 previous errors + diff --git a/tests/ui-internal/symbol_as_str_unfixable.rs b/tests/ui-internal/symbol_as_str_unfixable.rs new file mode 100644 index 00000000000..635f28007e9 --- /dev/null +++ b/tests/ui-internal/symbol_as_str_unfixable.rs @@ -0,0 +1,15 @@ +//@no-rustfix: paths that don't exist yet +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::Symbol; + +fn f(s: Symbol) { + s.as_str() == "xyz123"; + //~^ symbol_as_str + s.as_str() == "with-dash"; + //~^ symbol_as_str + s.as_str() == "with.dot"; + //~^ symbol_as_str +} diff --git a/tests/ui-internal/symbol_as_str_unfixable.stderr b/tests/ui-internal/symbol_as_str_unfixable.stderr new file mode 100644 index 00000000000..65664ebb451 --- /dev/null +++ b/tests/ui-internal/symbol_as_str_unfixable.stderr @@ -0,0 +1,43 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:9:5 + | +LL | s.as_str() == "xyz123"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: use preinterned symbols instead + | +LL - s.as_str() == "xyz123"; +LL + s == sym::xyz123; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:11:5 + | +LL | s.as_str() == "with-dash"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "with-dash"; +LL + s == sym::with_dash; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:13:5 + | +LL | s.as_str() == "with.dot"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "with.dot"; +LL + s == sym::with_dot; + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed deleted file mode 100644 index 577fad9341b..00000000000 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ /dev/null @@ -1,77 +0,0 @@ -//@aux-build:paths.rs -#![deny(clippy::internal)] -#![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] - -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; - -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; - -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; - -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; - -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = is_type_diagnostic_item(cx, ty, sym::Rc); - //~^ unnecessary_def_path - - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - - let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); - //~^ unnecessary_def_path - - let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); - //~^ unnecessary_def_path - let _ = cx.tcx.is_diagnostic_item(sym::Option, did); - //~^ unnecessary_def_path - let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); - //~^ unnecessary_def_path - - let _ = is_trait_method(cx, expr, sym::AsRef); - //~^ unnecessary_def_path - - let _ = is_path_diagnostic_item(cx, expr, sym::Option); - //~^ unnecessary_def_path - let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); - //~^ unnecessary_def_path - let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); - //~^ unnecessary_def_path -} - -fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index d4deb3626d0..5cd3254188d 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -1,77 +1,20 @@ -//@aux-build:paths.rs -#![deny(clippy::internal)] #![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{macro_path, sym, type_path, value_path}; -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; +static OPTION: PathLookup = type_path!(core::option::Option); +//~^ unnecessary_def_path +static SOME: PathLookup = type_path!(core::option::Option::Some); +//~^ unnecessary_def_path -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; +static RESULT: PathLookup = type_path!(core::result::Result); +//~^ unnecessary_def_path +static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); +//~^ unnecessary_def_path -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); +//~^ unnecessary_def_path -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = match_type(cx, ty, &OPTION); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, RESULT); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, &["core", "result", "Result"]); - //~^ unnecessary_def_path - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - //~^ unnecessary_def_path - - let _ = match_type(cx, ty, &paths::OPTION); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, paths::RESULT); - //~^ unnecessary_def_path - - let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - //~^ unnecessary_def_path - - let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - //~^ unnecessary_def_path - let _ = match_def_path(cx, did, &["core", "option", "Option"]); - //~^ unnecessary_def_path - let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - //~^ unnecessary_def_path - - let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - //~^ unnecessary_def_path - - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - //~^ unnecessary_def_path - let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - //~^ unnecessary_def_path - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); - //~^ unnecessary_def_path -} - -fn main() {} +static VEC_MACRO: PathLookup = macro_path!(std::vec); +//~^ unnecessary_def_path diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index 0053ba321bb..4abb1be7406 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -1,101 +1,58 @@ -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:37:13 +error: a diagnostic name exists for this path: sym::Option + --> tests/ui-internal/unnecessary_def_path.rs:6:29 | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` +LL | static OPTION: PathLookup = type_path!(core::option::Option); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui-internal/unnecessary_def_path.rs:2:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:39:13 - | -LL | let _ = match_type(cx, ty, RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:41:13 +error: a language item exists for this path: LangItem::OptionSome + --> tests/ui-internal/unnecessary_def_path.rs:8:27 | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:46:13 +LL | static SOME: PathLookup = type_path!(core::option::Option::Some); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:49:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:11:29 | -LL | let _ = match_type(cx, ty, &paths::OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:51:13 +LL | static RESULT: PathLookup = type_path!(core::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, paths::RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:54:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:13:37 | -LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:56:13 +LL | static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:59:13 +error: a diagnostic name exists for this path: sym::vec_new + --> tests/ui-internal/unnecessary_def_path.rs:16:30 | -LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:61:13 +LL | static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:63:13 - | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` +error: a diagnostic name exists for this path: sym::vec_macro + --> tests/ui-internal/unnecessary_def_path.rs:19:32 | - = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:66:13 - | -LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:69:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:71:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:73:13 +LL | static VEC_MACRO: PathLookup = macro_path!(std::vec); + | ^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: aborting due to 15 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs deleted file mode 100644 index 4801d76bd26..00000000000 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![feature(rustc_private)] -#![allow(unused)] -#![warn(clippy::unnecessary_def_path)] - -extern crate rustc_hir; - -use rustc_hir::LangItem; - -fn main() { - const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - //~^ unnecessary_def_path - const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - //~^ unnecessary_def_path - const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - //~^ unnecessary_def_path - - // Don't lint, not a diagnostic or language item - const OPS_MOD: [&str; 2] = ["core", "ops"]; -} diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr deleted file mode 100644 index ff4726de69a..00000000000 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:10:36 - | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::Deref` - = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` - -error: hardcoded path to a language item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40 - | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `LangItem::DerefMut` - -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:14:43 - | -LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::deref_method` - -error: aborting due to 3 previous errors - diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed deleted file mode 100644 index dc564daef82..00000000000 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ /dev/null @@ -1,26 +0,0 @@ -#![feature(rustc_private)] -#![deny(clippy::internal)] -#![allow( - clippy::slow_symbol_comparisons, - clippy::borrow_deref_ref, - clippy::unnecessary_operation, - unused_must_use, - clippy::missing_clippy_version_attribute -)] - -extern crate rustc_span; - -use rustc_span::symbol::{Ident, Symbol}; - -fn main() { - Symbol::intern("foo") == rustc_span::sym::clippy; - //~^ unnecessary_symbol_str - Symbol::intern("foo") == rustc_span::kw::SelfLower; - //~^ unnecessary_symbol_str - Symbol::intern("foo") != rustc_span::kw::SelfUpper; - //~^ unnecessary_symbol_str - Ident::empty().name == rustc_span::sym::clippy; - //~^ unnecessary_symbol_str - rustc_span::sym::clippy == Ident::empty().name; - //~^ unnecessary_symbol_str -} diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs deleted file mode 100644 index d74262d1294..00000000000 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![feature(rustc_private)] -#![deny(clippy::internal)] -#![allow( - clippy::slow_symbol_comparisons, - clippy::borrow_deref_ref, - clippy::unnecessary_operation, - unused_must_use, - clippy::missing_clippy_version_attribute -)] - -extern crate rustc_span; - -use rustc_span::symbol::{Ident, Symbol}; - -fn main() { - Symbol::intern("foo").as_str() == "clippy"; - //~^ unnecessary_symbol_str - Symbol::intern("foo").to_string() == "self"; - //~^ unnecessary_symbol_str - Symbol::intern("foo").to_ident_string() != "Self"; - //~^ unnecessary_symbol_str - &*Ident::empty().as_str() == "clippy"; - //~^ unnecessary_symbol_str - "clippy" == Ident::empty().to_string(); - //~^ unnecessary_symbol_str -} diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr deleted file mode 100644 index 517a395e93f..00000000000 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: unnecessary `Symbol` to string conversion - --> 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` - | -note: the lint level is defined here - --> tests/ui-internal/unnecessary_symbol_str.rs:2:9 - | -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:18:5 - | -LL | Symbol::intern("foo").to_string() == "self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::kw::SelfLower` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 - | -LL | Symbol::intern("foo").to_ident_string() != "Self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::kw::SelfUpper` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:22: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:24:5 - | -LL | "clippy" == Ident::empty().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` - -error: aborting due to 5 previous errors - diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed index f12273954c6..5e189471b00 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::collapsible_if)] fn main() { diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs index 5a984d7a3cb..525eebf632a 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::collapsible_if)] fn main() { diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr index c22a65a4473..c9de166a969 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr @@ -1,5 +1,5 @@ error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:5:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:4:5 | LL | / if let Some(a) = Some(3) { LL | | // with comment @@ -21,7 +21,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:13:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:12:5 | LL | / if let Some(a) = Some(3) { LL | | // with comment @@ -41,7 +41,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:21:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:20:5 | LL | / if Some(3) == Some(4).map(|x| x - 1) { LL | | // with comment diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs index e953a2a4e90..2a6097fb579 100644 --- a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs +++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -11,9 +11,9 @@ fn issue10272() { // should trigger warning let x = Cell::new(true); if x.get() { + //~^ ifs_same_cond } else if !x.take() { } else if x.get() { - //~^ ifs_same_cond } else { } } diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr b/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr index d67e7fca656..adc44358c4c 100644 --- a/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr +++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr @@ -1,14 +1,12 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:15:15 - | -LL | } else if x.get() { - | ^^^^^^^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:13:8 | LL | if x.get() { | ^^^^^^^ +... +LL | } else if x.get() { + | ^^^^^^^ + | = note: `-D clippy::ifs-same-cond` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` diff --git a/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml new file mode 100644 index 00000000000..3ed7cedbd14 --- /dev/null +++ b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml @@ -0,0 +1 @@ +allow-exact-repetitions = false diff --git a/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs new file mode 100644 index 00000000000..20603766624 --- /dev/null +++ b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs @@ -0,0 +1,13 @@ +#![warn(clippy::module_name_repetitions)] +#![allow(dead_code)] + +pub mod foo { + // this line should produce a warning: + pub fn foo() {} + //~^ module_name_repetitions + + // but this line shouldn't + pub fn to_foo() {} +} + +fn main() {} diff --git a/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr new file mode 100644 index 00000000000..8e6f726d02c --- /dev/null +++ b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr @@ -0,0 +1,11 @@ +error: item name is the same as its containing module's name + --> tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs:6:12 + | +LL | pub fn foo() {} + | ^^^ + | + = note: `-D clippy::module-name-repetitions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/missing_docs_allow_unused/clippy.toml b/tests/ui-toml/missing_docs_allow_unused/clippy.toml new file mode 100644 index 00000000000..2fe64b2755b --- /dev/null +++ b/tests/ui-toml/missing_docs_allow_unused/clippy.toml @@ -0,0 +1 @@ +missing-docs-allow-unused = true diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs new file mode 100644 index 00000000000..155f680c7b1 --- /dev/null +++ b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs @@ -0,0 +1,26 @@ +//! Test file for missing_docs_in_private_items lint with allow_unused configuration +#![warn(clippy::missing_docs_in_private_items)] +#![allow(dead_code)] + +/// A struct with some documented and undocumented fields +struct Test { + /// This field is documented + field1: i32, + _unused: i32, // This should not trigger a warning because it starts with an underscore + field3: i32, //~ missing_docs_in_private_items +} + +struct Test2 { + //~^ missing_docs_in_private_items + _field1: i32, // This should not trigger a warning + _field2: i32, // This should not trigger a warning +} + +struct Test3 { + //~^ missing_docs_in_private_items + /// This field is documented although this is not mandatory + _unused: i32, // This should not trigger a warning because it starts with an underscore + field2: i32, //~ missing_docs_in_private_items +} + +fn main() {} diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr new file mode 100644 index 00000000000..8f511883e90 --- /dev/null +++ b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr @@ -0,0 +1,38 @@ +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:10:5 + | +LL | field3: i32, + | ^^^^^^^^^^^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:13:1 + | +LL | / struct Test2 { +LL | | +LL | | _field1: i32, // This should not trigger a warning +LL | | _field2: i32, // This should not trigger a warning +LL | | } + | |_^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:19:1 + | +LL | / struct Test3 { +LL | | +LL | | /// This field is documented although this is not mandatory +LL | | _unused: i32, // This should not trigger a warning because it starts with an underscore +LL | | field2: i32, +LL | | } + | |_^ + +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:23:5 + | +LL | field2: i32, + | ^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs index 08a8e1186d5..13e19e9fe14 100644 --- a/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs +++ b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs @@ -29,7 +29,7 @@ unsafe impl<T> Send for MyOption<T> {} //~^ non_send_fields_in_send_ty // All fields are disallowed when raw pointer heuristic is off -extern "C" { +unsafe extern "C" { type NonSend; } diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index 41dbd506847..c7a326f2829 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -14,4 +14,7 @@ disallowed-methods = [ "conf_disallowed_methods::Struct::method", "conf_disallowed_methods::Trait::provided_method", "conf_disallowed_methods::Trait::implemented_method", + # re-exports + "conf_disallowed_methods::identity", + "conf_disallowed_methods::renamed", ] diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index dd170d6baf8..2dac01649a0 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -8,6 +8,9 @@ extern crate regex; use futures::stream::{empty, select_all}; use regex::Regex; +use std::convert::identity; +use std::hint::black_box as renamed; + fn local_fn() {} struct Struct; @@ -71,4 +74,9 @@ fn main() { //~^ disallowed_methods s.implemented_method(); //~^ disallowed_methods + + identity(()); + //~^ disallowed_methods + renamed(1); + //~^ disallowed_methods } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index f7dda81eb93..20474ad6e92 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:33:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `regex::Regex::is_match` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:35:8 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:38:8 | LL | re.is_match("abc"); | ^^^^^^^^ @@ -16,76 +16,88 @@ LL | re.is_match("abc"); = note: no matching allowed error: use of a disallowed method `std::iter::Iterator::sum` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:14 | LL | a.iter().sum::<i32>(); | ^^^ error: use of a disallowed method `slice::sort_unstable` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:45:7 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:46:20 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:49:20 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:50:61 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:53:61 | LL | let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:28 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:57:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:53 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:31 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_fn` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:66:5 | LL | local_fn(); | ^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_mod::f` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:65:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:5 | LL | local_mod::f(); | ^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Struct::method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:71:7 | LL | s.method(); | ^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:70:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:73:7 | LL | s.provided_method(); | ^^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:72:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:75:7 | LL | s.implemented_method(); | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: use of a disallowed method `conf_disallowed_methods::identity` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:78:5 + | +LL | identity(()); + | ^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::renamed` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:80:5 + | +LL | renamed(1); + | ^^^^^^^ + +error: aborting due to 16 previous errors diff --git a/tests/ui-toml/toml_disallowed_types/clippy.toml b/tests/ui-toml/toml_disallowed_types/clippy.toml index 6cb9e2ef954..08e35017f78 100644 --- a/tests/ui-toml/toml_disallowed_types/clippy.toml +++ b/tests/ui-toml/toml_disallowed_types/clippy.toml @@ -6,7 +6,7 @@ disallowed-types = [ "std::thread::Thread", "std::time::Instant", "std::io::Read", - "std::primitive::usize", + "usize", "bool", # can give path and reason with an inline table { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, diff --git a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr index 18bc36ca1e3..061cdc7649a 100644 --- a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr +++ b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr @@ -37,7 +37,7 @@ error: use of a disallowed type `std::io::Read` LL | fn trait_obj(_: &dyn std::io::Read) {} | ^^^^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:33 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} @@ -49,13 +49,13 @@ error: use of a disallowed type `bool` LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:28 | LL | fn const_generics<const C: usize>() {} | ^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:33:24 | LL | struct GenArg<const U: usize>([u8; U]); @@ -123,7 +123,7 @@ error: use of a disallowed type `proc_macro2::Ident` LL | let _ = syn::Ident::new("", todo!()); | ^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:61:12 | LL | let _: usize = 64_usize; diff --git a/tests/ui-toml/toml_invalid_path/clippy.toml b/tests/ui-toml/toml_invalid_path/clippy.toml index 6d0d732a922..997ed47b71c 100644 --- a/tests/ui-toml/toml_invalid_path/clippy.toml +++ b/tests/ui-toml/toml_invalid_path/clippy.toml @@ -1,12 +1,15 @@ -[[disallowed-types]] -path = "std::result::Result::Err" - [[disallowed-macros]] path = "bool" [[disallowed-methods]] path = "std::process::current_exe" +[[disallowed-methods]] +path = "" + +[[disallowed-types]] +path = "std::result::Result::Err" + # negative test [[disallowed-methods]] diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs index c1520382703..ff4eada3900 100644 --- a/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs +++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs @@ -1,5 +1,6 @@ //@error-in-other-file: expected a macro, found a primitive type -//@error-in-other-file: `std::process::current_exe` does not refer to an existing function -//@error-in-other-file: expected a type, found a tuple variant +//@error-in-other-file: `std::process::current_exe` does not refer to a reachable function +//@error-in-other-file: `` does not refer to a reachable function +//@error-in-other-file: expected a type, found a variant fn main() {} diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr index 82550108eba..59a427dc99c 100644 --- a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr +++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr @@ -1,23 +1,38 @@ warning: expected a macro, found a primitive type - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 | LL | / [[disallowed-macros]] LL | | path = "bool" | |_____________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: `std::process::current_exe` does not refer to an existing function - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 +warning: `std::process::current_exe` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 | LL | / [[disallowed-methods]] LL | | path = "std::process::current_exe" | |__________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: expected a type, found a tuple variant - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 +warning: `` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 + | +LL | / [[disallowed-methods]] +LL | | path = "" + | |_________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: expected a type, found a variant + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:10:1 | LL | / [[disallowed-types]] LL | | path = "std::result::Result::Err" | |_________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: 3 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml b/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml new file mode 100644 index 00000000000..82560cfd5e2 --- /dev/null +++ b/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml @@ -0,0 +1,4 @@ +# In the following configuration, "recommendation" should be "reason" or "replacement". +disallowed-macros = [ + { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, +] diff --git a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs new file mode 100644 index 00000000000..9c770c31f6f --- /dev/null +++ b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs @@ -0,0 +1,5 @@ +#[rustfmt::skip] +//@error-in-other-file: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum +fn main() { + panic!(); +} diff --git a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr new file mode 100644 index 00000000000..b564709721d --- /dev/null +++ b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum + --> $DIR/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml:3:5 + | +LL | { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f2eaa66a4ae..6ee77ebd8ec 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -5,6 +5,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -57,6 +58,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings @@ -97,6 +99,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -149,6 +152,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings @@ -189,6 +193,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -241,6 +246,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings diff --git a/tests/ui-toml/toml_unloaded_crate/clippy.toml b/tests/ui-toml/toml_unloaded_crate/clippy.toml new file mode 100644 index 00000000000..e664256d2a2 --- /dev/null +++ b/tests/ui-toml/toml_unloaded_crate/clippy.toml @@ -0,0 +1,10 @@ +# The first two `disallowed-methods` paths should generate warnings, but the third should not. + +[[disallowed-methods]] +path = "regex::Regex::new_" + +[[disallowed-methods]] +path = "regex::Regex_::new" + +[[disallowed-methods]] +path = "regex_::Regex::new" diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs new file mode 100644 index 00000000000..14f15e73311 --- /dev/null +++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs @@ -0,0 +1,6 @@ +//@error-in-other-file: `regex::Regex::new_` does not refer to a reachable function +//@error-in-other-file: `regex::Regex_::new` does not refer to a reachable function + +extern crate regex; + +fn main() {} diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr new file mode 100644 index 00000000000..e5fd548b26d --- /dev/null +++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr @@ -0,0 +1,20 @@ +warning: `regex::Regex::new_` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:3:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex::new_" + | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: `regex::Regex_::new` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:6:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex_::new" + | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: 2 warnings emitted + diff --git a/tests/ui-toml/type_repetition_in_bounds/main.rs b/tests/ui-toml/type_repetition_in_bounds/main.rs index 7f93d2071c9..b60cb7632e2 100644 --- a/tests/ui-toml/type_repetition_in_bounds/main.rs +++ b/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -12,7 +12,7 @@ fn f2<T>() where T: Copy + Clone + Sync + Send + ?Sized, T: Unpin + PartialEq, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } diff --git a/tests/ui-toml/type_repetition_in_bounds/main.stderr b/tests/ui-toml/type_repetition_in_bounds/main.stderr index c5102c39d1c..ba0f41167a0 100644 --- a/tests/ui-toml/type_repetition_in_bounds/main.stderr +++ b/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui-toml/type_repetition_in_bounds/main.rs:14:5 | LL | T: Unpin + PartialEq, diff --git a/tests/ui/asm_syntax_not_x86.rs b/tests/ui/asm_syntax_not_x86.rs index edcd5247f18..361bc203393 100644 --- a/tests/ui/asm_syntax_not_x86.rs +++ b/tests/ui/asm_syntax_not_x86.rs @@ -8,9 +8,11 @@ mod dont_warn { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - asm!("", options()); - asm!("", options(nostack)); + unsafe { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + } } global_asm!(""); diff --git a/tests/ui/asm_syntax_x86.rs b/tests/ui/asm_syntax_x86.rs index 4e91f27cd31..30401c9a044 100644 --- a/tests/ui/asm_syntax_x86.rs +++ b/tests/ui/asm_syntax_x86.rs @@ -5,17 +5,19 @@ mod warn_intel { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - //~^ inline_asm_x86_intel_syntax + unsafe { + asm!(""); + //~^ inline_asm_x86_intel_syntax - asm!("", options()); - //~^ inline_asm_x86_intel_syntax + asm!("", options()); + //~^ inline_asm_x86_intel_syntax - asm!("", options(nostack)); - //~^ inline_asm_x86_intel_syntax + asm!("", options(nostack)); + //~^ inline_asm_x86_intel_syntax - asm!("", options(att_syntax)); - asm!("", options(nostack, att_syntax)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } } global_asm!(""); @@ -32,14 +34,16 @@ mod warn_att { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - asm!("", options()); - asm!("", options(nostack)); - asm!("", options(att_syntax)); - //~^ inline_asm_x86_att_syntax - - asm!("", options(nostack, att_syntax)); - //~^ inline_asm_x86_att_syntax + unsafe { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + //~^ inline_asm_x86_att_syntax + + asm!("", options(nostack, att_syntax)); + //~^ inline_asm_x86_att_syntax + } } global_asm!(""); diff --git a/tests/ui/asm_syntax_x86.stderr b/tests/ui/asm_syntax_x86.stderr index 2dcd955f034..8e068cf2349 100644 --- a/tests/ui/asm_syntax_x86.stderr +++ b/tests/ui/asm_syntax_x86.stderr @@ -1,31 +1,31 @@ error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:8:9 + --> tests/ui/asm_syntax_x86.rs:9:13 | -LL | asm!(""); - | ^^^^^^^^ +LL | asm!(""); + | ^^^^^^^^ | = help: use AT&T x86 assembly syntax = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_intel_syntax)]` error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:11:9 + --> tests/ui/asm_syntax_x86.rs:12:13 | -LL | asm!("", options()); - | ^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:14:9 + --> tests/ui/asm_syntax_x86.rs:15:13 | -LL | asm!("", options(nostack)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:21:5 + --> tests/ui/asm_syntax_x86.rs:23:5 | LL | global_asm!(""); | ^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | global_asm!(""); = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:24:5 + --> tests/ui/asm_syntax_x86.rs:26:5 | LL | global_asm!("", options()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,25 +41,25 @@ LL | global_asm!("", options()); = help: use AT&T x86 assembly syntax error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:38:9 + --> tests/ui/asm_syntax_x86.rs:41:13 | -LL | asm!("", options(att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_att_syntax)]` error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:41:9 + --> tests/ui/asm_syntax_x86.rs:44:13 | -LL | asm!("", options(nostack, att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:47:5 + --> tests/ui/asm_syntax_x86.rs:51:5 | LL | global_asm!("", options(att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout index eed704e82fe..88a27530238 100644 --- a/tests/ui/author.stdout +++ b/tests/ui/author.stdout @@ -1,8 +1,6 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Cast(expr, cast_ty) = init.kind - && let TyKind::Path(ref qpath) = cast_ty.kind - && match_qpath(qpath, &["char"]) && let ExprKind::Lit(ref lit) = expr.kind && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index 54325f9776c..e453299edbc 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -14,8 +14,6 @@ if let ExprKind::Block(block, None) = expr.kind && name1.as_str() == "_t" && let StmtKind::Semi(e) = block.stmts[2].kind && let ExprKind::Unary(UnOp::Neg, inner) = e.kind - && let ExprKind::Path(ref qpath) = inner.kind - && match_qpath(qpath, &["x"]) && block.expr.is_none() { // report your lint here @@ -25,18 +23,14 @@ if let ExprKind::Block(block, None) = expr.kind && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["String", "new"]) + && is_path_diagnostic_item(cx, func, sym::string_new) && args.is_empty() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "expr" && let Some(trailing_expr) = block.expr && let ExprKind::Call(func1, args1) = trailing_expr.kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["drop"]) + && is_path_diagnostic_item(cx, func1, sym::mem_drop) && args1.len() == 1 - && let ExprKind::Path(ref qpath2) = args1[0].kind - && match_qpath(qpath2, &["expr"]) { // report your lint here } diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 59d4da490fe..2b179d45112 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,8 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && is_path_diagnostic_item(cx, func, sym::cmp_min) && args.len() == 2 && let ExprKind::Lit(ref lit) = args[0].kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 8ffdf886202..da359866bff 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -31,10 +31,8 @@ if let StmtKind::Let(local) = stmt.kind if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind && let PatKind::Expr(lit_expr) = let_expr.pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.init.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = then.kind && block.stmts.is_empty() && block.expr.is_none() diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index a5a8c0304ee..f02ea5bf075 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,11 +1,8 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["std", "mem", "transmute"]) + && is_path_diagnostic_item(cx, func, sym::transmute) && args.len() == 1 - && let ExprKind::Path(ref qpath1) = args[0].kind - && match_qpath(qpath1, &["ZPTR"]) && let PatKind::Wild = local.pat.kind { // report your lint here diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index c94eb171f52..79794cec926 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -14,8 +14,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block.stmts.len() == 1 && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init - && let ExprKind::Path(ref qpath1) = init.kind - && match_qpath(qpath1, &["y"]) && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind && name1.as_str() == "z" && block.expr.is_none() @@ -64,8 +62,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo // report your lint here } if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) - && let ExprKind::Path(ref qpath) = condition.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = body.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind @@ -77,10 +73,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) && let PatKind::Expr(lit_expr) = let_pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = if_then.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind diff --git a/tests/ui/author/macro_in_closure.stdout b/tests/ui/author/macro_in_closure.stdout index 3186d0cbc27..5f8a4ce2363 100644 --- a/tests/ui/author/macro_in_closure.stdout +++ b/tests/ui/author/macro_in_closure.stdout @@ -7,12 +7,10 @@ if let StmtKind::Let(local) = stmt.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind && let ExprKind::Call(func, args) = e.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -27,12 +25,9 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath2) = func2.kind - && match_qpath(qpath2, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath3) = inner2.kind - && match_qpath(qpath3, &["x"]) && block.expr.is_none() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "print_text" diff --git a/tests/ui/author/macro_in_loop.stdout b/tests/ui/author/macro_in_loop.stdout index 3f9be297c33..ecc25254311 100644 --- a/tests/ui/author/macro_in_loop.stdout +++ b/tests/ui/author/macro_in_loop.stdout @@ -17,12 +17,10 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block1.stmts.len() == 1 && let StmtKind::Semi(e1) = block1.stmts[0].kind && let ExprKind::Call(func, args) = e1.kind - && let ExprKind::Path(ref qpath1) = func.kind - && match_qpath(qpath1, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath2) = func1.kind - && match_qpath(qpath2, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -37,12 +35,9 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath3) = func2.kind - && match_qpath(qpath3, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath4) = inner2.kind - && match_qpath(qpath4, &["i"]) && block1.expr.is_none() && block.expr.is_none() { diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index acb3b140dfa..9752d7a9f99 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 && let PatKind::Expr(lit_expr) = arms[0].pat.kind - && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node && let PatKind::Expr(lit_expr1) = arms[1].pat.kind - && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind + && let PatExprKind::Lit { ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind @@ -23,8 +23,6 @@ if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind && name.as_str() == "x" && let Some(trailing_expr) = block.expr - && let ExprKind::Path(ref qpath) = trailing_expr.kind - && match_qpath(qpath, &["x"]) && let PatKind::Wild = arms[2].pat.kind && arms[2].guard.is_none() && let ExprKind::Lit(ref lit5) = arms[2].body.kind diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index b66bbccb3cf..1e8fbafd30c 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -1,5 +1,4 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind @@ -20,11 +19,10 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind // report your lint here } if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let PatKind::Expr(lit_expr) = fields[0].pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -34,10 +32,9 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind // report your lint here } if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind - && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 && let PatKind::Expr(lit_expr) = fields[0].kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -48,8 +45,6 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind } if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind && method_name.ident.as_str() == "test" - && let ExprKind::Path(ref qpath) = receiver.kind - && match_qpath(qpath, &["test_method_call"]) && args.is_empty() { // report your lint here diff --git a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/tests/ui/auxiliary/interior_mutable_const.rs index 96e037d4fcd..96e037d4fcd 100644 --- a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs +++ b/tests/ui/auxiliary/interior_mutable_const.rs diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index e72d6b6cead..4c61c5accd3 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -51,14 +51,14 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { let arg = sig.inputs.first_mut()?; - if let FnArg::Typed(PatType { pat, .. }) = arg { - if let Pat::Ident(PatIdent { ident, .. }) = &**pat { - if ident == "self" { - return Some(arg); - } - } + if let FnArg::Typed(PatType { pat, .. }) = arg + && let Pat::Ident(PatIdent { ident, .. }) = &**pat + && ident == "self" + { + Some(arg) + } else { + None } - None } let mut elided = 0; @@ -66,30 +66,29 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea // Look for methods having arbitrary self type taken by &mut ref for inner in &mut item.items { - if let ImplItem::Fn(method) = inner { - if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { - if let box Type::Reference(reference) = &mut pat_type.ty { - // Target only unnamed lifetimes - let name = match &reference.lifetime { - Some(lt) if lt.ident == "_" => make_name(elided), - None => make_name(elided), - _ => continue, - }; - elided += 1; - - // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. - // In order to avoid adding the dependency, get a default span from a nonexistent token. - // A default span is needed to mark the code as coming from expansion. - let span = Star::default().span(); - - // Replace old lifetime with the named one - let lifetime = Lifetime::new(&name, span); - reference.lifetime = Some(parse_quote!(#lifetime)); - - // Add lifetime to the generics of the method - method.sig.generics.params.push(parse_quote!(#lifetime)); - } - } + if let ImplItem::Fn(method) = inner + && let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) + && let box Type::Reference(reference) = &mut pat_type.ty + { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; + + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a nonexistent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); + + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); + + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); } } @@ -129,15 +128,15 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream let mut async_fn = parse_macro_input!(input as syn::ItemFn); for stmt in &mut async_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt { - if let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() { - let blc = quote_spanned!( await_token.span => { - #[allow(clippy::let_and_return)] - let __pinned = #base; - __pinned - }); - *scrutinee = parse_quote!(#blc); - } + if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt + && let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() + { + let blc = quote_spanned!( await_token.span => { + #[allow(clippy::let_and_return)] + let __pinned = #base; + __pinned + }); + *scrutinee = parse_quote!(#blc); } } diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index 7a4cc4fa9ee..bb55539617f 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![feature(proc_macro_span)] #![allow(clippy::needless_if, dead_code)] diff --git a/tests/ui/blocks_in_conditions.fixed b/tests/ui/blocks_in_conditions.fixed index cd307e803d0..6ae5b0cb2f0 100644 --- a/tests/ui/blocks_in_conditions.fixed +++ b/tests/ui/blocks_in_conditions.fixed @@ -3,7 +3,7 @@ #![warn(clippy::blocks_in_conditions)] #![allow( unused, - clippy::let_and_return, + unnecessary_transmutes, clippy::needless_if, clippy::missing_transmute_annotations )] @@ -71,28 +71,6 @@ fn block_in_assert() { ); } -// issue #11814 -fn block_in_match_expr(num: i32) -> i32 { - let res = { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - let opt = Some(2); - opt - }; match res { - Some(0) => 1, - Some(n) => num * 2, - None => 0, - }; - - match unsafe { - let hearty_hearty_hearty = vec![240, 159, 146, 150]; - String::from_utf8_unchecked(hearty_hearty_hearty).as_str() - } { - "💖" => 1, - "what" => 2, - _ => 3, - } -} - // issue #12162 macro_rules! timed { ($name:expr, $body:expr $(,)?) => {{ diff --git a/tests/ui/blocks_in_conditions.rs b/tests/ui/blocks_in_conditions.rs index 6a211c8edfd..3fd06062072 100644 --- a/tests/ui/blocks_in_conditions.rs +++ b/tests/ui/blocks_in_conditions.rs @@ -3,7 +3,7 @@ #![warn(clippy::blocks_in_conditions)] #![allow( unused, - clippy::let_and_return, + unnecessary_transmutes, clippy::needless_if, clippy::missing_transmute_annotations )] @@ -71,28 +71,6 @@ fn block_in_assert() { ); } -// issue #11814 -fn block_in_match_expr(num: i32) -> i32 { - match { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - let opt = Some(2); - opt - } { - Some(0) => 1, - Some(n) => num * 2, - None => 0, - }; - - match unsafe { - let hearty_hearty_hearty = vec![240, 159, 146, 150]; - String::from_utf8_unchecked(hearty_hearty_hearty).as_str() - } { - "💖" => 1, - "what" => 2, - _ => 3, - } -} - // issue #12162 macro_rules! timed { ($name:expr, $body:expr $(,)?) => {{ diff --git a/tests/ui/blocks_in_conditions.stderr b/tests/ui/blocks_in_conditions.stderr index da21344a842..282c42a98bf 100644 --- a/tests/ui/blocks_in_conditions.stderr +++ b/tests/ui/blocks_in_conditions.stderr @@ -34,24 +34,5 @@ LL | if true && x == 3 { 6 } else { 10 } = note: `-D clippy::nonminimal-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` -error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:76:5 - | -LL | / match { -LL | | -LL | | let opt = Some(2); -LL | | opt -LL | | } { - | |_____^ - | -help: try - | -LL ~ let res = { -LL + -LL + let opt = Some(2); -LL + opt -LL ~ }; match res { - | - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/blocks_in_conditions_2021.fixed b/tests/ui/blocks_in_conditions_2021.fixed new file mode 100644 index 00000000000..c7cc643dba6 --- /dev/null +++ b/tests/ui/blocks_in_conditions_2021.fixed @@ -0,0 +1,25 @@ +//@edition: 2021 + +#![allow(clippy::let_and_return)] + +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + let res = { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + }; match res { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} diff --git a/tests/ui/blocks_in_conditions_2021.rs b/tests/ui/blocks_in_conditions_2021.rs new file mode 100644 index 00000000000..a911237f5f7 --- /dev/null +++ b/tests/ui/blocks_in_conditions_2021.rs @@ -0,0 +1,25 @@ +//@edition: 2021 + +#![allow(clippy::let_and_return)] + +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + match { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + } { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} diff --git a/tests/ui/blocks_in_conditions_2021.stderr b/tests/ui/blocks_in_conditions_2021.stderr new file mode 100644 index 00000000000..497ee9d679d --- /dev/null +++ b/tests/ui/blocks_in_conditions_2021.stderr @@ -0,0 +1,23 @@ +error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> tests/ui/blocks_in_conditions_2021.rs:7:5 + | +LL | / match { +LL | | +LL | | let opt = Some(2); +LL | | opt +LL | | } { + | |_____^ + | + = note: `-D clippy::blocks-in-conditions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]` +help: try + | +LL ~ let res = { +LL + +LL + let opt = Some(2); +LL + opt +LL ~ }; match res { + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/bool_to_int_with_if.fixed b/tests/ui/bool_to_int_with_if.fixed index 0080801d46b..7fa7c016f93 100644 --- a/tests/ui/bool_to_int_with_if.fixed +++ b/tests/ui/bool_to_int_with_if.fixed @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -117,3 +116,27 @@ fn if_let(a: Enum, b: Enum) { 0 }; } + +fn issue14628() { + macro_rules! mac { + (if $cond:expr, $then:expr, $else:expr) => { + if $cond { $then } else { $else } + }; + (zero) => { + 0 + }; + (one) => { + 1 + }; + } + + let _ = i32::from(dbg!(4 > 0)); + //~^ bool_to_int_with_if + + let _ = dbg!(i32::from(4 > 0)); + //~^ bool_to_int_with_if + + let _ = mac!(if 4 > 0, 1, 0); + let _ = if 4 > 0 { mac!(one) } else { 0 }; + let _ = if 4 > 0 { 1 } else { mac!(zero) }; +} diff --git a/tests/ui/bool_to_int_with_if.rs b/tests/ui/bool_to_int_with_if.rs index 72c7e2c71c5..2295d6f1362 100644 --- a/tests/ui/bool_to_int_with_if.rs +++ b/tests/ui/bool_to_int_with_if.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -157,3 +156,27 @@ fn if_let(a: Enum, b: Enum) { 0 }; } + +fn issue14628() { + macro_rules! mac { + (if $cond:expr, $then:expr, $else:expr) => { + if $cond { $then } else { $else } + }; + (zero) => { + 0 + }; + (one) => { + 1 + }; + } + + let _ = if dbg!(4 > 0) { 1 } else { 0 }; + //~^ bool_to_int_with_if + + let _ = dbg!(if 4 > 0 { 1 } else { 0 }); + //~^ bool_to_int_with_if + + let _ = mac!(if 4 > 0, 1, 0); + let _ = if 4 > 0 { mac!(one) } else { 0 }; + let _ = if 4 > 0 { 1 } else { mac!(zero) }; +} diff --git a/tests/ui/bool_to_int_with_if.stderr b/tests/ui/bool_to_int_with_if.stderr index 415e80f8d73..e4ae5730414 100644 --- a/tests/ui/bool_to_int_with_if.stderr +++ b/tests/ui/bool_to_int_with_if.stderr @@ -1,5 +1,5 @@ error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:14:5 + --> tests/ui/bool_to_int_with_if.rs:13:5 | LL | / if a { LL | | @@ -14,7 +14,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::bool_to_int_with_if)]` error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:20:5 + --> tests/ui/bool_to_int_with_if.rs:19:5 | LL | / if a { LL | | @@ -27,7 +27,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:26:5 + --> tests/ui/bool_to_int_with_if.rs:25:5 | LL | / if !a { LL | | @@ -40,7 +40,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:32:5 + --> tests/ui/bool_to_int_with_if.rs:31:5 | LL | / if a || b { LL | | @@ -53,7 +53,7 @@ LL | | }; = note: `(a || b) as i32` or `(a || b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:38:5 + --> tests/ui/bool_to_int_with_if.rs:37:5 | LL | / if cond(a, b) { LL | | @@ -66,7 +66,7 @@ LL | | }; = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:44:5 + --> tests/ui/bool_to_int_with_if.rs:43:5 | LL | / if x + y < 4 { LL | | @@ -79,7 +79,7 @@ LL | | }; = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:54:12 + --> tests/ui/bool_to_int_with_if.rs:53:12 | LL | } else if b { | ____________^ @@ -93,7 +93,7 @@ LL | | }; = note: `b as i32` or `b.into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:64:12 + --> tests/ui/bool_to_int_with_if.rs:63:12 | LL | } else if b { | ____________^ @@ -107,12 +107,28 @@ LL | | }; = note: `!b as i32` or `(!b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:130:5 + --> tests/ui/bool_to_int_with_if.rs:129:5 | LL | if a { 1 } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` | = note: `a as u8` or `a.into()` can also be valid options -error: aborting due to 9 previous errors +error: boolean to int conversion using if + --> tests/ui/bool_to_int_with_if.rs:173:13 + | +LL | let _ = if dbg!(4 > 0) { 1 } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(dbg!(4 > 0))` + | + = note: `dbg!(4 > 0) as i32` or `dbg!(4 > 0).into()` can also be valid options + +error: boolean to int conversion using if + --> tests/ui/bool_to_int_with_if.rs:176:18 + | +LL | let _ = dbg!(if 4 > 0 { 1 } else { 0 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(4 > 0)` + | + = note: `(4 > 0) as i32` or `(4 > 0).into()` can also be valid options + +error: aborting due to 11 previous errors diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed index 3dca06fce4b..3ba2eea59f0 100644 --- a/tests/ui/borrow_as_ptr.fixed +++ b/tests/ui/borrow_as_ptr.fixed @@ -29,3 +29,21 @@ fn issue_13882() { let _raw = (&raw mut x[1]).wrapping_offset(-1); //~^ borrow_as_ptr } + +fn implicit_cast() { + let val = 1; + let p: *const i32 = &raw const val; + //~^ borrow_as_ptr + + let mut val = 1; + let p: *mut i32 = &raw mut val; + //~^ borrow_as_ptr + + let mut val = 1; + // Only lint the leftmost argument, the rightmost is ref to a temporary + core::ptr::eq(&raw const val, &1); + //~^ borrow_as_ptr + + // Do not lint references to temporaries + core::ptr::eq(&0i32, &1i32); +} diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs index 3559dc23d01..8cdd0512da5 100644 --- a/tests/ui/borrow_as_ptr.rs +++ b/tests/ui/borrow_as_ptr.rs @@ -29,3 +29,21 @@ fn issue_13882() { let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); //~^ borrow_as_ptr } + +fn implicit_cast() { + let val = 1; + let p: *const i32 = &val; + //~^ borrow_as_ptr + + let mut val = 1; + let p: *mut i32 = &mut val; + //~^ borrow_as_ptr + + let mut val = 1; + // Only lint the leftmost argument, the rightmost is ref to a temporary + core::ptr::eq(&val, &1); + //~^ borrow_as_ptr + + // Do not lint references to temporaries + core::ptr::eq(&0i32, &1i32); +} diff --git a/tests/ui/borrow_as_ptr.stderr b/tests/ui/borrow_as_ptr.stderr index 4a9f2ed4aa0..b1fcce49403 100644 --- a/tests/ui/borrow_as_ptr.stderr +++ b/tests/ui/borrow_as_ptr.stderr @@ -25,5 +25,38 @@ error: borrow as raw pointer LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]` -error: aborting due to 4 previous errors +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:35:25 + | +LL | let p: *const i32 = &val; + | ^^^^ + | +help: use a raw pointer instead + | +LL | let p: *const i32 = &raw const val; + | +++++++++ + +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:39:23 + | +LL | let p: *mut i32 = &mut val; + | ^^^^^^^^ + | +help: use a raw pointer instead + | +LL | let p: *mut i32 = &raw mut val; + | +++ + +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:44:19 + | +LL | core::ptr::eq(&val, &1); + | ^^^^ + | +help: use a raw pointer instead + | +LL | core::ptr::eq(&raw const val, &1); + | +++++++++ + +error: aborting due to 7 previous errors diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs new file mode 100644 index 00000000000..0f439f78915 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -0,0 +1,221 @@ +//@aux-build:interior_mutable_const.rs + +#![deny(clippy::borrow_interior_mutable_const)] +#![allow( + clippy::declare_interior_mutable_const, + clippy::out_of_bounds_indexing, + const_item_mutation, + unconditional_panic +)] + +use core::cell::{Cell, UnsafeCell}; +use core::ops::{Deref, Index}; + +trait ConstDefault { + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl<T: ConstDefault> ConstDefault for Cell<T> { + const DEFAULT: Self = Cell::new(T::DEFAULT); +} + +fn main() { + { + const C: String = String::new(); + let _ = C; + let _ = &C; + let _ = C.len(); + let _ = &*C; + } + { + const C: UnsafeCell<u32> = UnsafeCell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + let _ = C.get(); //~ borrow_interior_mutable_const + } + { + const C: Cell<u32> = Cell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &mut C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + + let local = C; + C.swap(&local) //~ borrow_interior_mutable_const + } + { + const C: [(Cell<u32>,); 1] = [(Cell::new(0),)]; + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C[0].0; //~ borrow_interior_mutable_const + C[0].0.set(1); //~ borrow_interior_mutable_const + } + { + struct S(Cell<u32>); + impl S { + const C: Self = Self(Cell::new(0)); + } + impl Deref for S { + type Target = Cell<u32>; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + let _ = S::C; + let _ = S::C.0; + let _ = &S::C; //~ borrow_interior_mutable_const + let _ = &S::C.0; //~ borrow_interior_mutable_const + S::C.set(1); //~ borrow_interior_mutable_const + let _ = &*S::C; //~ borrow_interior_mutable_const + (*S::C).set(1); //~ borrow_interior_mutable_const + } + { + enum E { + Cell(Cell<u32>), + Other, + } + const CELL: E = E::Cell(Cell::new(0)); + const OTHER: E = E::Other; + + let _ = CELL; + let _ = &CELL; //~ borrow_interior_mutable_const + let E::Cell(_) = CELL else { + return; + }; + + let _ = OTHER; + let _ = &OTHER; + let E::Cell(ref _x) = OTHER else { + return; + }; + } + { + struct S<T> { + cell: (Cell<T>, u32), + other: Option<T>, + } + impl<T: ConstDefault + Copy> S<T> { + const C: Self = Self { + cell: (Cell::<T>::DEFAULT, 0), + other: Some(T::DEFAULT), + }; + + fn f() { + let _ = Self::C; + let _ = &Self::C; //~ borrow_interior_mutable_const + let _ = Self::C.other; + let _ = &Self::C.other; + let _ = &Self::C.cell; //~ borrow_interior_mutable_const + let _ = &Self::C.cell.0; //~ borrow_interior_mutable_const + Self::C.cell.0.set(T::DEFAULT); //~ borrow_interior_mutable_const + let _ = &Self::C.cell.1; + } + } + } + { + trait T { + const VALUE: Option<Cell<u32>> = Some(Cell::new(0)); + } + impl T for u32 {} + impl T for i32 { + const VALUE: Option<Cell<u32>> = None; + } + + let _ = &u32::VALUE; //~ borrow_interior_mutable_const + let _ = &i32::VALUE; + } + { + trait Trait<T: ConstDefault> { + type T<U: ConstDefault>: ConstDefault; + const VALUE: Option<Self::T<T>> = Some(Self::T::<T>::DEFAULT); + } + impl<T: ConstDefault> Trait<T> for u32 { + type T<U: ConstDefault> = Cell<U>; + } + impl<T: ConstDefault> Trait<T> for i32 { + type T<U: ConstDefault> = Cell<U>; + const VALUE: Option<Cell<T>> = None; + } + + fn f<T: ConstDefault>() { + let _ = &<u32 as Trait<T>>::VALUE; //~ borrow_interior_mutable_const + let _ = &<i32 as Trait<T>>::VALUE; + } + } + { + trait Trait { + const UNFROZEN: Option<Cell<u32>> = Some(Cell::new(0)); + const FROZEN: Option<Cell<u32>> = None; + const NON_FREEZE: u32 = 0; + } + fn f<T: Trait>() { + // None of these are guaranteed to be frozen, so don't lint. + let _ = &T::UNFROZEN; + let _ = &T::FROZEN; + let _ = &T::NON_FREEZE; + } + } + { + struct S([Option<Cell<u32>>; 2]); + impl Index<usize> for S { + type Output = Option<Cell<u32>>; + fn index(&self, idx: usize) -> &Self::Output { + &self.0[idx] + } + } + + const C: S = S([Some(Cell::new(0)), None]); + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C.0[0]; //~ borrow_interior_mutable_const + let _ = &C.0[1]; + } + { + const C: [Option<Cell<u32>>; 2] = [None, None]; + let _ = &C[0]; + let _ = &C[1]; + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; + } + } + { + const C: [Option<Cell<u32>>; 2] = [None, Some(Cell::new(0))]; + let _ = &C[0]; + let _ = &C[1]; //~ borrow_interior_mutable_const + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; //~ borrow_interior_mutable_const + } + } + { + let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const + let _ = &interior_mutable_const::WRAPPED_PRIVATE_FROZEN_VARIANT; + } + { + type Cell2<T> = Cell<T>; + type MyCell = Cell2<u32>; + struct S(Option<MyCell>); + trait T { + type Assoc; + } + struct S2<T>(T, T, u32); + impl T for S { + type Assoc = S2<Self>; + } + type Assoc<X> = <X as T>::Assoc; + impl S { + const VALUE: Assoc<Self> = S2(Self(None), Self(Some(Cell::new(0))), 0); + } + let _ = &S::VALUE; //~ borrow_interior_mutable_const + let _ = &S::VALUE.0; + let _ = &S::VALUE.1; //~ borrow_interior_mutable_const + let _ = &S::VALUE.2; + } +} diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr new file mode 100644 index 00000000000..e7c3f879b05 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -0,0 +1,247 @@ +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:35:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing +note: the lint level is defined here + --> tests/ui/borrow_interior_mutable_const.rs:3:9 + | +LL | #![deny(clippy::borrow_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:37:17 + | +LL | let _ = C.get(); + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:42:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:43:17 + | +LL | let _ = &mut C; + | ^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:47:9 + | +LL | C.swap(&local) + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:52:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:53:17 + | +LL | let _ = &C[0]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:54:17 + | +LL | let _ = &C[0].0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:55:9 + | +LL | C[0].0.set(1); + | ^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:70:17 + | +LL | let _ = &S::C; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:71:17 + | +LL | let _ = &S::C.0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:72:9 + | +LL | S::C.set(1); + | ^^^^ + | + = note: there is a compiler inserted call to `Deref::deref` here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:73:18 + | +LL | let _ = &*S::C; + | ^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:74:9 + | +LL | (*S::C).set(1); + | ^^^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:85:17 + | +LL | let _ = &CELL; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:109:25 + | +LL | let _ = &Self::C; + | ^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:112:25 + | +LL | let _ = &Self::C.cell; + | ^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:113:25 + | +LL | let _ = &Self::C.cell.0; + | ^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:114:17 + | +LL | Self::C.cell.0.set(T::DEFAULT); + | ^^^^^^^^^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:128:17 + | +LL | let _ = &u32::VALUE; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:145:21 + | +LL | let _ = &<u32 as Trait<T>>::VALUE; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:172:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:173:18 + | +LL | let _ = &C[0]; + | ^^^^ + | + = note: this index expression is a call to `Index::index` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:174:17 + | +LL | let _ = &C.0[0]; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:190:17 + | +LL | let _ = &C[1]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:194:21 + | +LL | let _ = &C[i]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:198:17 + | +LL | let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:216:17 + | +LL | let _ = &S::VALUE; + | ^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:218:17 + | +LL | let _ = &S::VALUE.1; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: aborting due to 29 previous errors + diff --git a/tests/ui/borrow_interior_mutable_const/enums.rs b/tests/ui/borrow_interior_mutable_const/enums.rs deleted file mode 100644 index da940a4cfb5..00000000000 --- a/tests/ui/borrow_interior_mutable_const/enums.rs +++ /dev/null @@ -1,101 +0,0 @@ -//@aux-build:helper.rs - -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions. - -extern crate helper; - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell<bool>), - Frozen, -} - -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -fn borrow_optional_cell() { - let _ = &UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &FROZEN_VARIANT; -} - -trait AssocConsts { - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - const TO_BE_FROZEN_VARIANT: OptionalCell; - - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - // This is the "suboptimal behavior" mentioned in `is_value_unfrozen` - // caused by a similar reason to unfrozen types without any default values - // get linted even if it has frozen variants'. - let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR: interior mutability - - // The lint ignores default values because an impl of this trait can set - // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`. - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR: interior mutability - } -} - -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &<Self as AssocConsts>::TO_BE_FROZEN_VARIANT; - let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - } -} - -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>; - const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>; - - // there's no need to test here because it's the exactly same as `trait::AssocTypes` - fn function(); -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); - const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None; - - fn function() { - let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &<Self as AssocTypes>::TO_BE_FROZEN_VARIANT; - } -} - -enum BothOfCellAndGeneric<T> { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl<T> BothOfCellAndGeneric<T> { - const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); - const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5); - - fn function() { - let _ = &Self::UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::GENERIC_VARIANT; //~ ERROR: interior mutability - let _ = &Self::FROZEN_VARIANT; - } -} - -fn main() { - // constants defined in foreign crates - let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; -} diff --git a/tests/ui/borrow_interior_mutable_const/enums.stderr b/tests/ui/borrow_interior_mutable_const/enums.stderr deleted file mode 100644 index 43850384b90..00000000000 --- a/tests/ui/borrow_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,79 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:22:14 - | -LL | let _ = &UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/enums.rs:3:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:37:18 - | -LL | let _ = &Self::TO_BE_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:41:18 - | -LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:50:18 - | -LL | let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:52:18 - | -LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:74:18 - | -LL | let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:91:18 - | -LL | let _ = &Self::UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:92:18 - | -LL | let _ = &Self::GENERIC_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:99:14 - | -LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 9 previous errors - diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs deleted file mode 100644 index fa729b62d7f..00000000000 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ /dev/null @@ -1,128 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)] -#![allow(const_item_mutation)] - -use std::borrow::Cow; -use std::cell::{Cell, UnsafeCell}; -use std::fmt::Display; -use std::sync::Once; -use std::sync::atomic::{AtomicUsize, Ordering}; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -const CELL: Cell<usize> = Cell::new(6); -const ATOMIC_TUPLE: ([AtomicUsize; 1], Option<Box<AtomicUsize>>, u8) = ([ATOMIC], None, 7); -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow<str> = Cow::Borrowed("abcdef"); -const NO_ANN: &dyn Display = &70; -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -const ONCE_INIT: Once = Once::new(); - -// This is just a pointer that can be safely dereferenced, -// it's semantically the same as `&'static T`; -// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. -// For more information, please see the issue #5918. -pub struct StaticRef<T> { - ptr: *const T, -} - -impl<T> StaticRef<T> { - /// Create a new `StaticRef` from a raw pointer - /// - /// ## Safety - /// - /// Callers must pass in a reference to statically allocated memory which - /// does not overlap with other values. - pub const unsafe fn new(ptr: *const T) -> StaticRef<T> { - StaticRef { ptr } - } -} - -impl<T> std::ops::Deref for StaticRef<T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.ptr } - } -} - -// ICE regression test -mod issue12979 { - use std::cell::UnsafeCell; - - const ATOMIC_TUPLE: (Vec<UnsafeCell<u8>>, ()) = (Vec::new(), ()); - - fn main() { - let _x = &ATOMIC_TUPLE.0; - } -} - -// use a tuple to make sure referencing a field behind a pointer isn't linted. -const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) }; - -fn main() { - ATOMIC.store(1, Ordering::SeqCst); - //~^ borrow_interior_mutable_const - assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - //~^ borrow_interior_mutable_const - - let _once = ONCE_INIT; - let _once_ref = &ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_2 = &&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_4 = &&&&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_mut = &mut ONCE_INIT; - //~^ borrow_interior_mutable_const - let _atomic_into_inner = ATOMIC.into_inner(); - // these should be all fine. - let _twice = (ONCE_INIT, ONCE_INIT); - let _ref_twice = &(ONCE_INIT, ONCE_INIT); - let _ref_once = &(ONCE_INIT, ONCE_INIT).0; - let _array_twice = [ONCE_INIT, ONCE_INIT]; - let _ref_array_twice = &[ONCE_INIT, ONCE_INIT]; - let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0]; - - // referencing projection is still bad. - let _ = &ATOMIC_TUPLE; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0; - //~^ borrow_interior_mutable_const - let _ = &(&&&&ATOMIC_TUPLE).0; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.2; - let _ = (&&&&ATOMIC_TUPLE).0; - let _ = (&&&&ATOMIC_TUPLE).2; - let _ = ATOMIC_TUPLE.0; - let _ = ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.1.into_iter(); - let _ = ATOMIC_TUPLE.2; - let _ = &{ ATOMIC_TUPLE }; - - CELL.set(2); - //~^ borrow_interior_mutable_const - assert_eq!(CELL.get(), 6); - //~^ borrow_interior_mutable_const - - assert_eq!(INTEGER, 8); - assert!(STRING.is_empty()); - - let a = ATOMIC; - a.store(4, Ordering::SeqCst); - assert_eq!(a.load(Ordering::SeqCst), 4); - - STATIC_TUPLE.0.store(3, Ordering::SeqCst); - assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); - assert!(STATIC_TUPLE.1.is_empty()); - - assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. - - let _ = &CELL_REF.0; -} diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr deleted file mode 100644 index decea153f71..00000000000 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ /dev/null @@ -1,119 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 - | -LL | ATOMIC.store(1, Ordering::SeqCst); - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/others.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:67:16 - | -LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:71:22 - | -LL | let _once_ref = &ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:73:25 - | -LL | let _once_ref_2 = &&ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:75:27 - | -LL | let _once_ref_4 = &&&&ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:77:26 - | -LL | let _once_mut = &mut ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:89:14 - | -LL | let _ = &ATOMIC_TUPLE; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:91:14 - | -LL | let _ = &ATOMIC_TUPLE.0; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:93:19 - | -LL | let _ = &(&&&&ATOMIC_TUPLE).0; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:95:14 - | -LL | let _ = &ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:97:13 - | -LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:103:13 - | -LL | let _ = ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:109:5 - | -LL | CELL.set(2); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:111:16 - | -LL | assert_eq!(CELL.get(), 6); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 14 previous errors - diff --git a/tests/ui/borrow_interior_mutable_const/projections.rs b/tests/ui/borrow_interior_mutable_const/projections.rs deleted file mode 100644 index bbe5538fbe1..00000000000 --- a/tests/ui/borrow_interior_mutable_const/projections.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![deny(clippy::declare_interior_mutable_const)] - -// Inspired by https://github.com/rust-lang/rust/pull/130543#issuecomment-2364828139 - -use std::cell::UnsafeCell; - -trait Trait { - type Assoc; -} - -type Assoc<T> = <T as Trait>::Assoc; - -impl Trait for u8 { - type Assoc = UnsafeCell<u8>; -} - -impl Trait for () { - type Assoc = (); -} - -enum MaybeMutable { - Mutable(Assoc<u8>), - Immutable(Assoc<()>), -} - -const CELL: Assoc<u8> = UnsafeCell::new(0); //~ ERROR: interior mutable -const UNIT: Assoc<()> = (); -const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); //~ ERROR: interior mutable -const IMMUTABLE: MaybeMutable = MaybeMutable::Immutable(UNIT); - -fn print_ref<T>(t: &T) { - let p: *const T = t; - println!("{p:p}") -} - -fn main() { - print_ref(&CELL); //~ ERROR: interior mutability - print_ref(&UNIT); - print_ref(&MUTABLE); //~ ERROR: interior mutability - print_ref(&IMMUTABLE); -} diff --git a/tests/ui/borrow_interior_mutable_const/projections.stderr b/tests/ui/borrow_interior_mutable_const/projections.stderr deleted file mode 100644 index eabaf66560a..00000000000 --- a/tests/ui/borrow_interior_mutable_const/projections.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:27:1 - | -LL | const CELL: Assoc<u8> = UnsafeCell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:2:9 - | -LL | #![deny(clippy::declare_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:29:1 - | -LL | const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:38:16 - | -LL | print_ref(&CELL); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:40:16 - | -LL | print_ref(&MUTABLE); - | ^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 4 previous errors - diff --git a/tests/ui/borrow_interior_mutable_const/traits.rs b/tests/ui/borrow_interior_mutable_const/traits.rs deleted file mode 100644 index c4878dbe57b..00000000000 --- a/tests/ui/borrow_interior_mutable_const/traits.rs +++ /dev/null @@ -1,219 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file replicates its `declare` counterpart. Please see it for more discussions. - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::{AtomicUsize, Ordering}; - -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - const STRING: String; - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const STRING: String = String::new(); - - fn function() { - // Lint this again since implementers can choose not to borrow it. - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -trait GenericTypes<T, U> { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - } -} - -impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for Vec<T> { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - let _ = &Self::TO_BE_CONCRETE; - //~^ borrow_interior_mutable_const - } -} - -// a helper type used below -pub struct Wrapper<T>(T); - -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>; - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>; - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - } -} - -impl<T: ConstDefault> AssocTypes for Vec<T> { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT); - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM; - } -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -trait AssocTypesFromGenericParam<T> -where - T: AssocTypesHelper<ToBeBounded = AtomicUsize>, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -impl<T> AssocTypesFromGenericParam<T> for Vec<T> -where - T: AssocTypesHelper<ToBeBounded = AtomicUsize>, -{ - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -trait SelfType: Sized { - const SELF: Self; - const WRAPPED_SELF: Option<Self>; - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option<Self> = Some(20); - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for AtomicUsize { - const SELF: Self = AtomicUsize::new(17); - const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); - - fn function() { - let _ = &Self::SELF; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_SELF; - //~^ borrow_interior_mutable_const - } -} - -trait BothOfCellAndGeneric<T> { - const DIRECT: Cell<T>; - const INDIRECT: Cell<*const T>; - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> { - const DIRECT: Cell<T> = Cell::new(T::DEFAULT); - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -struct Local<T>(T); - -impl<T> Local<T> -where - T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::COW; - let _ = &Self::GENERIC_TYPE; - let _ = &Self::ASSOC_TYPE; - let _ = &Self::BOUNDED_ASSOC_TYPE; - //~^ borrow_interior_mutable_const - } -} - -fn main() { - u64::ATOMIC.store(5, Ordering::SeqCst); - //~^ borrow_interior_mutable_const - assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - //~^ borrow_interior_mutable_const -} diff --git a/tests/ui/borrow_interior_mutable_const/traits.stderr b/tests/ui/borrow_interior_mutable_const/traits.stderr deleted file mode 100644 index cad68ca9260..00000000000 --- a/tests/ui/borrow_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,143 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:15:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/traits.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:27:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:53:18 - | -LL | let _ = &Self::TO_BE_CONCRETE; - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:89:18 - | -LL | let _ = &Self::TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:91:18 - | -LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:114:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:128:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:158:18 - | -LL | let _ = &Self::SELF; - | ^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:160:18 - | -LL | let _ = &Self::WRAPPED_SELF; - | ^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:170:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:172:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:182:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:184:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:204:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:209:18 - | -LL | let _ = &Self::BOUNDED_ASSOC_TYPE; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:215:5 - | -LL | u64::ATOMIC.store(5, Ordering::SeqCst); - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:217:16 - | -LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 17 previous errors - diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 4101897d380..ba0d36d85fe 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -188,6 +188,91 @@ fn issue11371() { } } +fn gen_option() -> Option<()> { + Some(()) + // Or None +} + +fn gen_result() -> Result<(), ()> { + Ok(()) + // Or Err(()) +} + +fn issue14725() { + let option = Some(()); + + if option.is_some() { + let _ = option.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _ = option.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + let _y = 1; + result.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _y = 1; + result.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let mut option = Some(()); + if option.is_some() { + option = gen_option(); + option.as_mut().unwrap(); + } else { + option = gen_option(); + option.as_mut().unwrap(); + } + + let mut result = Ok::<(), ()>(()); + if result.is_ok() { + result = gen_result(); + result.as_mut().unwrap(); + } else { + result = gen_result(); + result.as_mut().unwrap(); + } +} + +fn issue14763(x: Option<String>, r: Result<(), ()>) { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + }; + _ = || { + if r.is_ok() { + _ = r.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + _ = r.as_ref().unwrap(); + //~^ panicking_unwrap + } + }; +} + +const ISSUE14763: fn(Option<String>) = |x| { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + } +}; + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index c17eaef2326..a4bf0099244 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -236,7 +236,93 @@ LL | if result.is_ok() { LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: creating a shared reference to mutable static is discouraged +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:205:17 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some(<item>) = &option` +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:208:17 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:216:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok(<item>) = &result` +LL | let _y = 1; +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:220:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:246:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some(<item>) = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:249:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: called `unwrap` on `r` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:255:17 + | +LL | if r.is_ok() { + | ------------ help: try: `if let Ok(<item>) = &r` +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:258:17 + | +LL | if r.is_ok() { + | --------- because of this check +... +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:267:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some(<item>) = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:270:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | LL | if X.is_some() { @@ -244,8 +330,7 @@ LL | if X.is_some() { | = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = note: `-D static-mut-refs` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(static_mut_refs)]` + = note: `#[deny(static_mut_refs)]` on by default -error: aborting due to 26 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/cloned_ref_to_slice_refs.fixed b/tests/ui/cloned_ref_to_slice_refs.fixed new file mode 100644 index 00000000000..818c6e23259 --- /dev/null +++ b/tests/ui/cloned_ref_to_slice_refs.fixed @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = std::slice::from_ref(data_ref); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = std::slice::from_ref(&Data); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = std::slice::from_ref(&Point(0, 0)); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/tests/ui/cloned_ref_to_slice_refs.rs b/tests/ui/cloned_ref_to_slice_refs.rs new file mode 100644 index 00000000000..9517dbfd156 --- /dev/null +++ b/tests/ui/cloned_ref_to_slice_refs.rs @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = &[data_ref.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = &[Data.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = &[Point(0, 0).clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/tests/ui/cloned_ref_to_slice_refs.stderr b/tests/ui/cloned_ref_to_slice_refs.stderr new file mode 100644 index 00000000000..6a31d878239 --- /dev/null +++ b/tests/ui/cloned_ref_to_slice_refs.stderr @@ -0,0 +1,23 @@ +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:10:17 + | +LL | let _ = &[data_ref.clone()]; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(data_ref)` + | + = note: `-D clippy::cloned-ref-to-slice-refs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cloned_ref_to_slice_refs)]` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:14:17 + | +LL | let _ = &[Data.clone()]; + | ^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Data)` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:21:17 + | +LL | let _ = &[Point(0, 0).clone()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Point(0, 0))` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index e1ceb04f9cb..b553182a445 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -101,27 +101,8 @@ fn main() { } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 if matches!(true, true) && matches!(true, true) {} @@ -162,3 +143,14 @@ fn layout_check() -> u32 { ; 3 //~^^^^^ collapsible_if } + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; +} diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 0b996dca22e..f5998457ca6 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -108,27 +108,8 @@ fn main() { } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 if matches!(true, true) { if matches!(true, true) {} @@ -172,3 +153,14 @@ fn layout_check() -> u32 { }; 3 //~^^^^^ collapsible_if } + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; +} diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 53281146239..32c6b019403 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -127,7 +127,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:133:5 + --> tests/ui/collapsible_if.rs:114:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} @@ -141,7 +141,7 @@ LL ~ && matches!(true, true) {} | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:139:5 + --> tests/ui/collapsible_if.rs:120:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} @@ -155,7 +155,7 @@ LL ~ && matches!(true, true) {} | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:151:5 + --> tests/ui/collapsible_if.rs:132:5 | LL | / if true { LL | | if true { @@ -173,7 +173,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:168:5 + --> tests/ui/collapsible_if.rs:149:5 | LL | / if true { LL | | if true { diff --git a/tests/ui/collapsible_if_let_chains.edition2024.fixed b/tests/ui/collapsible_if_let_chains.edition2024.fixed new file mode 100644 index 00000000000..ad08c21ba9e --- /dev/null +++ b/tests/ui/collapsible_if_let_chains.edition2024.fixed @@ -0,0 +1,68 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && let Some(b) = Some(4) { + let _ = a + b; + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && a + 1 == 4 { + let _ = a; + } + + //~[edition2024]v collapsible_if + if Some(3) == Some(4).map(|x| x - 1) + && let Some(b) = Some(4) { + let _ = b; + } + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 + && truth() {} + + // Suffix: + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 {} + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 + && truth() {} +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 + && true {} +} diff --git a/tests/ui/collapsible_if_let_chains.edition2024.stderr b/tests/ui/collapsible_if_let_chains.edition2024.stderr new file mode 100644 index 00000000000..b0aa3cecadb --- /dev/null +++ b/tests/ui/collapsible_if_let_chains.edition2024.stderr @@ -0,0 +1,132 @@ +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:17:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:24:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:31:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:43:5 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:49:5 + | +LL | / if truth() { +LL | | if let 0 = 1 {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:56:5 + | +LL | / if truth() { +LL | | if let 0 = 1 { +LL | | if truth() {} +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 { +LL | if truth() {} +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:57:9 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_________^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:73:5 + | +LL | / if let 0 = 1 { +LL | | if true {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && true {} + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/collapsible_if_let_chains.fixed b/tests/ui/collapsible_if_let_chains.fixed deleted file mode 100644 index 3dd9498a4c9..00000000000 --- a/tests/ui/collapsible_if_let_chains.fixed +++ /dev/null @@ -1,29 +0,0 @@ -#![feature(let_chains)] -#![warn(clippy::collapsible_if)] - -fn main() { - if let Some(a) = Some(3) { - // with comment, so do not lint - if let Some(b) = Some(4) { - let _ = a + b; - } - } - - if let Some(a) = Some(3) - && let Some(b) = Some(4) { - let _ = a + b; - } - //~^^^^^ collapsible_if - - if let Some(a) = Some(3) - && a + 1 == 4 { - let _ = a; - } - //~^^^^^ collapsible_if - - if Some(3) == Some(4).map(|x| x - 1) - && let Some(b) = Some(4) { - let _ = b; - } - //~^^^^^ collapsible_if -} diff --git a/tests/ui/collapsible_if_let_chains.rs b/tests/ui/collapsible_if_let_chains.rs index 064b9a0be48..b2e88b1a556 100644 --- a/tests/ui/collapsible_if_let_chains.rs +++ b/tests/ui/collapsible_if_let_chains.rs @@ -1,4 +1,8 @@ -#![feature(let_chains)] +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + #![warn(clippy::collapsible_if)] fn main() { @@ -9,24 +13,64 @@ fn main() { } } + //~[edition2024]v collapsible_if if let Some(a) = Some(3) { if let Some(b) = Some(4) { let _ = a + b; } } - //~^^^^^ collapsible_if + //~[edition2024]v collapsible_if if let Some(a) = Some(3) { if a + 1 == 4 { let _ = a; } } - //~^^^^^ collapsible_if + //~[edition2024]v collapsible_if if Some(3) == Some(4).map(|x| x - 1) { if let Some(b) = Some(4) { let _ = b; } } - //~^^^^^ collapsible_if + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 { + if truth() {} + } + + // Suffix: + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 {} + } + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 { + if truth() {} + } + } +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 { + if true {} + } } diff --git a/tests/ui/collapsible_if_let_chains.stderr b/tests/ui/collapsible_if_let_chains.stderr deleted file mode 100644 index 64a88114c47..00000000000 --- a/tests/ui/collapsible_if_let_chains.stderr +++ /dev/null @@ -1,58 +0,0 @@ -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:12:5 - | -LL | / if let Some(a) = Some(3) { -LL | | if let Some(b) = Some(4) { -LL | | let _ = a + b; -LL | | } -LL | | } - | |_____^ - | - = note: `-D clippy::collapsible-if` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` -help: collapse nested if block - | -LL ~ if let Some(a) = Some(3) -LL ~ && let Some(b) = Some(4) { -LL | let _ = a + b; -LL ~ } - | - -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:19:5 - | -LL | / if let Some(a) = Some(3) { -LL | | if a + 1 == 4 { -LL | | let _ = a; -LL | | } -LL | | } - | |_____^ - | -help: collapse nested if block - | -LL ~ if let Some(a) = Some(3) -LL ~ && a + 1 == 4 { -LL | let _ = a; -LL ~ } - | - -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:26:5 - | -LL | / if Some(3) == Some(4).map(|x| x - 1) { -LL | | if let Some(b) = Some(4) { -LL | | let _ = b; -LL | | } -LL | | } - | |_____^ - | -help: collapse nested if block - | -LL ~ if Some(3) == Some(4).map(|x| x - 1) -LL ~ && let Some(b) = Some(4) { -LL | let _ = b; -LL ~ } - | - -error: aborting due to 3 previous errors - diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 55ef5584495..71b82040ff6 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -1,5 +1,6 @@ #![warn(clippy::collapsible_match)] #![allow( + clippy::collapsible_if, clippy::equatable_if_let, clippy::needless_return, clippy::no_effect, diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 5294a9d6975..c290d84ec29 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:14:20 + --> tests/ui/collapsible_match.rs:15:20 | LL | Ok(val) => match val { | ____________________^ @@ -10,7 +10,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:14:12 + --> tests/ui/collapsible_match.rs:15:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -21,7 +21,7 @@ LL | Some(n) => foo(n), = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:24:20 + --> tests/ui/collapsible_match.rs:25:20 | LL | Ok(val) => match val { | ____________________^ @@ -32,7 +32,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:24:12 + --> tests/ui/collapsible_match.rs:25:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -41,7 +41,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:34:9 + --> tests/ui/collapsible_match.rs:35:9 | LL | / if let Some(n) = val { LL | | @@ -51,7 +51,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:33:15 + --> tests/ui/collapsible_match.rs:34:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -59,7 +59,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:43:9 + --> tests/ui/collapsible_match.rs:44:9 | LL | / if let Some(n) = val { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:42:15 + --> tests/ui/collapsible_match.rs:43:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -79,7 +79,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:56:9 + --> tests/ui/collapsible_match.rs:57:9 | LL | / match val { LL | | @@ -89,7 +89,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:55:15 + --> tests/ui/collapsible_match.rs:56:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -98,7 +98,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:66:13 + --> tests/ui/collapsible_match.rs:67:13 | LL | / if let Some(n) = val { LL | | @@ -108,7 +108,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:65:12 + --> tests/ui/collapsible_match.rs:66:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -116,7 +116,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:77:9 + --> tests/ui/collapsible_match.rs:78:9 | LL | / match val { LL | | @@ -126,7 +126,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:76:15 + --> tests/ui/collapsible_match.rs:77:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -135,7 +135,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:89:13 + --> tests/ui/collapsible_match.rs:90:13 | LL | / if let Some(n) = val { LL | | @@ -147,7 +147,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:88:12 + --> tests/ui/collapsible_match.rs:89:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -155,7 +155,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:102:20 + --> tests/ui/collapsible_match.rs:103:20 | LL | Ok(val) => match val { | ____________________^ @@ -166,7 +166,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:102:12 + --> tests/ui/collapsible_match.rs:103:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -175,7 +175,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:112:22 + --> tests/ui/collapsible_match.rs:113:22 | LL | Some(val) => match val { | ______________________^ @@ -186,7 +186,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:112:14 + --> tests/ui/collapsible_match.rs:113:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -195,7 +195,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:256:22 + --> tests/ui/collapsible_match.rs:257:22 | LL | Some(val) => match val { | ______________________^ @@ -206,7 +206,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:256:14 + --> tests/ui/collapsible_match.rs:257:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -215,7 +215,7 @@ LL | E::A(val) | E::B(val) => foo(val), | ^^^^^^^^^^^^^^^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:288:9 + --> tests/ui/collapsible_match.rs:289:9 | LL | / if let Some(u) = a { LL | | @@ -225,7 +225,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:287:27 + --> tests/ui/collapsible_match.rs:288:27 | LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding @@ -233,7 +233,7 @@ LL | if let Some(u) = a { | ^^^^^^^ with this pattern, prefixed by `a`: error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:298:9 + --> tests/ui/collapsible_match.rs:299:9 | LL | / if let Some(u) = a { LL | | @@ -243,7 +243,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:297:35 + --> tests/ui/collapsible_match.rs:298:35 | LL | if let Issue9647::A { a: Some(a), .. } = x { | ^ replace this binding diff --git a/tests/ui/comparison_chain.rs b/tests/ui/comparison_chain.rs index 669690a4d42..5695d48e0df 100644 --- a/tests/ui/comparison_chain.rs +++ b/tests/ui/comparison_chain.rs @@ -12,11 +12,10 @@ fn f(x: u8, y: u8, z: u8) { a() } - if x > y { - //~^ comparison_chain - + // Ignored: Not all cases are covered + if x < y { a() - } else if x < y { + } else if x > y { b() } @@ -123,9 +122,8 @@ fn g(x: f64, y: f64, z: f64) { } fn h<T: Ord>(x: T, y: T, z: T) { + // Ignored: Not all cases are covered if x > y { - //~^ comparison_chain - a() } else if x < y { b() diff --git a/tests/ui/comparison_chain.stderr b/tests/ui/comparison_chain.stderr index 0256573d0d9..ce580bd54a1 100644 --- a/tests/ui/comparison_chain.stderr +++ b/tests/ui/comparison_chain.stderr @@ -1,12 +1,12 @@ error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:15:5 + --> tests/ui/comparison_chain.rs:29:5 | LL | / if x > y { LL | | LL | | LL | | a() -LL | | } else if x < y { -LL | | b() +... | +LL | | c() LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` | @@ -14,19 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::comparison_chain)]` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:30:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -... | -LL | | c() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:40:5 + --> tests/ui/comparison_chain.rs:39:5 | LL | / if x > y { LL | | @@ -38,7 +26,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:50:5 + --> tests/ui/comparison_chain.rs:49:5 | LL | / if x > 1 { LL | | @@ -50,19 +38,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&1) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:126:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -LL | | } else if x < y { -LL | | b() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:134:5 + --> tests/ui/comparison_chain.rs:132:5 | LL | / if x > y { LL | | @@ -74,7 +50,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:144:5 + --> tests/ui/comparison_chain.rs:142:5 | LL | / if x > y { LL | | @@ -86,7 +62,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:251:5 + --> tests/ui/comparison_chain.rs:249:5 | LL | / if x + 1 > y * 2 { LL | | @@ -97,5 +73,5 @@ LL | | "cc" LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match (x + 1).cmp(&(y * 2)) {...}` -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed index dfbb6168384..7a71829dd62 100644 --- a/tests/ui/comparison_to_empty.fixed +++ b/tests/ui/comparison_to_empty.fixed @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs index 61cdb2bbe9f..5d213a09e81 100644 --- a/tests/ui/comparison_to_empty.rs +++ b/tests/ui/comparison_to_empty.rs @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr index 00a50430a3e..deb3e938878 100644 --- a/tests/ui/comparison_to_empty.stderr +++ b/tests/ui/comparison_to_empty.stderr @@ -1,5 +1,5 @@ error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:8:13 + --> tests/ui/comparison_to_empty.rs:7:13 | LL | let _ = s == ""; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` @@ -8,73 +8,73 @@ LL | let _ = s == ""; = help: to override `-D warnings` add `#[allow(clippy::comparison_to_empty)]` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:10:13 + --> tests/ui/comparison_to_empty.rs:9:13 | LL | let _ = s != ""; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:14:13 + --> tests/ui/comparison_to_empty.rs:13:13 | LL | let _ = v == []; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:16:13 + --> tests/ui/comparison_to_empty.rs:15:13 | LL | let _ = v != []; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:18:8 + --> tests/ui/comparison_to_empty.rs:17:8 | LL | if let [] = &*v {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(*v).is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:21:8 + --> tests/ui/comparison_to_empty.rs:20:8 | LL | if let [] = s {} | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:23:8 + --> tests/ui/comparison_to_empty.rs:22:8 | LL | if let [] = &*s {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:25:8 + --> tests/ui/comparison_to_empty.rs:24:8 | LL | if let [] = &*s | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:27:12 + --> tests/ui/comparison_to_empty.rs:26:12 | LL | && s == [] | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:48:13 + --> tests/ui/comparison_to_empty.rs:47:13 | LL | let _ = s.eq(""); | ^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:50:13 + --> tests/ui/comparison_to_empty.rs:49:13 | LL | let _ = s.ne(""); | ^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:53:13 + --> tests/ui/comparison_to_empty.rs:52:13 | LL | let _ = v.eq(&[]); | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:55:13 + --> tests/ui/comparison_to_empty.rs:54:13 | LL | let _ = v.ne(&[]); | ^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` diff --git a/tests/ui/confusing_method_to_numeric_cast.fixed b/tests/ui/confusing_method_to_numeric_cast.fixed new file mode 100644 index 00000000000..e698b99edd5 --- /dev/null +++ b/tests/ui/confusing_method_to_numeric_cast.fixed @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast +} diff --git a/tests/ui/confusing_method_to_numeric_cast.rs b/tests/ui/confusing_method_to_numeric_cast.rs new file mode 100644 index 00000000000..ef65c21563d --- /dev/null +++ b/tests/ui/confusing_method_to_numeric_cast.rs @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::max as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min as usize; //~ confusing_method_to_numeric_cast + let _ = u16::max_value as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min_value as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::maximum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::max as usize; //~ confusing_method_to_numeric_cast + let _ = f32::minimum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::min as usize; //~ confusing_method_to_numeric_cast +} diff --git a/tests/ui/confusing_method_to_numeric_cast.stderr b/tests/ui/confusing_method_to_numeric_cast.stderr new file mode 100644 index 00000000000..ba90df2059a --- /dev/null +++ b/tests/ui/confusing_method_to_numeric_cast.stderr @@ -0,0 +1,100 @@ +error: casting function pointer `u16::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:5:13 + | +LL | let _ = u16::max as usize; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::confusing-method-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::confusing_method_to_numeric_cast)]` +help: did you mean to use the associated constant? + | +LL - let _ = u16::max as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:6:13 + | +LL | let _ = u16::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `u16::max_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:7:13 + | +LL | let _ = u16::max_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::max_value as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:8:13 + | +LL | let _ = u16::min_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min_value as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `f32::maximum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:10:13 + | +LL | let _ = f32::maximum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::maximum as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:11:13 + | +LL | let _ = f32::max as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::max as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::minimum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:12:13 + | +LL | let _ = f32::minimum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::minimum as usize; +LL + let _ = f32::MIN as usize; + | + +error: casting function pointer `f32::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:13:13 + | +LL | let _ = f32::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::min as usize; +LL + let _ = f32::MIN as usize; + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/crashes/ice-11230.fixed b/tests/ui/crashes/ice-11230.fixed index 181e1ebbe5a..c49a419f0d4 100644 --- a/tests/ui/crashes/ice-11230.fixed +++ b/tests/ui/crashes/ice-11230.fixed @@ -12,7 +12,7 @@ fn main() { // needless_collect trait Helper<'a>: Iterator<Item = fn()> {} +// Should not be linted because we have no idea whether the iterator has side effects fn x(w: &mut dyn for<'a> Helper<'a>) { - w.next().is_none(); - //~^ needless_collect + w.collect::<Vec<_>>().is_empty(); } diff --git a/tests/ui/crashes/ice-11230.rs b/tests/ui/crashes/ice-11230.rs index fb05dc781bc..f66b7e961c8 100644 --- a/tests/ui/crashes/ice-11230.rs +++ b/tests/ui/crashes/ice-11230.rs @@ -12,7 +12,7 @@ fn main() { // needless_collect trait Helper<'a>: Iterator<Item = fn()> {} +// Should not be linted because we have no idea whether the iterator has side effects fn x(w: &mut dyn for<'a> Helper<'a>) { w.collect::<Vec<_>>().is_empty(); - //~^ needless_collect } diff --git a/tests/ui/crashes/ice-11230.stderr b/tests/ui/crashes/ice-11230.stderr index b4a3f67081a..91d59121ac4 100644 --- a/tests/ui/crashes/ice-11230.stderr +++ b/tests/ui/crashes/ice-11230.stderr @@ -7,14 +7,5 @@ LL | for v in A.iter() {} = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` -error: avoid using `collect()` when not needed - --> tests/ui/crashes/ice-11230.rs:16:7 - | -LL | w.collect::<Vec<_>>().is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` - | - = note: `-D clippy::needless-collect` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/crashes/ice-12979.1.fixed b/tests/ui/crashes/ice-12979.1.fixed new file mode 100644 index 00000000000..e68f1c20a8e --- /dev/null +++ b/tests/ui/crashes/ice-12979.1.fixed @@ -0,0 +1,2 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr +const FOO: u8 = 0; diff --git a/tests/ui/crashes/ice-12979.2.fixed b/tests/ui/crashes/ice-12979.2.fixed new file mode 100644 index 00000000000..e89fa636d4b --- /dev/null +++ b/tests/ui/crashes/ice-12979.2.fixed @@ -0,0 +1,3 @@ +#![deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/tests/ui/crashes/ice-12979.rs b/tests/ui/crashes/ice-12979.rs new file mode 100644 index 00000000000..a2787291d9e --- /dev/null +++ b/tests/ui/crashes/ice-12979.rs @@ -0,0 +1,3 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/tests/ui/crashes/ice-12979.stderr b/tests/ui/crashes/ice-12979.stderr new file mode 100644 index 00000000000..5e760816164 --- /dev/null +++ b/tests/ui/crashes/ice-12979.stderr @@ -0,0 +1,19 @@ +error: empty line after outer attribute + --> tests/ui/crashes/ice-12979.rs:1:1 + | +LL | / #[deny(clippy::declare_interior_mutable_const)] +LL | | + | |_^ +LL | const FOO: u8 = 0; + | --------- the attribute applies to this constant item + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` + = help: if the empty line is unintentional, remove it +help: if the attribute should apply to the crate use an inner attribute + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | + + +error: aborting due to 1 previous error + diff --git a/tests/ui/crashes/ice-13544-original.rs b/tests/ui/crashes/ice-13544-original.rs new file mode 100644 index 00000000000..1709eaeb365 --- /dev/null +++ b/tests/ui/crashes/ice-13544-original.rs @@ -0,0 +1,45 @@ +//@ check-pass +#![warn(clippy::significant_drop_tightening)] + +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; + +trait Scopable: Sized { + type SubType: Scopable; +} + +struct Subtree<T: Scopable>(ManuallyDrop<Box<Tree<T::SubType>>>); + +impl<T: Scopable> Drop for Subtree<T> { + fn drop(&mut self) { + // SAFETY: The field cannot be used after we drop + unsafe { ManuallyDrop::drop(&mut self.0) } + } +} + +impl<T: Scopable> Deref for Subtree<T> { + type Target = Tree<T::SubType>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T: Scopable> DerefMut for Subtree<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +enum Tree<T: Scopable> { + Group(Vec<Tree<T>>), + Subtree(Subtree<T>), + Leaf(T), +} + +impl<T: Scopable> Tree<T> { + fn foo(self) -> Self { + self + } +} + +fn main() {} diff --git a/tests/ui/crashes/ice-13544-reduced.rs b/tests/ui/crashes/ice-13544-reduced.rs new file mode 100644 index 00000000000..9266e71f5d0 --- /dev/null +++ b/tests/ui/crashes/ice-13544-reduced.rs @@ -0,0 +1,16 @@ +//@ check-pass +#![warn(clippy::significant_drop_tightening)] +#![allow(unused, clippy::no_effect)] + +use std::marker::PhantomData; + +trait Trait { + type Assoc: Trait; +} +struct S<T: Trait>(*const S<T::Assoc>, PhantomData<T>); + +fn f<T: Trait>(x: &mut S<T>) { + &mut x.0; +} + +fn main() {} diff --git a/tests/ui/crashes/ice-1782.rs b/tests/ui/crashes/ice-1782.rs index 4a1886c08af..776b0a93bf7 100644 --- a/tests/ui/crashes/ice-1782.rs +++ b/tests/ui/crashes/ice-1782.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(dead_code, unused_variables, invalid_null_arguments)] +#![allow(dead_code, unused_variables, invalid_null_arguments, unnecessary_transmutes)] #![allow(clippy::unnecessary_cast, clippy::missing_transmute_annotations)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/tests/ui/crashes/ice-7423.rs b/tests/ui/crashes/ice-7423.rs index a03981842fc..fbf5d6520ed 100644 --- a/tests/ui/crashes/ice-7423.rs +++ b/tests/ui/crashes/ice-7423.rs @@ -6,7 +6,7 @@ pub trait Trait { impl Trait for usize { fn f() { - extern "C" { + unsafe extern "C" { fn g() -> usize; } } diff --git a/tests/ui/crashes/ice-9445.rs b/tests/ui/crashes/ice-9445.rs deleted file mode 100644 index 232b8e4a795..00000000000 --- a/tests/ui/crashes/ice-9445.rs +++ /dev/null @@ -1,4 +0,0 @@ -const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit(); -//~^ declare_interior_mutable_const - -fn main() {} diff --git a/tests/ui/crashes/ice-9445.stderr b/tests/ui/crashes/ice-9445.stderr deleted file mode 100644 index 76689cd6f5c..00000000000 --- a/tests/ui/crashes/ice-9445.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/crashes/ice-9445.rs:1:1 - | -LL | const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: aborting due to 1 previous error - diff --git a/tests/ui/crashes/missing_const_for_fn_14774.fixed b/tests/ui/crashes/missing_const_for_fn_14774.fixed new file mode 100644 index 00000000000..9c85c4b8464 --- /dev/null +++ b/tests/ui/crashes/missing_const_for_fn_14774.fixed @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + const fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/tests/ui/crashes/missing_const_for_fn_14774.rs b/tests/ui/crashes/missing_const_for_fn_14774.rs new file mode 100644 index 00000000000..6519be61256 --- /dev/null +++ b/tests/ui/crashes/missing_const_for_fn_14774.rs @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/tests/ui/crashes/missing_const_for_fn_14774.stderr b/tests/ui/crashes/missing_const_for_fn_14774.stderr new file mode 100644 index 00000000000..a407376d0b9 --- /dev/null +++ b/tests/ui/crashes/missing_const_for_fn_14774.stderr @@ -0,0 +1,17 @@ +error: this could be a `const fn` + --> tests/ui/crashes/missing_const_for_fn_14774.rs:6:5 + | +LL | / fn foo(a: usize) -> usize { +LL | | a + 10 +LL | | } + | |_____^ + | + = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]` +help: make the function `const` + | +LL | const fn foo(a: usize) -> usize { + | +++++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs new file mode 100644 index 00000000000..c65df275038 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const.rs @@ -0,0 +1,200 @@ +#![deny(clippy::declare_interior_mutable_const)] +#![allow(clippy::missing_const_for_thread_local)] + +use core::cell::{Cell, RefCell, UnsafeCell}; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ptr; +use core::sync::atomic::AtomicUsize; + +fn main() {} + +const _: Cell<u32> = Cell::new(0); +const UNSAFE_CELL: UnsafeCell<u32> = UnsafeCell::new(0); //~ declare_interior_mutable_const +const REF_CELL: RefCell<u32> = RefCell::new(0); //~ declare_interior_mutable_const +const CELL: Cell<u32> = Cell::new(0); //~ declare_interior_mutable_const + +// Constants can't contain pointers or references to type with interior mutability. +const fn make_ptr() -> *const Cell<u32> { + ptr::null() +} +const PTR: *const Cell<u32> = make_ptr(); + +const fn casted_to_cell_ptr() -> *const Cell<u32> { + const VALUE: u32 = 0; + &VALUE as *const _ as *const Cell<u32> +} +const TRANSMUTED_PTR: *const Cell<u32> = casted_to_cell_ptr(); + +const CELL_TUPLE: (bool, Cell<u32>) = (true, Cell::new(0)); //~ declare_interior_mutable_const +const CELL_ARRAY: [Cell<u32>; 2] = [Cell::new(0), Cell::new(0)]; //~ declare_interior_mutable_const + +const UNINIT_CELL: MaybeUninit<Cell<&'static ()>> = MaybeUninit::uninit(); + +struct CellStruct { + x: u32, + cell: Cell<u32>, +} +//~v declare_interior_mutable_const +const CELL_STRUCT: CellStruct = CellStruct { + x: 0, + cell: Cell::new(0), +}; + +enum CellEnum { + Cell(Cell<u32>), +} +const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); //~ declare_interior_mutable_const + +const NONE_CELL: Option<Cell<u32>> = None; +const SOME_CELL: Option<Cell<u32>> = Some(Cell::new(0)); //~ declare_interior_mutable_const + +struct NestedCell([(Option<Cell<u32>>,); 1]); +const NONE_NESTED_CELL: NestedCell = NestedCell([(None,)]); +const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); //~ declare_interior_mutable_const + +union UnionCell { + cell: ManuallyDrop<Cell<u32>>, + x: u32, +} +//~v declare_interior_mutable_const +const UNION_CELL: UnionCell = UnionCell { + cell: ManuallyDrop::new(Cell::new(0)), +}; +// Access to either union field is valid so we have to be conservative here. +const UNION_U32: UnionCell = UnionCell { x: 0 }; //~ declare_interior_mutable_const + +struct Assoc; +impl Assoc { + const SELF: Self = Self; + const CELL: Cell<u32> = Cell::new(0); //~ declare_interior_mutable_const +} + +struct AssocCell(Cell<u32>); +impl AssocCell { + const SELF: Self = Self(Cell::new(0)); //~ declare_interior_mutable_const + const NONE_SELF: Option<Self> = None; + const SOME_SELF: Option<Self> = Some(Self(Cell::new(0))); //~ declare_interior_mutable_const +} + +trait ConstDefault { + // May or may not be `Freeze` + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl<T: ConstDefault> ConstDefault for Cell<T> { + // Interior mutability is forced by the trait. + const DEFAULT: Self = Cell::new(T::DEFAULT); +} +impl<T: ConstDefault> ConstDefault for Option<Cell<T>> { + // Could have been `None` + const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} + +enum GenericEnumCell<T> { + Cell(Cell<T>), + Other(T), +} +impl<T: ConstDefault> ConstDefault for GenericEnumCell<T> { + const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} +impl<T: ConstDefault> GenericEnumCell<T> { + const CELL: Self = Self::DEFAULT; //~ declare_interior_mutable_const + const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); //~ declare_interior_mutable_const + const OTHER: Self = Self::Other(T::DEFAULT); + const FROM_OTHER: Self = Self::OTHER; +} + +enum GenericNestedEnumCell<T> { + GenericEnumCell(GenericEnumCell<T>), + EnumCell(GenericEnumCell<u32>), + Other(T), +} +impl<T: ConstDefault> GenericNestedEnumCell<T> { + const GENERIC_OTHER: Self = Self::GenericEnumCell(GenericEnumCell::<T>::FROM_OTHER); + const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::<T>::CELL); //~ declare_interior_mutable_const + const ENUM_OTHER: Self = Self::EnumCell(GenericEnumCell::<u32>::FROM_OTHER); + const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::<u32>::CELL); //~ declare_interior_mutable_const +} + +trait CellTrait: ConstDefault + Sized { + // Must be non-`Freeze` due to the type + const CELL: Cell<Self>; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option<Cell<Self>>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option<Cell<Self>> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option<Cell<Self>> = None; +} + +trait CellWithAssoc { + type T; + const DEFAULT: Self::T; + // Must be non-`Freeze` due to the type + const CELL: Cell<Self::T>; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option<Cell<Self::T>>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option<Cell<Self::T>> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option<Cell<Self::T>> = None; +} + +impl CellWithAssoc for () { + type T = u32; + const DEFAULT: Self::T = 0; + const CELL: Cell<Self::T> = Cell::new(0); + const OPTION_CELL: Option<Cell<Self::T>> = None; +} + +trait WithAssoc { + type T; + const VALUE: Self::T; +} + +impl WithAssoc for u32 { + type T = Cell<u32>; + // The cell comes from the impl block, not the trait. + const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithLayeredAssoc { + type T: WithAssoc; + const VALUE: <Self::T as WithAssoc>::T; +} + +impl WithLayeredAssoc for u32 { + type T = u32; + // The cell comes from the impl block, not the trait. + const VALUE: <Self::T as WithAssoc>::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssoc { + type T<U>; + const VALUE: Self::T<u32>; +} + +impl WithGenericAssoc for u32 { + type T<U> = Cell<U>; + const VALUE: Self::T<u32> = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssocCell { + type T<U>; + const VALUE: Self::T<Cell<u32>>; +} + +impl WithGenericAssocCell for u32 { + type T<U> = Option<U>; + const VALUE: Self::T<Cell<u32>> = None; +} + +impl WithGenericAssocCell for i32 { + type T<U> = Option<U>; + const VALUE: Self::T<Cell<u32>> = Some(Cell::new(0)); //~ declare_interior_mutable_const +} + +thread_local!(static THREAD_LOCAL_CELL: Cell<u32> = const { Cell::new(0) }); +thread_local!(static THREAD_LOCAL_CELL2: Cell<u32> = Cell::new(0)); diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr new file mode 100644 index 00000000000..9742c17486c --- /dev/null +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -0,0 +1,197 @@ +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:12:7 + | +LL | const UNSAFE_CELL: UnsafeCell<u32> = UnsafeCell::new(0); + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item +note: the lint level is defined here + --> tests/ui/declare_interior_mutable_const.rs:1:9 + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:13:7 + | +LL | const REF_CELL: RefCell<u32> = RefCell::new(0); + | ^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:14:7 + | +LL | const CELL: Cell<u32> = Cell::new(0); + | ^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:28:7 + | +LL | const CELL_TUPLE: (bool, Cell<u32>) = (true, Cell::new(0)); + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:29:7 + | +LL | const CELL_ARRAY: [Cell<u32>; 2] = [Cell::new(0), Cell::new(0)]; + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:38:7 + | +LL | const CELL_STRUCT: CellStruct = CellStruct { + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:46:7 + | +LL | const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:49:7 + | +LL | const SOME_CELL: Option<Cell<u32>> = Some(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:53:7 + | +LL | const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); + | ^^^^^^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:60:7 + | +LL | const UNION_CELL: UnionCell = UnionCell { + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:64:7 + | +LL | const UNION_U32: UnionCell = UnionCell { x: 0 }; + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:69:11 + | +LL | const CELL: Cell<u32> = Cell::new(0); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:74:11 + | +LL | const SELF: Self = Self(Cell::new(0)); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:76:11 + | +LL | const SOME_SELF: Option<Self> = Some(Self(Cell::new(0))); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:92:11 + | +LL | const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:100:11 + | +LL | const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:103:11 + | +LL | const CELL: Self = Self::DEFAULT; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:104:11 + | +LL | const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); + | ^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:116:11 + | +LL | const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::<T>::CELL); + | ^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:118:11 + | +LL | const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::<u32>::CELL); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:123:11 + | +LL | const CELL: Cell<Self>; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:127:11 + | +LL | const SOME_CELL: Option<Cell<Self>> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:136:11 + | +LL | const CELL: Cell<Self::T>; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:140:11 + | +LL | const SOME_CELL: Option<Cell<Self::T>> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:160:11 + | +LL | const VALUE: Self::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:171:11 + | +LL | const VALUE: <Self::T as WithAssoc>::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:181:11 + | +LL | const VALUE: Self::T<u32> = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:196:11 + | +LL | const VALUE: Self::T<Cell<u32>> = Some(Cell::new(0)); + | ^^^^^ + +error: aborting due to 28 previous errors + diff --git a/tests/ui/declare_interior_mutable_const/enums.rs b/tests/ui/declare_interior_mutable_const/enums.rs deleted file mode 100644 index c87468277fb..00000000000 --- a/tests/ui/declare_interior_mutable_const/enums.rs +++ /dev/null @@ -1,135 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell<bool>), - Frozen, -} - -// a constant with enums should be linted only when the used variant is unfrozen (#3962). -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -const fn unfrozen_variant() -> OptionalCell { - OptionalCell::Unfrozen(Cell::new(false)) -} - -const fn frozen_variant() -> OptionalCell { - OptionalCell::Frozen -} - -const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); - -enum NestedInnermost { - Unfrozen(AtomicUsize), - Frozen, -} - -struct NestedInner { - inner: NestedInnermost, -} - -enum NestedOuter { - NestedInner(NestedInner), - NotNested(usize), -} - -struct NestedOutermost { - outer: NestedOuter, -} - -// a constant with enums should be linted according to its value, no matter how structs involve. -const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { - //~^ declare_interior_mutable_const - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), - }), -}; -const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost { - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Frozen, - }), -}; - -trait AssocConsts { - // When there's no default value, lint it only according to its type. - // Further details are on the corresponding code (`NonCopyConst::check_trait_item`). - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - - // Lint default values accordingly. - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - //~^ declare_interior_mutable_const - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; -} - -// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it -// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'. -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - // even if this sets an unfrozen variant, the lint ignores it. - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); -} - -// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters -// here are values; and I think substituted generics at definitions won't appear in MIR. -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>; - const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>; -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None; -} - -// Use raw pointers since direct generics have a false negative at the type level. -enum BothOfCellAndGeneric<T> { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl<T> BothOfCellAndGeneric<T> { - const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - //~^ declare_interior_mutable_const - - // This is a false positive. The argument about this is on `is_value_unfrozen_raw` - const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - - const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5); - - // This is what is likely to be a false negative when one tries to fix - // the `GENERIC_VARIANT` false positive. - const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - //~^ declare_interior_mutable_const -} - -// associated types here is basically the same as the one above. -trait BothOfCellAndGenericWithAssocType { - type AssocType; - - const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = - //~^ declare_interior_mutable_const - BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - const FROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Frozen(5); -} - -fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/enums.stderr b/tests/ui/declare_interior_mutable_const/enums.stderr deleted file mode 100644 index 32839d14f0e..00000000000 --- a/tests/ui/declare_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:12:1 - | -LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:24:1 - | -LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:47:1 - | -LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { -LL | | -LL | | outer: NestedOuter::NestedInner(NestedInner { -LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), -LL | | }), -LL | | }; - | |__^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:62:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:64:5 - | -LL | const TO_BE_FROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:68:5 - | -LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:95:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:108:5 - | -LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:112:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:119:5 - | -LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:127:5 - | -LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = -LL | | -LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | |____________________________________________________________________^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:130:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs deleted file mode 100644 index 7ce04a3f2c3..00000000000 --- a/tests/ui/declare_interior_mutable_const/others.rs +++ /dev/null @@ -1,76 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::fmt::Display; -use std::ptr; -use std::sync::Once; -use std::sync::atomic::AtomicUsize; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -//~^ declare_interior_mutable_const -const CELL: Cell<usize> = Cell::new(6); -//~^ declare_interior_mutable_const -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); -//~^ declare_interior_mutable_const - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} -declare_const!(_ONCE: Once = Once::new()); - -// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. - -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow<str> = Cow::Borrowed("abcdef"); -// note: a const item of Cow is used in the `postgres` package. - -const NO_ANN: &dyn Display = &70; - -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -// there should be no lints on the line above line - -mod issue_8493 { - use std::cell::Cell; - - thread_local! { - static _BAR: Cell<i32> = const { Cell::new(0) }; - } - - macro_rules! issue_8493 { - () => { - const _BAZ: Cell<usize> = Cell::new(0); - //~^ declare_interior_mutable_const - static _FOOBAR: () = { - thread_local! { - static _VAR: Cell<i32> = const { Cell::new(0) }; - } - }; - }; - } - - issue_8493!(); -} - -#[repr(C, align(8))] -struct NoAtomic(usize); -#[repr(C, align(8))] -struct WithAtomic(AtomicUsize); - -const fn with_non_null() -> *const WithAtomic { - const NO_ATOMIC: NoAtomic = NoAtomic(0); - (&NO_ATOMIC as *const NoAtomic).cast() -} -const WITH_ATOMIC: *const WithAtomic = with_non_null(); - -struct Generic<T>(T); -impl<T> Generic<T> { - const RAW_POINTER: *const Cell<T> = ptr::null(); -} - -fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr deleted file mode 100644 index 09299b29041..00000000000 --- a/tests/ui/declare_interior_mutable_const/others.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:10:1 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:12:1 - | -LL | const CELL: Cell<usize> = Cell::new(6); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:14:1 - | -LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:19:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(_ONCE: Once = Once::new()); - | ----------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:47:13 - | -LL | const _BAZ: Cell<usize> = Cell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | issue_8493!(); - | ------------- in this macro invocation - | - = note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 5 previous errors - diff --git a/tests/ui/declare_interior_mutable_const/traits.rs b/tests/ui/declare_interior_mutable_const/traits.rs deleted file mode 100644 index d3139be6859..00000000000 --- a/tests/ui/declare_interior_mutable_const/traits.rs +++ /dev/null @@ -1,162 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} - -// a constant whose type is a concrete type should be linted at the definition site. -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - //~^ declare_interior_mutable_const - const INTEGER: u64; - const STRING: String; - declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INTEGER: u64 = 10; - const STRING: String = String::new(); -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -// a constant whose type is a generic type should be linted at the implementation site. -trait GenericTypes<T, U> { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; - declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); -} - -impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for u64 { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - //~^ declare_interior_mutable_const -} - -// a helper type used below -struct Wrapper<T>(T); - -// a constant whose type is an associated type should be linted at the implementation site, too. -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>; - // to ensure it can handle things when a generic type remains after normalization. - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>; -} - -impl<T: ConstDefault> AssocTypes for Vec<T> { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT); -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -// a constant whose type is an assoc type originated from a generic param bounded at the definition -// site should be linted at there. -trait AssocTypesFromGenericParam<T> -where - T: AssocTypesHelper<ToBeBounded = AtomicUsize>, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - //~^ declare_interior_mutable_const -} - -impl<T> AssocTypesFromGenericParam<T> for u64 -where - T: AssocTypesHelper<ToBeBounded = AtomicUsize>, -{ - // an associated type could remain unknown in a trait impl. - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); -} - -// a constant whose type is `Self` should be linted at the implementation site as well. -// (`Option` requires `Sized` bound.) -trait SelfType: Sized { - const SELF: Self; - // this was the one in the original issue (#5050). - const WRAPPED_SELF: Option<Self>; -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option<Self> = Some(20); -} - -impl SelfType for AtomicUsize { - // this (interior mutable `Self` const) exists in `parking_lot`. - // `const_trait_impl` will replace it in the future, hopefully. - const SELF: Self = AtomicUsize::new(17); - //~^ declare_interior_mutable_const - const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); - //~^ declare_interior_mutable_const -} - -// Even though a constant contains a generic type, if it also have an interior mutable type, -// it should be linted at the definition site. -trait BothOfCellAndGeneric<T> { - const DIRECT: Cell<T>; - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T>; - //~^ declare_interior_mutable_const -} - -impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 { - const DIRECT: Cell<T> = Cell::new(T::DEFAULT); - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); -} - -struct Local<T>(T); - -// a constant in an inherent impl are essentially the same as a normal const item -// except there can be a generic or associated type. -impl<T> Local<T> -where - T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - //~^ declare_interior_mutable_const - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - //~^ declare_interior_mutable_const -} - -fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/traits.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr deleted file mode 100644 index b03dd7a0840..00000000000 --- a/tests/ui/declare_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,88 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:16:5 - | -LL | const ATOMIC: AtomicUsize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:9:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); - | ---------------------------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:45:5 - | -LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:71:5 - | -LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:73:5 - | -LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:93:5 - | -LL | const BOUNDED: T::ToBeBounded; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:122:5 - | -LL | const SELF: Self = AtomicUsize::new(17); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:124:5 - | -LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:131:5 - | -LL | const DIRECT: Cell<T>; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:133:5 - | -LL | const INDIRECT: Cell<*const T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:138:5 - | -LL | const DIRECT: Cell<T> = Cell::new(T::DEFAULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:151:5 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:158:5 - | -LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 13 previous errors - diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index 03f5ca31f5f..40f40f7ea09 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -5,7 +5,7 @@ #![allow(clippy::missing_safety_doc)] #[link(name = "c")] -extern "C" {} +unsafe extern "C" {} #[lang = "sized"] pub trait Sized {} diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 2787f6406fe..6b69bdd29ce 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -2,20 +2,20 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` #![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice` -#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` -#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` -#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::match_on_vec_items)] //~ ERROR: lint `clippy::match_on_vec_items` #![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute` -#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` +#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` +#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` +#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` +#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` +#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` +#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` #![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` +#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` #![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect` -#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` -#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` -#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` #![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` -#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` -#![warn(clippy::match_on_vec_items)] //~ ERROR: lint `clippy::match_on_vec_items` fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 604732405c3..07e59d33d60 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,8 +1,8 @@ -error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can +error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy --> tests/ui/deprecated.rs:5:9 | -LL | #![warn(clippy::should_assert_eq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::assign_ops)] + | ^^^^^^^^^^^^^^^^^^ | = note: `-D renamed-and-removed-lints` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` @@ -13,83 +13,83 @@ error: lint `clippy::extend_from_slice` has been removed: `Vec::extend_from_slic LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator +error: lint `clippy::match_on_vec_items` has been removed: `clippy::indexing_slicing` covers indexing and slicing on `Vec<_>` --> tests/ui/deprecated.rs:7:9 | -LL | #![warn(clippy::range_step_by_zero)] +LL | #![warn(clippy::match_on_vec_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable +error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` --> tests/ui/deprecated.rs:8:9 | -LL | #![warn(clippy::unstable_as_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable +error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case --> tests/ui/deprecated.rs:9:9 | -LL | #![warn(clippy::unstable_as_mut_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::option_map_or_err_ok)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` +error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config --> tests/ui/deprecated.rs:10:9 | -LL | #![warn(clippy::misaligned_transmute)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::pub_enum_variant_names)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy +error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator --> tests/ui/deprecated.rs:11:9 | -LL | #![warn(clippy::assign_ops)] - | ^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::range_step_by_zero)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower +error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 --> tests/ui/deprecated.rs:12:9 | -LL | #![warn(clippy::unsafe_vector_initialization)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::regex_macro)] + | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` +error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated --> tests/ui/deprecated.rs:13:9 | -LL | #![warn(clippy::unused_collect)] +LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated +error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can --> tests/ui/deprecated.rs:14:9 | -LL | #![warn(clippy::replace_consts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::should_assert_eq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower --> tests/ui/deprecated.rs:15:9 | -LL | #![warn(clippy::regex_macro)] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unsafe_vector_initialization)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable --> tests/ui/deprecated.rs:16:9 | -LL | #![warn(clippy::pub_enum_variant_names)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable --> tests/ui/deprecated.rs:17:9 | -LL | #![warn(clippy::wrong_pub_self_convention)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case +error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` --> tests/ui/deprecated.rs:18:9 | -LL | #![warn(clippy::option_map_or_err_ok)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unused_collect)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::match_on_vec_items` has been removed: `clippy::indexing_slicing` covers indexing and slicing on `Vec<_>` +error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config --> tests/ui/deprecated.rs:19:9 | -LL | #![warn(clippy::match_on_vec_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::wrong_pub_self_convention)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 15 previous errors diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 5f2b697f88b..8cf20d8b1a1 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -1,4 +1,3 @@ - //! This file tests for the `DOC_MARKDOWN` lint. #![allow(dead_code, incomplete_features)] @@ -272,7 +271,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern "C" { +unsafe extern "C" { /// `foo()` //~^ doc_markdown fn in_extern(); diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index ed3925694c6..5b6f2bd8330 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -1,4 +1,3 @@ - //! This file tests for the `DOC_MARKDOWN` lint. #![allow(dead_code, incomplete_features)] @@ -272,7 +271,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern "C" { +unsafe extern "C" { /// foo() //~^ doc_markdown fn in_extern(); diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index d67da75a230..98c26e6bec2 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -1,5 +1,5 @@ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:9:9 + --> tests/ui/doc/doc-fixable.rs:8:9 | LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) | ^^^^^^^ @@ -13,7 +13,7 @@ LL + /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:9:51 + --> tests/ui/doc/doc-fixable.rs:8:51 | LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) | ^^^^^^^^ @@ -25,7 +25,7 @@ LL + /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:12:83 + --> tests/ui/doc/doc-fixable.rs:11:83 | LL | /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun | ^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. B | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:15:13 + --> tests/ui/doc/doc-fixable.rs:14:13 | LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though. | ^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + /// Here be `::a::global:path`, and _::another::global::path_. :: is not a | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:15:36 + --> tests/ui/doc/doc-fixable.rs:14:36 | LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though. | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + /// Here be ::a::global:path, and _`::another::global::path`_. :: is not a | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:18:25 + --> tests/ui/doc/doc-fixable.rs:17:25 | LL | /// Import an item from ::awesome::global::blob:: (Intended postfix) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + /// Import an item from `::awesome::global::blob::` (Intended postfix) | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:20:31 + --> tests/ui/doc/doc-fixable.rs:19:31 | LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted) | ^^^^^ @@ -85,7 +85,7 @@ LL + /// These are the options for `::Cat`: (Intended trailing single colon, sho | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:22:22 + --> tests/ui/doc/doc-fixable.rs:21:22 | LL | /// That's not code ~NotInCodeBlock~. | ^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + /// That's not code ~`NotInCodeBlock`~. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:24:5 + --> tests/ui/doc/doc-fixable.rs:23:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:39:5 + --> tests/ui/doc/doc-fixable.rs:38:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:47:5 + --> tests/ui/doc/doc-fixable.rs:46:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -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:62:5 + --> tests/ui/doc/doc-fixable.rs:61:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:91:5 + --> tests/ui/doc/doc-fixable.rs:90:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:109:5 + --> tests/ui/doc/doc-fixable.rs:108:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:118:8 + --> tests/ui/doc/doc-fixable.rs:117:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:122:7 + --> tests/ui/doc/doc-fixable.rs:121:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:125:22 + --> tests/ui/doc/doc-fixable.rs:124:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:127:5 + --> tests/ui/doc/doc-fixable.rs:126:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:135:5 + --> tests/ui/doc/doc-fixable.rs:134:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:149:5 + --> tests/ui/doc/doc-fixable.rs:148:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:161:43 + --> tests/ui/doc/doc-fixable.rs:160:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:166:5 + --> tests/ui/doc/doc-fixable.rs:165:5 | LL | And BarQuz too. | ^^^^^^ @@ -265,7 +265,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:167:1 + --> tests/ui/doc/doc-fixable.rs:166:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:175:43 + --> tests/ui/doc/doc-fixable.rs:174:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:180:5 + --> tests/ui/doc/doc-fixable.rs:179:5 | LL | And BarQuz too. | ^^^^^^ @@ -301,7 +301,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:181:1 + --> tests/ui/doc/doc-fixable.rs:180:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:195:5 + --> tests/ui/doc/doc-fixable.rs:194:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:215:22 + --> tests/ui/doc/doc-fixable.rs:214:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:240:34 + --> tests/ui/doc/doc-fixable.rs:239:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:22 + --> tests/ui/doc/doc-fixable.rs:263:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:30 + --> tests/ui/doc/doc-fixable.rs:263:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:269:5 + --> tests/ui/doc/doc-fixable.rs:268:5 | LL | /// ABes | ^^^^ @@ -385,7 +385,7 @@ LL + /// `ABes` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:276:9 + --> tests/ui/doc/doc-fixable.rs:275:9 | LL | /// foo() | ^^^^^ @@ -397,7 +397,7 @@ LL + /// `foo()` | error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:281:5 + --> tests/ui/doc/doc-fixable.rs:280: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/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index 1bdf01e4e22..7146fd7941a 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -103,7 +103,7 @@ macro_rules! very_unsafe { /// /// Please keep the seat belt fastened pub unsafe fn drive() { - whee() + unsafe { whee() } } }; } diff --git a/tests/ui/double_ended_iterator_last.fixed b/tests/ui/double_ended_iterator_last.fixed index 17d0d71a885..be31ee5fb48 100644 --- a/tests/ui/double_ended_iterator_last.fixed +++ b/tests/ui/double_ended_iterator_last.fixed @@ -52,31 +52,51 @@ fn main() { let _ = CustomLast.last(); } +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn issue_14139() { let mut index = [true, true, false, false, false, true].iter(); - let mut subindex = index.by_ref().take(3); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let subindex = index.by_ref().take(3); + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); - let (mut subindex, _) = (index.by_ref().take(3), 42); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let (subindex, _) = (index.by_ref().take(3), 42); + let _ = subindex.last(); + let _ = index.next(); } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter<S>); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option<Self::Item> { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -85,8 +105,19 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let mut v = v.into_iter(); + let mut v = DropDeIterator(v.into_iter()); println!("Last element is {}", v.next_back().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); } + +fn issue_14444() { + let mut squares = vec![]; + let last_square = [1, 2, 3] + .into_iter() + .map(|x| { + squares.push(x * x); + Some(x * x) + }) + .last(); +} diff --git a/tests/ui/double_ended_iterator_last.rs b/tests/ui/double_ended_iterator_last.rs index 41bc669b171..30864e15bce 100644 --- a/tests/ui/double_ended_iterator_last.rs +++ b/tests/ui/double_ended_iterator_last.rs @@ -52,31 +52,51 @@ fn main() { let _ = CustomLast.last(); } +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn issue_14139() { let mut index = [true, true, false, false, false, true].iter(); let subindex = index.by_ref().take(3); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let (subindex, _) = (index.by_ref().take(3), 42); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter<S>); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option<Self::Item> { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -85,8 +105,19 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let v = v.into_iter(); + let v = DropDeIterator(v.into_iter()); println!("Last element is {}", v.last().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); } + +fn issue_14444() { + let mut squares = vec![]; + let last_square = [1, 2, 3] + .into_iter() + .map(|x| { + squares.push(x * x); + Some(x * x) + }) + .last(); +} diff --git a/tests/ui/double_ended_iterator_last.stderr b/tests/ui/double_ended_iterator_last.stderr index 1702a24d7a0..72a6ead47a9 100644 --- a/tests/ui/double_ended_iterator_last.stderr +++ b/tests/ui/double_ended_iterator_last.stderr @@ -18,55 +18,7 @@ LL | let _ = DeIterator.last(); | help: try: `next_back()` error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:58:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^^^^^^^ - | -help: try - | -LL ~ let mut subindex = index.by_ref().take(3); -LL ~ let _ = subindex.next_back(); - | - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:62:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:67:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:72:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:76:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^^^^^^^ - | -help: try - | -LL ~ let (mut subindex, _) = (index.by_ref().take(3), 42); -LL ~ let _ = subindex.next_back(); - | - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:89:36 + --> tests/ui/double_ended_iterator_last.rs:109:36 | LL | println!("Last element is {}", v.last().unwrap().0); | ^^^^^^^^ @@ -74,9 +26,9 @@ LL | println!("Last element is {}", v.last().unwrap().0); = note: this change will alter drop order which may be undesirable help: try | -LL ~ let mut v = v.into_iter(); +LL ~ let mut v = DropDeIterator(v.into_iter()); LL ~ println!("Last element is {}", v.next_back().unwrap().0); | -error: aborting due to 8 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/double_ended_iterator_last_unfixable.rs b/tests/ui/double_ended_iterator_last_unfixable.rs index 3f125c7f20c..e9218bbb409 100644 --- a/tests/ui/double_ended_iterator_last_unfixable.rs +++ b/tests/ui/double_ended_iterator_last_unfixable.rs @@ -1,13 +1,29 @@ //@no-rustfix #![warn(clippy::double_ended_iterator_last)] +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn main() { let mut index = [true, true, false, false, false, true].iter(); let subindex = (index.by_ref().take(3), 42); - let _ = subindex.0.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.0.last(); + let _ = index.next(); } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter<S>); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option<Self::Item> { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -16,7 +32,7 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let v = (v.into_iter(), 42); + let v = (DropDeIterator(v.into_iter()), 42); println!("Last element is {}", v.0.last().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); diff --git a/tests/ui/double_ended_iterator_last_unfixable.stderr b/tests/ui/double_ended_iterator_last_unfixable.stderr index f4be757d00d..e330a22a354 100644 --- a/tests/ui/double_ended_iterator_last_unfixable.stderr +++ b/tests/ui/double_ended_iterator_last_unfixable.stderr @@ -1,21 +1,5 @@ error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13 - | -LL | let _ = subindex.0.last(); - | ^^^^^^^^^^^------ - | | - | help: try: `next_back()` - | -note: this must be made mutable to use `.next_back()` - --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13 - | -LL | let _ = subindex.0.last(); - | ^^^^^^^^^^ - = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^^------ @@ -24,10 +8,12 @@ LL | println!("Last element is {}", v.0.last().unwrap().0); | = note: this change will alter drop order which may be undesirable note: this must be made mutable to use `.next_back()` - --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^ + = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/eager_transmute.fixed b/tests/ui/eager_transmute.fixed index 14cbb6113e6..47a32ec836c 100644 --- a/tests/ui/eager_transmute.fixed +++ b/tests/ui/eager_transmute.fixed @@ -71,8 +71,10 @@ fn f(op: u8, op2: Data, unrelated: u8) { } unsafe fn f2(op: u8) { - (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); - //~^ eager_transmute + unsafe { + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); + //~^ eager_transmute + } } #[rustc_layout_scalar_valid_range_end(254)] diff --git a/tests/ui/eager_transmute.rs b/tests/ui/eager_transmute.rs index 48d7d50cdae..906cd7bccc8 100644 --- a/tests/ui/eager_transmute.rs +++ b/tests/ui/eager_transmute.rs @@ -71,8 +71,10 @@ fn f(op: u8, op2: Data, unrelated: u8) { } unsafe fn f2(op: u8) { - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); - //~^ eager_transmute + unsafe { + (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); + //~^ eager_transmute + } } #[rustc_layout_scalar_valid_range_end(254)] diff --git a/tests/ui/eager_transmute.stderr b/tests/ui/eager_transmute.stderr index 54850d110eb..c719ca8adc1 100644 --- a/tests/ui/eager_transmute.stderr +++ b/tests/ui/eager_transmute.stderr @@ -157,19 +157,19 @@ LL + let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem: | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:74:24 + --> tests/ui/eager_transmute.rs:75:28 | -LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider using `bool::then` to only transmute if the condition holds | -LL - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); -LL + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); +LL - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); +LL + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:104:62 + --> tests/ui/eager_transmute.rs:106:62 | LL | let _: Option<NonZero<u8>> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _: Option<NonZero<u8>> = (v1 > 0).then(|| unsafe { std::mem::transm | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:111:86 + --> tests/ui/eager_transmute.rs:113:86 | LL | let _: Option<NonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + let _: Option<NonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then(|| u | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:118:93 + --> tests/ui/eager_transmute.rs:120:93 | LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/empty_docs.rs b/tests/ui/empty_docs.rs index d7768e07901..57f8976cd6a 100644 --- a/tests/ui/empty_docs.rs +++ b/tests/ui/empty_docs.rs @@ -84,7 +84,7 @@ mod issue_12377 { use proc_macro_attr::with_empty_docs; #[with_empty_docs] - extern "C" { + unsafe extern "C" { type Test; } diff --git a/tests/ui/empty_enum_variants_with_brackets.fixed b/tests/ui/empty_enum_variants_with_brackets.fixed index 885f6a50025..abdf6ca5cb6 100644 --- a/tests/ui/empty_enum_variants_with_brackets.fixed +++ b/tests/ui/empty_enum_variants_with_brackets.fixed @@ -6,8 +6,7 @@ pub enum PublicTestEnum { NonEmptyParentheses(i32, i32), // No error EmptyBraces, //~^ empty_enum_variants_with_brackets - EmptyParentheses, - //~^ empty_enum_variants_with_brackets + EmptyParentheses(), // No error as enum is pub } enum TestEnum { @@ -20,6 +19,67 @@ enum TestEnum { AnotherEnum, // No error } +mod issue12551 { + enum EvenOdd { + // Used as functions -> no error + Even(), + Odd(), + // Not used as a function + Unknown, + //~^ empty_enum_variants_with_brackets + } + + fn even_odd(x: i32) -> EvenOdd { + (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) + } + + fn natural_number(x: i32) -> NaturalOrNot { + (x > 0) + .then(NaturalOrNot::Natural) + .unwrap_or_else(NaturalOrNot::NotNatural) + } + + enum NaturalOrNot { + // Used as functions -> no error + Natural(), + NotNatural(), + // Not used as a function + Unknown, + //~^ empty_enum_variants_with_brackets + } + + enum RedundantParenthesesFunctionCall { + // Used as a function call but with redundant parentheses + Parentheses, + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } + + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall::Parentheses; + RedundantParenthesesFunctionCall::NoParentheses; + } + + // Same test as above but with usage of the enum occurring before the definition. + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call_2() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall2::Parentheses; + RedundantParenthesesFunctionCall2::NoParentheses; + } + + enum RedundantParenthesesFunctionCall2 { + // Used as a function call but with redundant parentheses + Parentheses, + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } +} + enum TestEnumWithFeatures { NonEmptyBraces { #[cfg(feature = "thisisneverenabled")] @@ -28,4 +88,18 @@ enum TestEnumWithFeatures { NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error } +#[derive(Clone)] +enum Foo { + Variant1(i32), + Variant2, + Variant3, //~ ERROR: enum variant has empty brackets +} + +#[derive(Clone)] +pub enum PubFoo { + Variant1(i32), + Variant2, + Variant3(), +} + fn main() {} diff --git a/tests/ui/empty_enum_variants_with_brackets.rs b/tests/ui/empty_enum_variants_with_brackets.rs index 092712ee2ea..63a5a8e9143 100644 --- a/tests/ui/empty_enum_variants_with_brackets.rs +++ b/tests/ui/empty_enum_variants_with_brackets.rs @@ -6,8 +6,7 @@ pub enum PublicTestEnum { NonEmptyParentheses(i32, i32), // No error EmptyBraces {}, //~^ empty_enum_variants_with_brackets - EmptyParentheses(), - //~^ empty_enum_variants_with_brackets + EmptyParentheses(), // No error as enum is pub } enum TestEnum { @@ -20,6 +19,67 @@ enum TestEnum { AnotherEnum, // No error } +mod issue12551 { + enum EvenOdd { + // Used as functions -> no error + Even(), + Odd(), + // Not used as a function + Unknown(), + //~^ empty_enum_variants_with_brackets + } + + fn even_odd(x: i32) -> EvenOdd { + (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) + } + + fn natural_number(x: i32) -> NaturalOrNot { + (x > 0) + .then(NaturalOrNot::Natural) + .unwrap_or_else(NaturalOrNot::NotNatural) + } + + enum NaturalOrNot { + // Used as functions -> no error + Natural(), + NotNatural(), + // Not used as a function + Unknown(), + //~^ empty_enum_variants_with_brackets + } + + enum RedundantParenthesesFunctionCall { + // Used as a function call but with redundant parentheses + Parentheses(), + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } + + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall::Parentheses(); + RedundantParenthesesFunctionCall::NoParentheses; + } + + // Same test as above but with usage of the enum occurring before the definition. + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call_2() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall2::Parentheses(); + RedundantParenthesesFunctionCall2::NoParentheses; + } + + enum RedundantParenthesesFunctionCall2 { + // Used as a function call but with redundant parentheses + Parentheses(), + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } +} + enum TestEnumWithFeatures { NonEmptyBraces { #[cfg(feature = "thisisneverenabled")] @@ -28,4 +88,18 @@ enum TestEnumWithFeatures { NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error } +#[derive(Clone)] +enum Foo { + Variant1(i32), + Variant2, + Variant3(), //~ ERROR: enum variant has empty brackets +} + +#[derive(Clone)] +pub enum PubFoo { + Variant1(i32), + Variant2, + Variant3(), +} + fn main() {} diff --git a/tests/ui/empty_enum_variants_with_brackets.stderr b/tests/ui/empty_enum_variants_with_brackets.stderr index a9ae3b476dd..7fe85e829a3 100644 --- a/tests/ui/empty_enum_variants_with_brackets.stderr +++ b/tests/ui/empty_enum_variants_with_brackets.stderr @@ -9,7 +9,15 @@ LL | EmptyBraces {}, = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:9:21 + --> tests/ui/empty_enum_variants_with_brackets.rs:15:16 + | +LL | EmptyBraces {}, + | ^^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:17:21 | LL | EmptyParentheses(), | ^^ @@ -17,20 +25,58 @@ LL | EmptyParentheses(), = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:16:16 + --> tests/ui/empty_enum_variants_with_brackets.rs:28:16 | -LL | EmptyBraces {}, - | ^^^ +LL | Unknown(), + | ^^ | = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:18:21 + --> tests/ui/empty_enum_variants_with_brackets.rs:47:16 | -LL | EmptyParentheses(), - | ^^ +LL | Unknown(), + | ^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:53:20 + | +LL | Parentheses(), + | ^^ + | +help: remove the brackets + | +LL ~ Parentheses, +LL | +... +LL | // The parentheses in the below line are redundant. +LL ~ RedundantParenthesesFunctionCall::Parentheses; + | + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:76:20 + | +LL | Parentheses(), + | ^^ + | +help: remove the brackets + | +LL ~ RedundantParenthesesFunctionCall2::Parentheses; +LL | RedundantParenthesesFunctionCall2::NoParentheses; +... +LL | // Used as a function call but with redundant parentheses +LL ~ Parentheses, + | + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:95:13 + | +LL | Variant3(), + | ^^ | = help: remove the brackets -error: aborting due to 4 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index f7bbf83654f..18c80c7aba4 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -220,4 +220,19 @@ mod issue11494 { } } +mod encapsulated { + mod types { + pub struct FooError; + pub struct BarError; + pub struct BazError; + } + + enum Error { + FooError(types::FooError), + BarError(types::BarError), + BazError(types::BazError), + Other, + } +} + fn main() {} diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 166b1387ba2..ce8b67f9ca7 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if matches!(cs.iter().next(), Some('i')) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if matches!(cs.iter().next(), Some(1)) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if matches!(get_enum(), Some(MyEnum::B)) { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 09c2483ae6d..ff09533f265 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if let Some('i') = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if let Some(1) = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if let Some(MyEnum::B) = get_enum() { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 81e0e15a5c7..dd1832ad68b 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -85,5 +85,23 @@ error: this pattern matching can be expressed using equality LL | if let inline!("abc") = "abc" { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")` -error: aborting due to 14 previous errors +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:109:12 + | +LL | if let Some('i') = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some('i'))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:117:12 + | +LL | if let Some(1) = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some(1))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:135:12 + | +LL | if let Some(MyEnum::B) = get_enum() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(get_enum(), Some(MyEnum::B))` + +error: aborting due to 17 previous errors diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index 7235f7d5b82..ec6bed152e7 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -59,7 +59,7 @@ fn f_str_t<T>(_: &str, _: T) {} fn f_box_t<T>(_: &Box<T>) {} -extern "C" { +unsafe extern "C" { fn var(_: u32, ...); } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index c4d2b28ff4b..ca58c650d9c 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -59,7 +59,7 @@ fn f_str_t<T>(_: &str, _: T) {} fn f_box_t<T>(_: &Box<T>) {} -extern "C" { +unsafe extern "C" { fn var(_: u32, ...); } diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 0d1a5f80f3d..619329a6ade 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = &*opt_a.unwrap(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); <Aaa as Deref>::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &*&"foo"; //~ explicit_deref_methods + let mut x = String::new(); + let _ = &&mut **&mut x; //~ explicit_deref_methods + let _ = &&mut ***(&mut &mut x); //~ explicit_deref_methods } diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 8d4a899cd26..9f2d513283c 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = opt_a.unwrap().deref(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); <Aaa as Deref>::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &Deref::deref(&"foo"); //~ explicit_deref_methods + let mut x = String::new(); + let _ = &DerefMut::deref_mut(&mut x); //~ explicit_deref_methods + let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); //~ explicit_deref_methods } diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 2ca376cba00..a81e2f60317 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:54:19 + --> tests/ui/explicit_deref_methods.rs:55:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,70 +8,88 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:57:23 + --> tests/ui/explicit_deref_methods.rs:58:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:39 + --> tests/ui/explicit_deref_methods.rs:62:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:50 + --> tests/ui/explicit_deref_methods.rs:62:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:20 + --> tests/ui/explicit_deref_methods.rs:66:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:69:11 + --> tests/ui/explicit_deref_methods.rs:70:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:74:28 + --> tests/ui/explicit_deref_methods.rs:75:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:77:13 + --> tests/ui/explicit_deref_methods.rs:78:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:80:28 + --> tests/ui/explicit_deref_methods.rs:81:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:83:19 + --> tests/ui/explicit_deref_methods.rs:84:19 | LL | let b: &str = a.deref().deref(); | ^^^^^^^^^^^^^^^^^ help: try: `&**a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:87:13 + --> tests/ui/explicit_deref_methods.rs:88:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*opt_a.unwrap()` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:125:31 + --> tests/ui/explicit_deref_methods.rs:123:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` -error: aborting due to 12 previous errors +error: explicit `deref` method call + --> tests/ui/explicit_deref_methods.rs:141:14 + | +LL | let _ = &Deref::deref(&"foo"); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:143:14 + | +LL | let _ = &DerefMut::deref_mut(&mut x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:144:14 + | +LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` + +error: aborting due to 15 previous errors diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index cc18708d25f..25d25663d1e 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -1,7 +1,7 @@ #![warn(clippy::fn_params_excessive_bools)] #![allow(clippy::too_many_arguments)] -extern "C" { +unsafe extern "C" { // Should not lint, most of the time users have no control over extern function signatures fn f(_: bool, _: bool, _: bool, _: bool); } @@ -14,8 +14,8 @@ macro_rules! foo { foo!(); -#[no_mangle] -extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} +#[unsafe(no_mangle)] +unsafe extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: bool) {} //~^ ERROR: more than 3 bools in function parameters fn h(_: bool, _: bool, _: bool) {} @@ -39,8 +39,8 @@ impl S { fn f(&self, _: bool, _: bool, _: bool, _: bool) {} //~^ ERROR: more than 3 bools in function parameters fn g(&self, _: bool, _: bool, _: bool) {} - #[no_mangle] - extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} + #[unsafe(no_mangle)] + unsafe extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} } impl Trait for S { diff --git a/tests/ui/from_iter_instead_of_collect.fixed b/tests/ui/from_iter_instead_of_collect.fixed index 8618004efb8..be98b227795 100644 --- a/tests/ui/from_iter_instead_of_collect.fixed +++ b/tests/ui/from_iter_instead_of_collect.fixed @@ -73,3 +73,46 @@ fn main() { for _i in [1, 2, 3].iter().collect::<Vec<&i32>>() {} //~^ from_iter_instead_of_collect } + +fn issue14581() { + let nums = [0, 1, 2]; + let _ = &nums.iter().map(|&num| char::from_u32(num).unwrap()).collect::<String>(); + //~^ from_iter_instead_of_collect +} + +fn test_implicit_generic_args(iter: impl Iterator<Item = &'static i32> + Copy) { + struct S<'l, T = i32, const A: usize = 3, const B: usize = 3> { + a: [&'l T; A], + b: [&'l T; B], + } + + impl<'l, T, const A: usize, const B: usize> FromIterator<&'l T> for S<'l, T, A, B> { + fn from_iter<I: IntoIterator<Item = &'l T>>(_: I) -> Self { + todo!() + } + } + + let _ = iter.collect::<S<'static, i32, 7>>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::<S<'static, i32>>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::<S<'static, _, 7>>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::<S<'static, _, 7, 8>>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::<S<_, 7, 8>>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::<S<i32>>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::<S<i32>>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::<S>(); + //~^ from_iter_instead_of_collect +} diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index c46397e8ff5..ce20fef2ac3 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -73,3 +73,46 @@ fn main() { for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} //~^ from_iter_instead_of_collect } + +fn issue14581() { + let nums = [0, 1, 2]; + let _ = &String::from_iter(nums.iter().map(|&num| char::from_u32(num).unwrap())); + //~^ from_iter_instead_of_collect +} + +fn test_implicit_generic_args(iter: impl Iterator<Item = &'static i32> + Copy) { + struct S<'l, T = i32, const A: usize = 3, const B: usize = 3> { + a: [&'l T; A], + b: [&'l T; B], + } + + impl<'l, T, const A: usize, const B: usize> FromIterator<&'l T> for S<'l, T, A, B> { + fn from_iter<I: IntoIterator<Item = &'l T>>(_: I) -> Self { + todo!() + } + } + + let _ = <S<'static, i32, 7>>::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = <S<'static, i32>>::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = <S<'static, _, 7>>::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = <S<'static, _, 7, 8>>::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = <S<'_, _, 7, 8>>::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = <S<i32>>::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = <S<'_, i32>>::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = <S>::from_iter(iter); + //~^ from_iter_instead_of_collect +} diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index b46d97af152..ec11a375c0d 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -91,5 +91,59 @@ error: usage of `FromIterator::from_iter` LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::<Vec<&i32>>()` -error: aborting due to 15 previous errors +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:79:14 + | +LL | let _ = &String::from_iter(nums.iter().map(|&num| char::from_u32(num).unwrap())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `nums.iter().map(|&num| char::from_u32(num).unwrap()).collect::<String>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:95:13 + | +LL | let _ = <S<'static, i32, 7>>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S<'static, i32, 7>>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:98:13 + | +LL | let _ = <S<'static, i32>>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S<'static, i32>>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:101:13 + | +LL | let _ = <S<'static, _, 7>>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S<'static, _, 7>>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:104:13 + | +LL | let _ = <S<'static, _, 7, 8>>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S<'static, _, 7, 8>>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:107:13 + | +LL | let _ = <S<'_, _, 7, 8>>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S<_, 7, 8>>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:110:13 + | +LL | let _ = <S<i32>>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S<i32>>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:113:13 + | +LL | let _ = <S<'_, i32>>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S<i32>>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:116:13 + | +LL | let _ = <S>::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::<S>()` + +error: aborting due to 24 previous errors diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index ebc3acb1b77..7067434953d 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -6,19 +6,25 @@ fn ifs_same_cond() { let b = false; if b { + //~^ ifs_same_cond } else if b { + } + + if b { //~^ ifs_same_cond + } else if b { + } else if b { } if a == 1 { - } else if a == 1 { //~^ ifs_same_cond + } else if a == 1 { } if 2 * a == 1 { + //~^ ifs_same_cond } else if 2 * a == 2 { } else if 2 * a == 1 { - //~^ ifs_same_cond } else if a == 1 { } @@ -50,8 +56,8 @@ fn ifs_same_cond() { fn issue10272() { let a = String::from("ha"); if a.contains("ah") { - } else if a.contains("ah") { //~^ ifs_same_cond + } else if a.contains("ah") { // Trigger this lint } else if a.contains("ha") { diff --git a/tests/ui/ifs_same_cond.stderr b/tests/ui/ifs_same_cond.stderr index df21e6f1b82..7acbc1a6399 100644 --- a/tests/ui/ifs_same_cond.stderr +++ b/tests/ui/ifs_same_cond.stderr @@ -1,52 +1,52 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:9:15 - | -LL | } else if b { - | ^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui/ifs_same_cond.rs:8:8 | LL | if b { | ^ +LL | +LL | } else if b { + | ^ + | = note: `-D clippy::ifs-same-cond` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:14:15 - | -LL | } else if a == 1 { - | ^^^^^^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui/ifs_same_cond.rs:13:8 | +LL | if b { + | ^ +LL | +LL | } else if b { + | ^ +LL | } else if b { + | ^ + +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:19:8 + | LL | if a == 1 { | ^^^^^^ +LL | +LL | } else if a == 1 { + | ^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:20:15 - | -LL | } else if 2 * a == 1 { - | ^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:18:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:24:8 | LL | if 2 * a == 1 { | ^^^^^^^^^^ +... +LL | } else if 2 * a == 1 { + | ^^^^^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:53:15 - | -LL | } else if a.contains("ah") { - | ^^^^^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:52:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:58:8 | LL | if a.contains("ah") { | ^^^^^^^^^^^^^^^^ +LL | +LL | } else if a.contains("ah") { + | ^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index e19aa4acb4c..050cdfcba96 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum<T> { One(T), diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 29039356855..91429bfea27 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum<T> { One(T), diff --git a/tests/ui/integer_division.rs b/tests/ui/integer_division.rs index 632fedc9e8f..a40956e2672 100644 --- a/tests/ui/integer_division.rs +++ b/tests/ui/integer_division.rs @@ -1,5 +1,9 @@ #![warn(clippy::integer_division)] +use std::num::NonZeroU32; + +const TWO: NonZeroU32 = NonZeroU32::new(2).unwrap(); + fn main() { let two = 2; let n = 1 / 2; @@ -12,4 +16,8 @@ fn main() { //~^ integer_division let x = 1. / 2.0; + + let a = 1; + let s = a / TWO; + //~^ integer_division } diff --git a/tests/ui/integer_division.stderr b/tests/ui/integer_division.stderr index 0fe2021a1a9..c0e34a562f6 100644 --- a/tests/ui/integer_division.stderr +++ b/tests/ui/integer_division.stderr @@ -1,5 +1,5 @@ error: integer division - --> tests/ui/integer_division.rs:5:13 + --> tests/ui/integer_division.rs:9:13 | LL | let n = 1 / 2; | ^^^^^ @@ -9,7 +9,7 @@ LL | let n = 1 / 2; = help: to override `-D warnings` add `#[allow(clippy::integer_division)]` error: integer division - --> tests/ui/integer_division.rs:8:13 + --> tests/ui/integer_division.rs:12:13 | LL | let o = 1 / two; | ^^^^^^^ @@ -17,12 +17,20 @@ LL | let o = 1 / two; = help: division of integers may cause loss of precision. consider using floats error: integer division - --> tests/ui/integer_division.rs:11:13 + --> tests/ui/integer_division.rs:15:13 | LL | let p = two / 4; | ^^^^^^^ | = help: division of integers may cause loss of precision. consider using floats -error: aborting due to 3 previous errors +error: integer division + --> tests/ui/integer_division.rs:21:13 + | +LL | let s = a / TWO; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats + +error: aborting due to 4 previous errors diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 7fcab6592e2..874f749b33d 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -166,3 +166,18 @@ fn msrv_1_54() { let _ = map.values().map(|v| v + 2).collect::<Vec<_>>(); //~^ iter_kv_map } + +fn issue14595() { + pub struct Foo(BTreeMap<String, i32>); + + impl AsRef<BTreeMap<String, i32>> for Foo { + fn as_ref(&self) -> &BTreeMap<String, i32> { + &self.0 + } + } + + let map = Foo(BTreeMap::default()); + + let _ = map.as_ref().values().copied().collect::<Vec<_>>(); + //~^ iter_kv_map +} diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index b590aef7b80..f570e3c32cb 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -170,3 +170,18 @@ fn msrv_1_54() { let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>(); //~^ iter_kv_map } + +fn issue14595() { + pub struct Foo(BTreeMap<String, i32>); + + impl AsRef<BTreeMap<String, i32>> for Foo { + fn as_ref(&self) -> &BTreeMap<String, i32> { + &self.0 + } + } + + let map = Foo(BTreeMap::default()); + + let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::<Vec<_>>(); + //~^ iter_kv_map +} diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index 00d566ed14a..31ee76c25b7 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -263,5 +263,11 @@ error: iterating on a map's values LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` -error: aborting due to 38 previous errors +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:185:13 + | +LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::<Vec<_>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()` + +error: aborting due to 39 previous errors diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 99991269029..b0e548f1790 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -59,7 +59,7 @@ fn main() { iter: impl Iterator<Item = &'a (&'a u32, String)> + 'a, target: String, ) -> impl Iterator<Item = (&'a u32, String)> + 'a { - iter.filter(move |&(&a, b)| a == 1 && b == &target).cloned() + iter.filter(move |&&(&a, ref b)| a == 1 && b == &target).cloned() //~^ iter_overeager_cloned } diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 6a860dad5af..cedf62a6b47 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -60,7 +60,7 @@ fn main() { iter: impl Iterator<Item = &'a (&'a u32, String)> + 'a, target: String, ) -> impl Iterator<Item = (&'a u32, String)> + 'a { - iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) + iter.cloned().filter(move |&(&a, ref b)| a == 1 && b == &target) //~^ iter_overeager_cloned } diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index f3239b59582..1616dec95b7 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -120,10 +120,10 @@ LL | let _ = vec.iter().cloned().find(f); error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:63:9 | -LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) - | ^^^^------------------------------------------------------- +LL | iter.cloned().filter(move |&(&a, ref b)| a == 1 && b == &target) + | ^^^^------------------------------------------------------------ | | - | help: try: `.filter(move |&(&a, b)| a == 1 && b == &target).cloned()` + | help: try: `.filter(move |&&(&a, ref b)| a == 1 && b == &target).cloned()` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:75:13 diff --git a/tests/ui/let_underscore_untyped.rs b/tests/ui/let_underscore_untyped.rs index 26ba8682dc2..7f74ab035bd 100644 --- a/tests/ui/let_underscore_untyped.rs +++ b/tests/ui/let_underscore_untyped.rs @@ -6,7 +6,6 @@ extern crate proc_macros; use proc_macros::with_span; -use clippy_utils::is_from_proc_macro; use std::boxed::Box; use std::fmt::Display; use std::future::Future; diff --git a/tests/ui/let_underscore_untyped.stderr b/tests/ui/let_underscore_untyped.stderr index 86cdd5c662c..54b955ac3a5 100644 --- a/tests/ui/let_underscore_untyped.stderr +++ b/tests/ui/let_underscore_untyped.stderr @@ -1,11 +1,11 @@ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:51:5 + --> tests/ui/let_underscore_untyped.rs:50:5 | LL | let _ = a(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:51:10 + --> tests/ui/let_underscore_untyped.rs:50:10 | LL | let _ = a(); | ^ @@ -13,49 +13,49 @@ LL | let _ = a(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_untyped)]` error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:53:5 + --> tests/ui/let_underscore_untyped.rs:52:5 | LL | let _ = b(1); | ^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:53:10 + --> tests/ui/let_underscore_untyped.rs:52:10 | LL | let _ = b(1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:56:5 + --> tests/ui/let_underscore_untyped.rs:55:5 | LL | let _ = d(&1); | ^^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:56:10 + --> tests/ui/let_underscore_untyped.rs:55:10 | LL | let _ = d(&1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:58:5 + --> tests/ui/let_underscore_untyped.rs:57:5 | LL | let _ = e(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:58:10 + --> tests/ui/let_underscore_untyped.rs:57:10 | LL | let _ = e(); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:60:5 + --> tests/ui/let_underscore_untyped.rs:59:5 | LL | let _ = f(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:60:10 + --> tests/ui/let_underscore_untyped.rs:59:10 | LL | let _ = f(); | ^ diff --git a/tests/ui/let_with_type_underscore.fixed b/tests/ui/let_with_type_underscore.fixed new file mode 100644 index 00000000000..7a4af4e3d1e --- /dev/null +++ b/tests/ui/let_with_type_underscore.fixed @@ -0,0 +1,47 @@ +//@aux-build: proc_macros.rs +#![allow(unused)] +#![warn(clippy::let_with_type_underscore)] +#![allow(clippy::let_unit_value, clippy::needless_late_init)] + +extern crate proc_macros; + +fn func() -> &'static str { + "" +} + +#[rustfmt::skip] +fn main() { + // Will lint + let x = 1; + //~^ let_with_type_underscore + let _ = 2; + //~^ let_with_type_underscore + let x = func(); + //~^ let_with_type_underscore + let x; + //~^ let_with_type_underscore + x = (); + + let x = 1; // Will not lint, Rust infers this to an integer before Clippy + let x = func(); + let x: Vec<_> = Vec::<u32>::new(); + let x: [_; 1] = [1]; + let x = 1; + //~^ let_with_type_underscore + + // Do not lint from procedural macros + proc_macros::with_span! { + span + let x: _ = (); + // Late initialization + let x: _; + x = (); + // Ensure weird formatting will not break it (hopefully) + let x : _ = 1; + let x +: _ = 1; + let x : + _; + x = (); + }; +} diff --git a/tests/ui/let_with_type_underscore.stderr b/tests/ui/let_with_type_underscore.stderr index 2284d1fe2e4..9179f992207 100644 --- a/tests/ui/let_with_type_underscore.stderr +++ b/tests/ui/let_with_type_underscore.stderr @@ -4,13 +4,13 @@ error: variable declared with type underscore LL | let x: _ = 1; | ^^^^^^^^^^^^^ | -help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:15:10 - | -LL | let x: _ = 1; - | ^^^ = note: `-D clippy::let-with-type-underscore` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_with_type_underscore)]` +help: remove the explicit type `_` declaration + | +LL - let x: _ = 1; +LL + let x = 1; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:17:5 @@ -19,10 +19,10 @@ LL | let _: _ = 2; | ^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:17:10 | -LL | let _: _ = 2; - | ^^^ +LL - let _: _ = 2; +LL + let _ = 2; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:19:5 @@ -31,10 +31,10 @@ LL | let x: _ = func(); | ^^^^^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:19:10 | -LL | let x: _ = func(); - | ^^^ +LL - let x: _ = func(); +LL + let x = func(); + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:21:5 @@ -43,10 +43,10 @@ LL | let x: _; | ^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:21:10 | -LL | let x: _; - | ^^^ +LL - let x: _; +LL + let x; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:29:5 @@ -55,10 +55,10 @@ LL | let x : _ = 1; | ^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:29:10 | -LL | let x : _ = 1; - | ^^^^ +LL - let x : _ = 1; +LL + let x = 1; + | error: aborting due to 5 previous errors diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index ad0266d39e9..a284ca9f625 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -75,7 +75,7 @@ impl S { async fn elided(_: &i32) -> i32 { 42 } // should be ignored -fn elided_not_bound(_: &i32) -> impl Future<Output = i32> { +fn elided_not_bound(_: &i32) -> impl Future<Output = i32> + use<> { async { 42 } } @@ -84,7 +84,7 @@ async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } // should be ignored #[allow(clippy::needless_lifetimes)] -fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> { +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + use<> { async { 42 } } @@ -94,7 +94,7 @@ mod issue_5765 { struct A; impl A { - fn f(&self) -> impl Future<Output = ()> { + fn f(&self) -> impl Future<Output = ()> + use<> { async {} } } diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index fe367b4bc7b..188f8a4982c 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -102,7 +102,7 @@ fn elided(_: &i32) -> impl Future<Output = i32> + '_ { } // should be ignored -fn elided_not_bound(_: &i32) -> impl Future<Output = i32> { +fn elided_not_bound(_: &i32) -> impl Future<Output = i32> + use<> { async { 42 } } @@ -114,7 +114,7 @@ fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + // should be ignored #[allow(clippy::needless_lifetimes)] -fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> { +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + use<> { async { 42 } } @@ -124,7 +124,7 @@ mod issue_5765 { struct A; impl A { - fn f(&self) -> impl Future<Output = ()> { + fn f(&self) -> impl Future<Output = ()> + use<> { async {} } } diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed index 57fe8917afe..58ee6978fc1 100644 --- a/tests/ui/manual_div_ceil.fixed +++ b/tests/ui/manual_div_ceil.fixed @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = x.div_ceil(8); + //~^ manual_div_ceil + let _ = x.div_ceil(8); + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = 33u32.div_ceil(eight!()); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs index ec343513e5c..aa0d81b22a0 100644 --- a/tests/ui/manual_div_ceil.rs +++ b/tests/ui/manual_div_ceil.rs @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = (x + 7) / 8; + //~^ manual_div_ceil + let _ = (7 + x) / 8; + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = (33u32 + 7) / eight!(); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr index 8e14ab27426..9be5a19bf39 100644 --- a/tests/ui/manual_div_ceil.stderr +++ b/tests/ui/manual_div_ceil.stderr @@ -1,5 +1,5 @@ error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:9:13 + --> tests/ui/manual_div_ceil.rs:25:13 | LL | let _ = (x + (y - 1)) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` @@ -8,94 +8,122 @@ LL | let _ = (x + (y - 1)) / y; = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:11:13 + --> tests/ui/manual_div_ceil.rs:27:13 | LL | let _ = ((y - 1) + x) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:13:13 + --> tests/ui/manual_div_ceil.rs:29:13 | LL | let _ = (x + y - 1) / y; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:16:13 + --> tests/ui/manual_div_ceil.rs:32:13 | LL | let _ = (7_u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:18:13 + --> tests/ui/manual_div_ceil.rs:34:13 | LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:39:13 + --> tests/ui/manual_div_ceil.rs:6:17 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:8:17 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:56:13 + | +LL | let _ = (33u32 + 7) / eight!(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `33u32.div_ceil(eight!())` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:62:13 | LL | let _ = (2048 + x - 1) / x; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:43:13 + --> tests/ui/manual_div_ceil.rs:66:13 | LL | let _ = (2048usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:47:13 + --> tests/ui/manual_div_ceil.rs:70:13 | LL | let _ = (2048_usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:51:13 + --> tests/ui/manual_div_ceil.rs:74:13 | LL | let _ = (x + 4 - 1) / 4; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:54:18 + --> tests/ui/manual_div_ceil.rs:77:18 | LL | let _: u32 = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:56:20 + --> tests/ui/manual_div_ceil.rs:79:20 | LL | let _: usize = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:58:18 + --> tests/ui/manual_div_ceil.rs:81:18 | LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:61:13 + --> tests/ui/manual_div_ceil.rs:84:13 | LL | let _ = (2048 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:64:13 + --> tests/ui/manual_div_ceil.rs:87:13 | LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:70:13 + --> tests/ui/manual_div_ceil.rs:93:13 | LL | let _ = (x + 7) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:72:13 + --> tests/ui/manual_div_ceil.rs:95:13 | LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/manual_is_power_of_two.fixed b/tests/ui/manual_is_power_of_two.fixed index 6f29d76bd21..8a1ab785dfb 100644 --- a/tests/ui/manual_is_power_of_two.fixed +++ b/tests/ui/manual_is_power_of_two.fixed @@ -1,4 +1,17 @@ #![warn(clippy::manual_is_power_of_two)] +#![allow(clippy::precedence)] + +macro_rules! binop { + ($a: expr, equal, $b: expr) => { + $a == $b + }; + ($a: expr, and, $b: expr) => { + $a & $b + }; + ($a: expr, minus, $b: expr) => { + $a - $b + }; +} fn main() { let a = 16_u64; @@ -7,6 +20,8 @@ fn main() { //~^ manual_is_power_of_two let _ = a.is_power_of_two(); //~^ manual_is_power_of_two + let _ = a.is_power_of_two(); + //~^ manual_is_power_of_two // Test different orders of expression let _ = a.is_power_of_two(); @@ -23,4 +38,23 @@ fn main() { // is_power_of_two only works for unsigned integers let _ = b.count_ones() == 1; let _ = b & (b - 1) == 0; + + let i: i32 = 3; + let _ = (i as u32).is_power_of_two(); + //~^ manual_is_power_of_two + + let _ = binop!(a.count_ones(), equal, 1); + let _ = binop!(a, and, a - 1) == 0; + let _ = a & binop!(a, minus, 1) == 0; +} + +#[clippy::msrv = "1.31"] +const fn low_msrv(a: u32) -> bool { + a & (a - 1) == 0 +} + +#[clippy::msrv = "1.32"] +const fn high_msrv(a: u32) -> bool { + a.is_power_of_two() + //~^ manual_is_power_of_two } diff --git a/tests/ui/manual_is_power_of_two.rs b/tests/ui/manual_is_power_of_two.rs index 0c44d7a660b..57a3b05e033 100644 --- a/tests/ui/manual_is_power_of_two.rs +++ b/tests/ui/manual_is_power_of_two.rs @@ -1,10 +1,25 @@ #![warn(clippy::manual_is_power_of_two)] +#![allow(clippy::precedence)] + +macro_rules! binop { + ($a: expr, equal, $b: expr) => { + $a == $b + }; + ($a: expr, and, $b: expr) => { + $a & $b + }; + ($a: expr, minus, $b: expr) => { + $a - $b + }; +} fn main() { let a = 16_u64; let _ = a.count_ones() == 1; //~^ manual_is_power_of_two + let _ = u64::count_ones(a) == 1; + //~^ manual_is_power_of_two let _ = a & (a - 1) == 0; //~^ manual_is_power_of_two @@ -23,4 +38,23 @@ fn main() { // is_power_of_two only works for unsigned integers let _ = b.count_ones() == 1; let _ = b & (b - 1) == 0; + + let i: i32 = 3; + let _ = i as u32 & (i as u32 - 1) == 0; + //~^ manual_is_power_of_two + + let _ = binop!(a.count_ones(), equal, 1); + let _ = binop!(a, and, a - 1) == 0; + let _ = a & binop!(a, minus, 1) == 0; +} + +#[clippy::msrv = "1.31"] +const fn low_msrv(a: u32) -> bool { + a & (a - 1) == 0 +} + +#[clippy::msrv = "1.32"] +const fn high_msrv(a: u32) -> bool { + a & (a - 1) == 0 + //~^ manual_is_power_of_two } diff --git a/tests/ui/manual_is_power_of_two.stderr b/tests/ui/manual_is_power_of_two.stderr index ad12ee10565..5781a093d5f 100644 --- a/tests/ui/manual_is_power_of_two.stderr +++ b/tests/ui/manual_is_power_of_two.stderr @@ -1,5 +1,5 @@ error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:6:13 + --> tests/ui/manual_is_power_of_two.rs:19:13 | LL | let _ = a.count_ones() == 1; | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` @@ -8,34 +8,52 @@ LL | let _ = a.count_ones() == 1; = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:8:13 + --> tests/ui/manual_is_power_of_two.rs:21:13 + | +LL | let _ = u64::count_ones(a) == 1; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:23:13 | LL | let _ = a & (a - 1) == 0; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:12:13 + --> tests/ui/manual_is_power_of_two.rs:27:13 | LL | let _ = 1 == a.count_ones(); | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:14:13 + --> tests/ui/manual_is_power_of_two.rs:29:13 | LL | let _ = (a - 1) & a == 0; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:16:13 + --> tests/ui/manual_is_power_of_two.rs:31:13 | LL | let _ = 0 == a & (a - 1); | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:18:13 + --> tests/ui/manual_is_power_of_two.rs:33:13 | LL | let _ = 0 == (a - 1) & a; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` -error: aborting due to 6 previous errors +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:43:13 + | +LL | let _ = i as u32 & (i as u32 - 1) == 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `(i as u32).is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:58:5 + | +LL | a & (a - 1) == 0 + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index a753566b34c..3781ba1676f 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -514,3 +514,35 @@ mod issue13768 { }; } } + +mod issue14598 { + fn bar() -> Result<bool, &'static str> { + let value = match foo() { + //~^ manual_let_else + Err(_) => return Err("abc"), + Ok(value) => value, + }; + + let w = Some(0); + let v = match w { + //~^ manual_let_else + None => return Err("abc"), + Some(x) => x, + }; + + enum Foo<T> { + Foo(T), + } + + let v = match Foo::Foo(Some(())) { + Foo::Foo(Some(_)) => return Err("abc"), + Foo::Foo(v) => v, + }; + + Ok(value == 42) + } + + fn foo() -> Result<u32, &'static str> { + todo!() + } +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index ef042192114..a1eea041929 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -529,5 +529,25 @@ LL + return; LL + }; | -error: aborting due to 33 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:520:9 + | +LL | / let value = match foo() { +LL | | +LL | | Err(_) => return Err("abc"), +LL | | Ok(value) => value, +LL | | }; + | |__________^ help: consider writing: `let Ok(value) = foo() else { return Err("abc") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:527:9 + | +LL | / let v = match w { +LL | | +LL | | None => return Err("abc"), +LL | | Some(x) => x, +LL | | }; + | |__________^ help: consider writing: `let Some(v) = w else { return Err("abc") };` + +error: aborting due to 35 previous errors diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 9477d0d795d..40133748d45 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -101,7 +101,7 @@ fn main() { match &mut Some(String::new()) { //~^ manual_map - Some(ref x) => Some(x.len()), + &mut Some(ref x) => Some(x.len()), None => None, }; diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 8f9bce4c265..486379c1e5f 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -127,7 +127,7 @@ error: manual implementation of `Option::map` | LL | / match &mut Some(String::new()) { LL | | -LL | | Some(ref x) => Some(x.len()), +LL | | &mut Some(ref x) => Some(x.len()), LL | | None => None, LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` diff --git a/tests/ui/manual_map_option_2.fixed b/tests/ui/manual_map_option_2.fixed index d698cc74ea6..206c6d5d077 100644 --- a/tests/ui/manual_map_option_2.fixed +++ b/tests/ui/manual_map_option_2.fixed @@ -115,7 +115,7 @@ mod with_type_coercion { fn with_fn_ret(s: &Option<String>) -> Option<(String, &str)> { // Don't lint, `map` doesn't work as the return type is adjusted. match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } } @@ -124,7 +124,7 @@ mod with_type_coercion { if true { // Don't lint, `map` doesn't work as the return type is adjusted. return match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, }; } @@ -136,7 +136,7 @@ mod with_type_coercion { let x: Option<(String, &'a str)>; x = { match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } }; diff --git a/tests/ui/manual_map_option_2.rs b/tests/ui/manual_map_option_2.rs index 069c2381f6d..a47dc950760 100644 --- a/tests/ui/manual_map_option_2.rs +++ b/tests/ui/manual_map_option_2.rs @@ -143,7 +143,7 @@ mod with_type_coercion { fn with_fn_ret(s: &Option<String>) -> Option<(String, &str)> { // Don't lint, `map` doesn't work as the return type is adjusted. match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } } @@ -152,7 +152,7 @@ mod with_type_coercion { if true { // Don't lint, `map` doesn't work as the return type is adjusted. return match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, }; } @@ -164,7 +164,7 @@ mod with_type_coercion { let x: Option<(String, &'a str)>; x = { match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } }; diff --git a/tests/ui/manual_ok_err.fixed b/tests/ui/manual_ok_err.fixed index bc169b64be9..e6f799aa58d 100644 --- a/tests/ui/manual_ok_err.fixed +++ b/tests/ui/manual_ok_err.fixed @@ -80,6 +80,11 @@ fn no_lint() { Ok(3) => None, Ok(v) => Some(v), }; + + let _ = match funcall() { + Ok(v @ 1..) => Some(v), + _ => None, + }; } const fn cf(x: Result<u32, &'static str>) -> Option<u32> { diff --git a/tests/ui/manual_ok_err.rs b/tests/ui/manual_ok_err.rs index 03c730d4b4e..972b2c41ee7 100644 --- a/tests/ui/manual_ok_err.rs +++ b/tests/ui/manual_ok_err.rs @@ -116,6 +116,11 @@ fn no_lint() { Ok(3) => None, Ok(v) => Some(v), }; + + let _ = match funcall() { + Ok(v @ 1..) => Some(v), + _ => None, + }; } const fn cf(x: Result<u32, &'static str>) -> Option<u32> { diff --git a/tests/ui/manual_ok_err.stderr b/tests/ui/manual_ok_err.stderr index 13fceacda10..040e170f397 100644 --- a/tests/ui/manual_ok_err.stderr +++ b/tests/ui/manual_ok_err.stderr @@ -94,7 +94,7 @@ LL | | }; | |_____^ help: replace with: `(-S).ok()` error: manual implementation of `ok` - --> tests/ui/manual_ok_err.rs:132:12 + --> tests/ui/manual_ok_err.rs:137:12 | LL | } else if let Ok(n) = "1".parse::<u8>() { | ____________^ diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index ca8491131c0..016f520e216 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_retain)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { @@ -31,7 +31,7 @@ fn binary_heap_retain() { // Do lint, because we use pattern matching let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -99,7 +99,7 @@ fn btree_set_retain() { // Do lint, because we use pattern matching let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -166,7 +166,7 @@ fn hash_set_retain() { // Do lint, because we use pattern matching let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -220,7 +220,7 @@ fn vec_retain() { // Do lint, because we use pattern matching let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index cd05a41f3f2..62f9b7b0595 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_retain)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { @@ -31,7 +31,7 @@ fn binary_heap_retain() { // Do lint, because we use pattern matching let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -103,7 +103,7 @@ fn btree_set_retain() { // Do lint, because we use pattern matching let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -174,7 +174,7 @@ fn hash_set_retain() { // Do lint, because we use pattern matching let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -228,7 +228,7 @@ fn vec_retain() { // Do lint, because we use pattern matching let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index 2f81647dd8b..e7d3e34b5d7 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -22,8 +22,8 @@ LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().colle error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:34:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:36:5 @@ -74,8 +74,8 @@ LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:106:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:108:5 @@ -126,8 +126,8 @@ LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:177:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:179:5 @@ -162,8 +162,8 @@ LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:231:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:233:5 diff --git a/tests/ui/manual_saturating_arithmetic.fixed b/tests/ui/manual_saturating_arithmetic.fixed index 3f73d6e5a1a..304be05f6c4 100644 --- a/tests/ui/manual_saturating_arithmetic.fixed +++ b/tests/ui/manual_saturating_arithmetic.fixed @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.saturating_add(1); //~^ manual_saturating_arithmetic diff --git a/tests/ui/manual_saturating_arithmetic.rs b/tests/ui/manual_saturating_arithmetic.rs index 98246a5cd96..c2b570e974a 100644 --- a/tests/ui/manual_saturating_arithmetic.rs +++ b/tests/ui/manual_saturating_arithmetic.rs @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); //~^ manual_saturating_arithmetic diff --git a/tests/ui/manual_saturating_arithmetic.stderr b/tests/ui/manual_saturating_arithmetic.stderr index 9d133d8a073..2f006a3ae17 100644 --- a/tests/ui/manual_saturating_arithmetic.stderr +++ b/tests/ui/manual_saturating_arithmetic.stderr @@ -1,5 +1,5 @@ error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:6:13 + --> tests/ui/manual_saturating_arithmetic.rs:4:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` @@ -8,19 +8,19 @@ LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); = help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:8:13 + --> tests/ui/manual_saturating_arithmetic.rs:6:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:10:13 + --> tests/ui/manual_saturating_arithmetic.rs:8:13 | LL | let _ = 1u8.checked_add(1).unwrap_or(255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:12:13 + --> tests/ui/manual_saturating_arithmetic.rs:10:13 | LL | let _ = 1u128 | _____________^ @@ -30,49 +30,49 @@ LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:18:13 + --> tests/ui/manual_saturating_arithmetic.rs:16:13 | LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:21:13 + --> tests/ui/manual_saturating_arithmetic.rs:19:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:23:13 + --> tests/ui/manual_saturating_arithmetic.rs:21:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:25:13 + --> tests/ui/manual_saturating_arithmetic.rs:23:13 | LL | let _ = 1u8.checked_sub(1).unwrap_or(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:30:13 + --> tests/ui/manual_saturating_arithmetic.rs:28:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:32:13 + --> tests/ui/manual_saturating_arithmetic.rs:30:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:34:13 + --> tests/ui/manual_saturating_arithmetic.rs:32:13 | LL | let _ = 1i8.checked_add(1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:36:13 + --> tests/ui/manual_saturating_arithmetic.rs:34:13 | LL | let _ = 1i128 | _____________^ @@ -82,25 +82,25 @@ LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:40:13 + --> tests/ui/manual_saturating_arithmetic.rs:38:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:42:13 + --> tests/ui/manual_saturating_arithmetic.rs:40:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:44:13 + --> tests/ui/manual_saturating_arithmetic.rs:42:13 | LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:46:13 + --> tests/ui/manual_saturating_arithmetic.rs:44:13 | LL | let _ = 1i128 | _____________^ @@ -110,25 +110,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:54:13 + --> tests/ui/manual_saturating_arithmetic.rs:52:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:56:13 + --> tests/ui/manual_saturating_arithmetic.rs:54:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:58:13 + --> tests/ui/manual_saturating_arithmetic.rs:56:13 | LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:60:13 + --> tests/ui/manual_saturating_arithmetic.rs:58:13 | LL | let _ = 1i128 | _____________^ @@ -138,25 +138,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:64:13 + --> tests/ui/manual_saturating_arithmetic.rs:62:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:66:13 + --> tests/ui/manual_saturating_arithmetic.rs:64:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:68:13 + --> tests/ui/manual_saturating_arithmetic.rs:66:13 | LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:70:13 + --> tests/ui/manual_saturating_arithmetic.rs:68:13 | LL | let _ = 1i128 | _____________^ diff --git a/tests/ui/manual_slice_fill.fixed b/tests/ui/manual_slice_fill.fixed index bba863247f5..d07d1d60e2c 100644 --- a/tests/ui/manual_slice_fill.fixed +++ b/tests/ui/manual_slice_fill.fixed @@ -123,3 +123,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList<T>(T); + + impl<T> ZipList<T> { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl<T> Index<usize> for ZipList<T> { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl<T> IndexMut<usize> for ZipList<T> { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList<usize>) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} diff --git a/tests/ui/manual_slice_fill.rs b/tests/ui/manual_slice_fill.rs index 44c60dc40f0..c74ab2225c0 100644 --- a/tests/ui/manual_slice_fill.rs +++ b/tests/ui/manual_slice_fill.rs @@ -136,3 +136,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList<T>(T); + + impl<T> ZipList<T> { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl<T> Index<usize> for ZipList<T> { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl<T> IndexMut<usize> for ZipList<T> { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList<usize>) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index f4a78ee7619..41ca44ceef4 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -36,10 +36,12 @@ fn main() { // Issue #12531 unsafe fn no_deref_ptr(a: Option<i32>, b: *const Option<i32>) -> i32 { - match a { - // `*b` being correct depends on `a == Some(_)` - Some(_) => (*b).unwrap_or_default(), - _ => 0, + unsafe { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => (*b).unwrap_or_default(), + _ => 0, + } } } @@ -104,3 +106,16 @@ fn issue_12928() { fn allowed_manual_unwrap_or_zero() -> u32 { Some(42).unwrap_or_default() } + +mod issue14716 { + struct Foo { + name: Option<String>, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index 60b84b621f6..343fbc4879c 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -68,14 +68,16 @@ fn main() { // Issue #12531 unsafe fn no_deref_ptr(a: Option<i32>, b: *const Option<i32>) -> i32 { - match a { - // `*b` being correct depends on `a == Some(_)` - Some(_) => match *b { - //~^ manual_unwrap_or_default - Some(v) => v, + unsafe { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => match *b { + //~^ manual_unwrap_or_default + Some(v) => v, + _ => 0, + }, _ => 0, - }, - _ => 0, + } } } @@ -145,3 +147,16 @@ fn allowed_manual_unwrap_or_zero() -> u32 { 0 } } + +mod issue14716 { + struct Foo { + name: Option<String>, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} diff --git a/tests/ui/manual_unwrap_or_default.stderr b/tests/ui/manual_unwrap_or_default.stderr index 1e92f20a757..e8f38a2e389 100644 --- a/tests/ui/manual_unwrap_or_default.stderr +++ b/tests/ui/manual_unwrap_or_default.stderr @@ -76,18 +76,18 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:73:20 + --> tests/ui/manual_unwrap_or_default.rs:74:24 | -LL | Some(_) => match *b { - | ____________________^ +LL | Some(_) => match *b { + | ________________________^ LL | | -LL | | Some(v) => v, -LL | | _ => 0, -LL | | }, - | |_________^ help: replace it with: `(*b).unwrap_or_default()` +LL | | Some(v) => v, +LL | | _ => 0, +LL | | }, + | |_____________^ help: replace it with: `(*b).unwrap_or_default()` error: if let can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:141:5 + --> tests/ui/manual_unwrap_or_default.rs:143:5 | LL | / if let Some(x) = Some(42) { LL | | diff --git a/tests/ui/match_same_arms.fixed b/tests/ui/match_same_arms.fixed new file mode 100644 index 00000000000..31684a5759f --- /dev/null +++ b/tests/ui/match_same_arms.fixed @@ -0,0 +1,142 @@ +#![allow(clippy::manual_range_patterns)] +#![warn(clippy::match_same_arms)] + +pub enum Abc { + A, + B, + C, +} + +fn match_same_arms() { + let _ = match Abc::A { + Abc::B => 1, + _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + _ => 'b', + //~^ match_same_arms + }; + + match (1, 2, 3) { + (1, .., 3) | (.., 3) => 42, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 42 | 51 => 1, + 41 | 52 => 2, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 1 | 2 | 3 => 2, + 4 => 3, + _ => 0, + }; +} + +mod issue4244 { + #[derive(PartialEq, PartialOrd, Eq, Ord)] + pub enum CommandInfo { + BuiltIn { name: String, about: Option<String> }, + External { name: String, path: std::path::PathBuf }, + } + + impl CommandInfo { + pub fn name(&self) -> String { + match self { + //~^ match_same_arms + CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), + } + } + } +} + +macro_rules! m { + (foo) => {}; + (bar) => {}; +} +macro_rules! foo { + () => { + 1 + }; +} +macro_rules! bar { + () => { + 1 + }; +} + +fn main() { + let x = 0; + let _ = match 0 { + 0 => { + m!(foo); + x + }, + 1 => { + m!(bar); + x + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + m!(foo); + 0 + }, + 1 => { + m!(bar); + 0 + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + let mut x = 0; + #[cfg(not_enabled)] + { + x = 5; + } + #[cfg(not(not_enabled))] + { + x = 6; + } + x + }, + 1 => { + let mut x = 0; + #[cfg(also_not_enabled)] + { + x = 5; + } + #[cfg(not(also_not_enabled))] + { + x = 6; + } + x + }, + _ => 0, + }; + + let _ = match 0 { + 0 => foo!(), + 1 => bar!(), + _ => 1, + }; + + let _ = match 0 { + 0 => cfg!(not_enabled), + 1 => cfg!(also_not_enabled), + _ => false, + }; +} diff --git a/tests/ui/match_same_arms.rs b/tests/ui/match_same_arms.rs index 55441948e91..39bee01bac2 100644 --- a/tests/ui/match_same_arms.rs +++ b/tests/ui/match_same_arms.rs @@ -1,4 +1,4 @@ -//@no-rustfix: overlapping suggestions +#![allow(clippy::manual_range_patterns)] #![warn(clippy::match_same_arms)] pub enum Abc { @@ -10,9 +10,17 @@ pub enum Abc { fn match_same_arms() { let _ = match Abc::A { Abc::A => 0, - //~^ match_same_arms Abc::B => 1, _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + 2 => 'b', + 3 => 'b', + _ => 'b', + //~^ match_same_arms }; match (1, 2, 3) { @@ -24,8 +32,8 @@ fn match_same_arms() { let _ = match 42 { 42 => 1, - 51 => 1, //~^ match_same_arms + 51 => 1, 41 => 2, //~^ match_same_arms 52 => 2, @@ -34,11 +42,9 @@ fn match_same_arms() { let _ = match 42 { 1 => 2, - 2 => 2, //~^ match_same_arms - //~| match_same_arms + 2 => 2, 3 => 2, - //~^ match_same_arms 4 => 3, _ => 0, }; @@ -55,8 +61,8 @@ mod issue4244 { pub fn name(&self) -> String { match self { CommandInfo::BuiltIn { name, .. } => name.to_string(), - CommandInfo::External { name, .. } => name.to_string(), //~^ match_same_arms + CommandInfo::External { name, .. } => name.to_string(), } } } diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr index 3744b83d89c..8aa60f83576 100644 --- a/tests/ui/match_same_arms.stderr +++ b/tests/ui/match_same_arms.stderr @@ -1,118 +1,121 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms.rs:12:9 | -LL | / Abc::A => 0, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms.rs:15:9 - | +LL | Abc::A => 0, + | ^^^^^^^^^^^ +LL | Abc::B => 1, LL | _ => 0, - | ^^^^^^ + | ^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise remove the non-wildcard arm + | +LL - Abc::A => 0, + | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:19:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:20:9 + | +LL | 2 => 'b', + | ^^^^^^^^ +LL | 3 => 'b', + | ^^^^^^^^ +LL | _ => 'b', + | ^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arms + | +LL - 2 => 'b', +LL - 3 => 'b', +LL + _ => 'b', + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:27:9 | LL | (1, .., 3) => 42, | ^^^^^^^^^^^^^^^^ +LL | +LL | (.., 3) => 42, + | ^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (1, .., 3) | (.., 3) => 42, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:27:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:34:9 | +LL | 42 => 1, + | ^^^^^^^ +LL | LL | 51 => 1, | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => 1, -LL - 51 => 1, -LL + 51 | 42 => 1, +LL ~ +LL ~ 42 | 51 => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:29:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:37:9 | LL | 41 => 2, | ^^^^^^^ +LL | +LL | 52 => 2, + | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ 41 | 52 => 2, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 - | -LL | 2 => 2, - | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL - 1 => 2, -LL - 2 => 2, -LL + 2 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:40:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:44:9 | -LL | 3 => 2, +LL | 1 => 2, | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL ~ 2 => 2, -LL | LL | -LL ~ 3 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 - | LL | 2 => 2, | ^^^^^^ +LL | 3 => 2, + | ^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ 2 | 3 => 2, -LL | -LL | LL ~ +LL ~ 1 | 2 | 3 => 2, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:58:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:63:17 | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | CommandInfo::External { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), -LL - CommandInfo::External { name, .. } => name.to_string(), -LL + CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), +LL ~ +LL ~ CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), | -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/match_same_arms2.fixed b/tests/ui/match_same_arms2.fixed index 0d93e2c728d..cb860cef1e6 100644 --- a/tests/ui/match_same_arms2.fixed +++ b/tests/ui/match_same_arms2.fixed @@ -14,7 +14,7 @@ fn foo() -> bool { fn match_same_arms() { let _ = match 42 { - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -27,14 +27,14 @@ fn match_same_arms() { }; let _ = match 42 { - 51 | 42 => foo(), //~^ match_same_arms + 42 | 51 => foo(), _ => true, }; let _ = match Some(42) { - None | Some(_) => 24, //~^ match_same_arms + Some(_) | None => 24, }; let _ = match Some(42) { @@ -55,8 +55,8 @@ fn match_same_arms() { }; match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) => bar(a), //~^ match_same_arms + (Some(a), None) | (None, Some(a)) => bar(a), _ => (), } @@ -69,8 +69,8 @@ fn match_same_arms() { }; let _ = match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) if a == 42 => a, //~^ match_same_arms + (Some(a), None) | (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -124,8 +124,8 @@ fn match_same_arms() { // False negative #2251. match x { Ok(_tmp) => println!("ok"), - Ok(_) | Ok(3) => println!("ok"), //~^ match_same_arms + Ok(3) | Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -149,10 +149,10 @@ fn match_same_arms() { // still lint if the tokens are the same match 0 { - 1 | 0 => { + //~^^^ match_same_arms + 0 | 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -208,9 +208,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { - Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) | Foo::X(0) => 1, //~^ match_same_arms + Foo::Y(_) | Foo::Z(0) => 2, + Foo::X(0) | Foo::Z(_) => 1, _ => 0, }; @@ -230,10 +230,10 @@ fn main() { // Lint. let _ = match None { + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, - Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, - //~^ match_same_arms + Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, _ => 200, }; @@ -246,8 +246,8 @@ fn main() { }; let _ = match 0 { - 1 | 0 => cfg!(not_enable), //~^ match_same_arms + 0 | 1 => cfg!(not_enable), _ => false, }; } @@ -262,9 +262,34 @@ mod with_lifetime { impl<'a> MaybeStaticStr<'a> { fn get(&self) -> &'a str { match *self { - MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, //~^ match_same_arms + MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index b0ebc4784f3..0fd5d76e7d3 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -23,7 +23,7 @@ fn match_same_arms() { a = -31 - a; a }, - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -37,15 +37,15 @@ fn match_same_arms() { let _ = match 42 { 42 => foo(), - 51 => foo(), //~^ match_same_arms + 51 => foo(), _ => true, }; let _ = match Some(42) { Some(_) => 24, - None => 24, //~^ match_same_arms + None => 24, }; let _ = match Some(42) { @@ -67,8 +67,8 @@ fn match_same_arms() { match (Some(42), Some(42)) { (Some(a), None) => bar(a), - (None, Some(a)) => bar(a), //~^ match_same_arms + (None, Some(a)) => bar(a), _ => (), } @@ -82,8 +82,8 @@ fn match_same_arms() { let _ = match (Some(42), Some(42)) { (Some(a), None) if a == 42 => a, - (None, Some(a)) if a == 42 => a, //~^ match_same_arms + (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -140,8 +140,8 @@ fn match_same_arms() { match x { Ok(_tmp) => println!("ok"), Ok(3) => println!("ok"), - Ok(_) => println!("ok"), //~^ match_same_arms + Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -168,10 +168,10 @@ fn match_same_arms() { 0 => { empty!(0); }, + //~^^^ match_same_arms 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -229,9 +229,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { Foo::X(0) => 1, + //~^ match_same_arms Foo::Y(_) | Foo::Z(0) => 2, Foo::Z(_) => 1, - //~^ match_same_arms _ => 0, }; @@ -252,10 +252,10 @@ fn main() { // Lint. let _ = match None { Some(Bar { x: 0, y: 5, .. }) => 1, + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, Some(Bar { y: 0, x: 5, .. }) => 1, - //~^ match_same_arms _ => 200, }; @@ -269,8 +269,8 @@ fn main() { let _ = match 0 { 0 => cfg!(not_enable), - 1 => cfg!(not_enable), //~^ match_same_arms + 1 => cfg!(not_enable), _ => false, }; } @@ -286,9 +286,36 @@ mod with_lifetime { fn get(&self) -> &'a str { match *self { MaybeStaticStr::Static(s) => s, - MaybeStaticStr::Borrowed(s) => s, //~^ match_same_arms + MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 21a8743cc32..f3031619cce 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,4 +1,4 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:17:9 | LL | / 42 => { @@ -6,14 +6,10 @@ LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; LL | | if true { ... | +LL | | a LL | | }, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:27:9 - | + | |_________^ +LL | LL | / _ => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; @@ -21,134 +17,169 @@ LL | | if true { ... | LL | | a LL | | }, - | |_________^ + | |_________^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise remove the non-wildcard arm + | +LL - 42 => { +LL - foo(); +LL - let mut a = 42 + [23].len() as i32; +LL - if true { +LL - a += 7; +LL - } +LL - a = -31 - a; +LL - a +LL - }, + | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:40:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:39:9 | +LL | 42 => foo(), + | ^^^^^^^^^^^ +LL | LL | 51 => foo(), | ^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => foo(), -LL - 51 => foo(), -LL + 51 | 42 => foo(), +LL ~ +LL ~ 42 | 51 => foo(), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:47:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:46:9 | +LL | Some(_) => 24, + | ^^^^^^^^^^^^^ +LL | LL | None => 24, | ^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Some(_) => 24, -LL - None => 24, -LL + None | Some(_) => 24, +LL ~ +LL ~ Some(_) | None => 24, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:70:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:69:9 | +LL | (Some(a), None) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) => bar(a), -LL - (None, Some(a)) => bar(a), -LL + (None, Some(a)) | (Some(a), None) => bar(a), +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) => bar(a), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:85:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:84:9 | +LL | (Some(a), None) if a == 42 => a, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) if a == 42 => a, -LL - (None, Some(a)) if a == 42 => a, -LL + (None, Some(a)) | (Some(a), None) if a == 42 => a, +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) if a == 42 => a, | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:91:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (.., Some(a)) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Some(a), ..) | (.., Some(a)) => bar(a), LL | LL ~ _ => (), | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:126:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), LL | LL ~ _ => println!("err"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:143:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:142:9 | +LL | Ok(3) => println!("ok"), + | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | Ok(_) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Ok(3) => println!("ok"), -LL - Ok(_) => println!("ok"), -LL + Ok(_) | Ok(3) => println!("ok"), +LL ~ +LL ~ Ok(3) | Ok(_) => println!("ok"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:171:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:168:9 | +LL | / 0 => { +LL | | empty!(0); +LL | | }, + | |_________^ +LL | LL | / 1 => { LL | | empty!(0); LL | | }, | |_________^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => { -LL - empty!(0); -LL - }, -LL - 1 => { -LL + 1 | 0 => { +LL ~ +LL ~ 0 | 1 => { | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:222:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ +... +LL | Foo::Z(_) => 1, + | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ Foo::X(0) | Foo::Z(_) => 1, LL | @@ -156,60 +187,106 @@ LL | Foo::X(_) | Foo::Y(_) => 2, LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:233:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:231:9 | +LL | Foo::X(0) => 1, + | ^^^^^^^^^^^^^^ +... LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Foo::Y(_) | Foo::Z(0) => 2, -LL ~ Foo::Z(_) | Foo::X(0) => 1, +LL ~ +LL | Foo::Y(_) | Foo::Z(0) => 2, +LL ~ Foo::X(0) | Foo::Z(_) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:257:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:254:9 | +LL | Some(Bar { x: 0, y: 5, .. }) => 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Some(Bar { y: 10, z: 0, .. }) => 2, +LL ~ +LL | Some(Bar { y: 10, z: 0, .. }) => 2, LL | None => 50, -LL ~ Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, +LL ~ Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:272:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:271:9 | +LL | 0 => cfg!(not_enable), + | ^^^^^^^^^^^^^^^^^^^^^ +LL | LL | 1 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => cfg!(not_enable), -LL - 1 => cfg!(not_enable), -LL + 1 | 0 => cfg!(not_enable), +LL ~ +LL ~ 0 | 1 => cfg!(not_enable), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:289:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:288:17 | +LL | MaybeStaticStr::Static(s) => s, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | MaybeStaticStr::Borrowed(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, | -LL - MaybeStaticStr::Static(s) => s, -LL - MaybeStaticStr::Borrowed(s) => s, -LL + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:306:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[allow(clippy::match_same_arms)] + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:315:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[expect(clippy::match_same_arms)] | -error: aborting due to 14 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/match_same_arms_non_exhaustive.fixed b/tests/ui/match_same_arms_non_exhaustive.fixed index 0c9398933b8..61a5bd0323a 100644 --- a/tests/ui/match_same_arms_non_exhaustive.fixed +++ b/tests/ui/match_same_arms_non_exhaustive.fixed @@ -7,14 +7,22 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } @@ -28,21 +36,21 @@ mod f { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -54,8 +62,8 @@ mod g { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/tests/ui/match_same_arms_non_exhaustive.rs b/tests/ui/match_same_arms_non_exhaustive.rs index 304a9e5c28e..66f65eb39d0 100644 --- a/tests/ui/match_same_arms_non_exhaustive.rs +++ b/tests/ui/match_same_arms_non_exhaustive.rs @@ -7,15 +7,25 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel => repeat(), + //~^ match_same_arms + Ordering::SeqCst | _ => repeat(), + } } mod f { @@ -29,12 +39,13 @@ mod f { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { @@ -42,8 +53,8 @@ pub fn g(x: Ordering) { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -56,8 +67,8 @@ mod g { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/tests/ui/match_same_arms_non_exhaustive.stderr b/tests/ui/match_same_arms_non_exhaustive.stderr index aa7f8c95dce..03252f346c6 100644 --- a/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,32 +1,80 @@ -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:44:9 - | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:46:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:16:9 | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:58:13 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:25:9 + | +LL | Ordering::AcqRel => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | Ordering::SeqCst | _ => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |____________^ help: try removing the arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:60:13 +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:41:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:55:9 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:69:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 3a3eee4c958..bdf39796ebf 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -171,3 +171,20 @@ fn issue_10447() -> usize { 2 } + +fn issue14634() { + macro_rules! id { + ($i:ident) => { + $i + }; + } + dbg!(3); + println!("here"); + //~^^^ match_single_binding + let id!(a) = dbg!(3); + println!("found {a}"); + //~^^^ match_single_binding + let id!(b) = dbg!(3); + let id!(_a) = dbg!(b + 1); + //~^^^ match_single_binding +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index ada51254c6c..419ff95d873 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -229,3 +229,23 @@ fn issue_10447() -> usize { 2 } + +fn issue14634() { + macro_rules! id { + ($i:ident) => { + $i + }; + } + match dbg!(3) { + _ => println!("here"), + } + //~^^^ match_single_binding + match dbg!(3) { + id!(a) => println!("found {a}"), + } + //~^^^ match_single_binding + let id!(_a) = match dbg!(3) { + id!(b) => dbg!(b + 1), + }; + //~^^^ match_single_binding +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 7e1ec32dac2..bdd0134a5f1 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -336,5 +336,47 @@ LL | | _ => println!("1"), LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` -error: aborting due to 24 previous errors +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:239:5 + | +LL | / match dbg!(3) { +LL | | _ => println!("here"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ dbg!(3); +LL + println!("here"); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:243:5 + | +LL | / match dbg!(3) { +LL | | id!(a) => println!("found {a}"), +LL | | } + | |_____^ + | +help: consider using a `let` statement + | +LL ~ let id!(a) = dbg!(3); +LL + println!("found {a}"); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:247:5 + | +LL | / let id!(_a) = match dbg!(3) { +LL | | id!(b) => dbg!(b + 1), +LL | | }; + | |______^ + | +help: consider using a `let` statement + | +LL ~ let id!(b) = dbg!(3); +LL + let id!(_a) = dbg!(b + 1); + | + +error: aborting due to 27 previous errors diff --git a/tests/ui/misnamed_getters.fixed b/tests/ui/misnamed_getters.fixed index cada5307b1c..bc123d1a40b 100644 --- a/tests/ui/misnamed_getters.fixed +++ b/tests/ui/misnamed_getters.fixed @@ -54,63 +54,63 @@ impl B { unsafe fn a(&self) -> &u8 { //~^ misnamed_getters - &self.a + unsafe { &self.a } } unsafe fn a_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn b(self) -> u8 { //~^ misnamed_getters - self.b + unsafe { self.b } } unsafe fn b_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn c(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } unsafe fn a_unchecked(&self) -> &u8 { //~^ misnamed_getters - &self.a + unsafe { &self.a } } unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn b_unchecked(self) -> u8 { //~^ misnamed_getters - self.b + unsafe { self.b } } unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn c_unchecked(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } } diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs index f529c56b471..6590101157c 100644 --- a/tests/ui/misnamed_getters.rs +++ b/tests/ui/misnamed_getters.rs @@ -54,63 +54,63 @@ impl B { unsafe fn a(&self) -> &u8 { //~^ misnamed_getters - &self.b + unsafe { &self.b } } unsafe fn a_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn b(self) -> u8 { //~^ misnamed_getters - self.a + unsafe { self.a } } unsafe fn b_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn c(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } unsafe fn a_unchecked(&self) -> &u8 { //~^ misnamed_getters - &self.b + unsafe { &self.b } } unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn b_unchecked(self) -> u8 { //~^ misnamed_getters - self.a + unsafe { self.a } } unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn c_unchecked(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } } diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr index 5dd1d75bcf6..aaf21cecb92 100644 --- a/tests/ui/misnamed_getters.stderr +++ b/tests/ui/misnamed_getters.stderr @@ -73,8 +73,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a(&self) -> &u8 { LL | | LL | | -LL | | &self.b - | | ------- help: consider using: `&self.a` +LL | | unsafe { &self.b } + | | ------- help: consider using: `&self.a` LL | | } | |_____^ @@ -84,8 +84,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.b - | | ----------- help: consider using: `&mut self.a` +LL | | unsafe { &mut self.b } + | | ----------- help: consider using: `&mut self.a` LL | | } | |_____^ @@ -95,8 +95,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b(self) -> u8 { LL | | LL | | -LL | | self.a - | | ------ help: consider using: `self.b` +LL | | unsafe { self.a } + | | ------ help: consider using: `self.b` LL | | } | |_____^ @@ -106,8 +106,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.a - | | ----------- help: consider using: `&mut self.b` +LL | | unsafe { &mut self.a } + | | ----------- help: consider using: `&mut self.b` LL | | } | |_____^ @@ -117,8 +117,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | LL | | -LL | | &self.b - | | ------- help: consider using: `&self.a` +LL | | unsafe { &self.b } + | | ------- help: consider using: `&self.a` LL | | } | |_____^ @@ -128,8 +128,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.b - | | ----------- help: consider using: `&mut self.a` +LL | | unsafe { &mut self.b } + | | ----------- help: consider using: `&mut self.a` LL | | } | |_____^ @@ -139,8 +139,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_unchecked(self) -> u8 { LL | | LL | | -LL | | self.a - | | ------ help: consider using: `self.b` +LL | | unsafe { self.a } + | | ------ help: consider using: `self.b` LL | | } | |_____^ @@ -150,8 +150,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.a - | | ----------- help: consider using: `&mut self.b` +LL | | unsafe { &mut self.a } + | | ----------- help: consider using: `&mut self.b` LL | | } | |_____^ diff --git a/tests/ui/misnamed_getters_2021.fixed b/tests/ui/misnamed_getters_2021.fixed new file mode 100644 index 00000000000..7112719a9f2 --- /dev/null +++ b/tests/ui/misnamed_getters_2021.fixed @@ -0,0 +1,24 @@ +//@edition: 2021 +#![allow(unused)] +#![allow(clippy::struct_field_names)] +#![warn(clippy::misnamed_getters)] + +// Edition 2021 specific check, where `unsafe` blocks are not required +// inside `unsafe fn`. + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + //~^ misnamed_getters + + &self.a + } +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/misnamed_getters_2021.rs b/tests/ui/misnamed_getters_2021.rs new file mode 100644 index 00000000000..19b5d086041 --- /dev/null +++ b/tests/ui/misnamed_getters_2021.rs @@ -0,0 +1,24 @@ +//@edition: 2021 +#![allow(unused)] +#![allow(clippy::struct_field_names)] +#![warn(clippy::misnamed_getters)] + +// Edition 2021 specific check, where `unsafe` blocks are not required +// inside `unsafe fn`. + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + //~^ misnamed_getters + + &self.b + } +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/misnamed_getters_2021.stderr b/tests/ui/misnamed_getters_2021.stderr new file mode 100644 index 00000000000..5495e2e3733 --- /dev/null +++ b/tests/ui/misnamed_getters_2021.stderr @@ -0,0 +1,16 @@ +error: getter function appears to return the wrong field + --> tests/ui/misnamed_getters_2021.rs:15:5 + | +LL | / unsafe fn a(&self) -> &u8 { +LL | | +LL | | +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + | + = note: `-D clippy::misnamed-getters` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/missing_asserts_for_indexing.fixed b/tests/ui/missing_asserts_for_indexing.fixed index 6e803322f65..9018f38100e 100644 --- a/tests/ui/missing_asserts_for_indexing.fixed +++ b/tests/ui/missing_asserts_for_indexing.fixed @@ -149,4 +149,21 @@ fn highest_index_first(v1: &[u8]) { let _ = v1[2] + v1[1] + v1[0]; } +fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert!(v1.len() == 3); + assert_eq!(v2.len(), 4); + assert!(v3.len() == 3); + assert_eq!(4, v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing + + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ missing_asserts_for_indexing + + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing.rs b/tests/ui/missing_asserts_for_indexing.rs index 4614a8ef5d0..44c5eddf3d8 100644 --- a/tests/ui/missing_asserts_for_indexing.rs +++ b/tests/ui/missing_asserts_for_indexing.rs @@ -149,4 +149,21 @@ fn highest_index_first(v1: &[u8]) { let _ = v1[2] + v1[1] + v1[0]; } +fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert_eq!(v1.len(), 2); + assert_eq!(v2.len(), 4); + assert_eq!(2, v3.len()); + assert_eq!(4, v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing + + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ missing_asserts_for_indexing + + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing.stderr b/tests/ui/missing_asserts_for_indexing.stderr index 5d30920ccf5..b610de94b53 100644 --- a/tests/ui/missing_asserts_for_indexing.stderr +++ b/tests/ui/missing_asserts_for_indexing.stderr @@ -301,5 +301,57 @@ LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 11 previous errors +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:158:13 + | +LL | assert_eq!(v1.len(), 2); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)` +... +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:21 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:29 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:163:13 + | +LL | assert_eq!(2, v3.len()); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)` +... +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:13 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:21 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:29 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 13 previous errors diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.rs b/tests/ui/missing_asserts_for_indexing_unfixable.rs index 2fac9d7a59c..eb98969efa4 100644 --- a/tests/ui/missing_asserts_for_indexing_unfixable.rs +++ b/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -79,4 +79,11 @@ fn assert_after_indexing(v1: &[u8]) { assert!(v1.len() > 2); } +fn issue14255(v1: &[u8]) { + assert_ne!(v1.len(), 2); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing +} + fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/tests/ui/missing_asserts_for_indexing_unfixable.stderr index 1674861c9ed..a17ad023213 100644 --- a/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -199,5 +199,29 @@ LL | let _ = v1[1] + v1[2]; | ^^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 9 previous errors +error: indexing into a slice multiple times without an `assert` + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:21 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:29 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 10 previous errors diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index 10df44e73b8..65eb2d5938b 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -144,7 +144,7 @@ mod msrv { #[clippy::msrv = "1.62"] mod with_extern { - const extern "C" fn c() {} + const unsafe extern "C" fn c() {} //~^ missing_const_for_fn #[rustfmt::skip] @@ -153,7 +153,7 @@ mod msrv { //~^ missing_const_for_fn // any item functions in extern block won't trigger this lint - extern "C" { + unsafe extern "C" { fn c_in_block(); } } diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index bc44b34daef..3690d2f799f 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -144,7 +144,7 @@ mod msrv { #[clippy::msrv = "1.62"] mod with_extern { - extern "C" fn c() {} + unsafe extern "C" fn c() {} //~^ missing_const_for_fn #[rustfmt::skip] @@ -153,7 +153,7 @@ mod msrv { //~^ missing_const_for_fn // any item functions in extern block won't trigger this lint - extern "C" { + unsafe extern "C" { fn c_in_block(); } } diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 5df5a54ff52..10e07d12f5a 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -212,12 +212,12 @@ 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:147:9 | -LL | extern "C" fn c() {} - | ^^^^^^^^^^^^^^^^^^^^ +LL | unsafe extern "C" fn c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `const` | -LL | const extern "C" fn c() {} +LL | const unsafe extern "C" fn c() {} | +++++ error: this could be a `const fn` diff --git a/tests/ui/missing_transmute_annotations.fixed b/tests/ui/missing_transmute_annotations.fixed index a3c94ab139e..58faeaee09d 100644 --- a/tests/ui/missing_transmute_annotations.fixed +++ b/tests/ui/missing_transmute_annotations.fixed @@ -18,8 +18,10 @@ fn bar(x: i32) -> i32 { } unsafe fn foo1() -> i32 { - // Should not warn! - std::mem::transmute([1u16, 2u16]) + unsafe { + // Should not warn! + std::mem::transmute([1u16, 2u16]) + } } // Should not warn! @@ -31,33 +33,35 @@ enum Foo { } unsafe fn foo2() -> i32 { - let mut i: i32 = 0; - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations + unsafe { + let mut i: i32 = 0; + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations - let x: i32 = bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations - bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations + let x: i32 = bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations - i = local_bad_transmute!([1u16, 2u16]); + i = local_bad_transmute!([1u16, 2u16]); - // Should not warn. - i = bad_transmute!([1u16, 2u16]); + // Should not warn. + i = bad_transmute!([1u16, 2u16]); - i = std::mem::transmute::<[i16; 2], i32>([0i16, 0i16]); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[i16; 2], i32>([0i16, 0i16]); + //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<Foo, i32>(Foo::A); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<Foo, i32>(Foo::A); + //~^ ERROR: transmute used without annotations - i + i + } } fn main() { diff --git a/tests/ui/missing_transmute_annotations.rs b/tests/ui/missing_transmute_annotations.rs index c12e1b0f8d2..c9a4c5fa83b 100644 --- a/tests/ui/missing_transmute_annotations.rs +++ b/tests/ui/missing_transmute_annotations.rs @@ -18,8 +18,10 @@ fn bar(x: i32) -> i32 { } unsafe fn foo1() -> i32 { - // Should not warn! - std::mem::transmute([1u16, 2u16]) + unsafe { + // Should not warn! + std::mem::transmute([1u16, 2u16]) + } } // Should not warn! @@ -31,33 +33,35 @@ enum Foo { } unsafe fn foo2() -> i32 { - let mut i: i32 = 0; - i = std::mem::transmute([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<_, _>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<_, i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations + unsafe { + let mut i: i32 = 0; + i = std::mem::transmute([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations - let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations - bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations + let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations - i = local_bad_transmute!([1u16, 2u16]); + i = local_bad_transmute!([1u16, 2u16]); - // Should not warn. - i = bad_transmute!([1u16, 2u16]); + // Should not warn. + i = bad_transmute!([1u16, 2u16]); - i = std::mem::transmute([0i16, 0i16]); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute([0i16, 0i16]); + //~^ ERROR: transmute used without annotations - i = std::mem::transmute(Foo::A); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute(Foo::A); + //~^ ERROR: transmute used without annotations - i + i + } } fn main() { diff --git a/tests/ui/missing_transmute_annotations.stderr b/tests/ui/missing_transmute_annotations.stderr index 5903ed488ef..63f7e28ee7d 100644 --- a/tests/ui/missing_transmute_annotations.stderr +++ b/tests/ui/missing_transmute_annotations.stderr @@ -1,41 +1,41 @@ error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:35:19 + --> tests/ui/missing_transmute_annotations.rs:38:23 | -LL | i = std::mem::transmute([1u16, 2u16]); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute([1u16, 2u16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` | = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:37:19 + --> tests/ui/missing_transmute_annotations.rs:40:23 | -LL | i = std::mem::transmute::<_, _>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<_, _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:39:19 + --> tests/ui/missing_transmute_annotations.rs:42:23 | -LL | i = std::mem::transmute::<_, i32>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<_, i32>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:41:19 + --> tests/ui/missing_transmute_annotations.rs:44:23 | -LL | i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:44:32 + --> tests/ui/missing_transmute_annotations.rs:47:36 | -LL | let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:46:19 + --> tests/ui/missing_transmute_annotations.rs:49:23 | -LL | bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations --> tests/ui/missing_transmute_annotations.rs:11:19 @@ -43,31 +43,31 @@ error: transmute used without annotations LL | std::mem::transmute($e) | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` ... -LL | i = local_bad_transmute!([1u16, 2u16]); - | ---------------------------------- in this macro invocation +LL | i = local_bad_transmute!([1u16, 2u16]); + | ---------------------------------- in this macro invocation | = note: this error originates in the macro `local_bad_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:54:19 + --> tests/ui/missing_transmute_annotations.rs:57:23 | -LL | i = std::mem::transmute([0i16, 0i16]); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[i16; 2], i32>` +LL | i = std::mem::transmute([0i16, 0i16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[i16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:57:19 + --> tests/ui/missing_transmute_annotations.rs:60:23 | -LL | i = std::mem::transmute(Foo::A); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<Foo, i32>` +LL | i = std::mem::transmute(Foo::A); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<Foo, i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:64:35 + --> tests/ui/missing_transmute_annotations.rs:68:35 | LL | let x: _ = unsafe { std::mem::transmute::<_, i32>([1u16, 2u16]) }; | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:67:30 + --> tests/ui/missing_transmute_annotations.rs:71:30 | LL | let x: _ = std::mem::transmute::<_, i32>([1u16, 2u16]); | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index b5d356a5021..4c1d6b1ccb5 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -88,11 +88,13 @@ static mut COUNTER: usize = 0; /// /// Don't ever call this from multiple threads pub unsafe fn mutates_static() -> usize { - COUNTER += 1; - COUNTER + unsafe { + COUNTER += 1; + COUNTER + } } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/tests/ui/must_use_candidates.rs b/tests/ui/must_use_candidates.rs index 14ea16662fd..71d546718ae 100644 --- a/tests/ui/must_use_candidates.rs +++ b/tests/ui/must_use_candidates.rs @@ -88,11 +88,13 @@ static mut COUNTER: usize = 0; /// /// Don't ever call this from multiple threads pub unsafe fn mutates_static() -> usize { - COUNTER += 1; - COUNTER + unsafe { + COUNTER += 1; + COUNTER + } } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index b8c10f3eeb8..1b0b351518c 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -1,4 +1,10 @@ -#![allow(unused, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut)] +#![allow( + unused, + clippy::needless_lifetimes, + clippy::needless_pass_by_ref_mut, + clippy::redundant_allocation, + clippy::boxed_local +)] #![warn(clippy::mut_from_ref)] struct Foo; @@ -40,6 +46,18 @@ fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { unsafe { unimplemented!() } } +fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + //~^ mut_from_ref + + unsafe { unimplemented!() } +} + +fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + //~^ mut_from_ref + + unsafe { unimplemented!() } +} + // this is OK, because the result borrows y fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { unsafe { unimplemented!() } @@ -50,6 +68,20 @@ fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { unsafe { unimplemented!() } } +fn works_tuples<'a>(x: (&'a u32, &'a mut u32)) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +fn works_box<'a>(x: &'a u32, y: Box<&'a mut u32>) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +struct RefMut<'a>(&'a mut u32); + +fn works_parameter<'a>(x: &'a u32, y: RefMut<'a>) -> &'a mut u32 { + unsafe { unimplemented!() } +} + unsafe fn also_broken(x: &u32) -> &mut u32 { //~^ mut_from_ref diff --git a/tests/ui/mut_from_ref.stderr b/tests/ui/mut_from_ref.stderr index 8c3c8e0c3d8..09742687346 100644 --- a/tests/ui/mut_from_ref.stderr +++ b/tests/ui/mut_from_ref.stderr @@ -1,11 +1,11 @@ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:7:39 + --> tests/ui/mut_from_ref.rs:13:39 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:7:29 + --> tests/ui/mut_from_ref.rs:13:29 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^ @@ -13,64 +13,88 @@ LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { = help: to override `-D warnings` add `#[allow(clippy::mut_from_ref)]` error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:15:25 + --> tests/ui/mut_from_ref.rs:21:25 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:15:16 + --> tests/ui/mut_from_ref.rs:21:16 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:25:21 + --> tests/ui/mut_from_ref.rs:31:21 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:25:12 + --> tests/ui/mut_from_ref.rs:31:12 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:31:50 + --> tests/ui/mut_from_ref.rs:37:50 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:31:25 + --> tests/ui/mut_from_ref.rs:37:25 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:37:67 + --> tests/ui/mut_from_ref.rs:43:67 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:37:27 + --> tests/ui/mut_from_ref.rs:43:27 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:53:35 + --> tests/ui/mut_from_ref.rs:49:46 + | +LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> tests/ui/mut_from_ref.rs:49:24 + | +LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + | ^^^^^^^ ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> tests/ui/mut_from_ref.rs:55:37 + | +LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> tests/ui/mut_from_ref.rs:55:24 + | +LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + | ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> tests/ui/mut_from_ref.rs:85:35 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:53:26 + --> tests/ui/mut_from_ref.rs:85:26 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index e4504bc2784..84924cac62d 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -89,7 +89,7 @@ fn should_not_lint( tuple_struct: TupleStruct, s: Struct, ) { - if let [ref a] = slice {} + if let [a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index 7edfda60b97..280cef43340 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -89,7 +89,7 @@ fn should_not_lint( tuple_struct: TupleStruct, s: Struct, ) { - if let [ref a] = slice {} + if let [a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 6551fa56b42..b09efe9888f 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -126,3 +126,87 @@ fn main() { fn foo(_: impl IntoIterator<Item = usize>) {} fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {} fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {} + +mod issue9191 { + use std::cell::Cell; + use std::collections::HashSet; + use std::hash::Hash; + use std::marker::PhantomData; + use std::ops::Deref; + + fn captures_ref_mut(xs: Vec<i32>, mut ys: HashSet<i32>) { + if xs.iter().map(|x| ys.remove(x)).collect::<Vec<_>>().contains(&true) { + todo!() + } + } + + #[derive(Debug, Clone)] + struct MyRef<'a>(PhantomData<&'a mut Cell<HashSet<i32>>>, *mut Cell<HashSet<i32>>); + + impl MyRef<'_> { + fn new(target: &mut Cell<HashSet<i32>>) -> Self { + MyRef(PhantomData, target) + } + + fn get(&mut self) -> &mut Cell<HashSet<i32>> { + unsafe { &mut *self.1 } + } + } + + fn captures_phantom(xs: Vec<i32>, mut ys: Cell<HashSet<i32>>) { + let mut ys_ref = MyRef::new(&mut ys); + if xs + .iter() + .map({ + let mut ys_ref = ys_ref.clone(); + move |x| ys_ref.get().get_mut().remove(x) + }) + .collect::<Vec<_>>() + .contains(&true) + { + todo!() + } + } +} + +pub fn issue8055(v: impl IntoIterator<Item = i32>) -> Result<impl Iterator<Item = i32>, usize> { + let mut zeros = 0; + + let res: Vec<_> = v + .into_iter() + .filter(|i| { + if *i == 0 { + zeros += 1 + }; + *i != 0 + }) + .collect(); + + if zeros != 0 { + return Err(zeros); + } + Ok(res.into_iter()) +} + +mod issue8055_regression { + struct Foo<T> { + inner: T, + marker: core::marker::PhantomData<Self>, + } + + impl<T: Iterator> Iterator for Foo<T> { + type Item = T::Item; + fn next(&mut self) -> Option<Self::Item> { + self.inner.next() + } + } + + fn foo() { + Foo { + inner: [].iter(), + marker: core::marker::PhantomData, + } + .collect::<Vec<&i32>>() + .len(); + } +} diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 973c41c6875..da4182966bb 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -126,3 +126,87 @@ fn main() { fn foo(_: impl IntoIterator<Item = usize>) {} fn bar<I: IntoIterator<Item = usize>>(_: Vec<usize>, _: I) {} fn baz<I: IntoIterator<Item = usize>>(_: I, _: (), _: impl IntoIterator<Item = char>) {} + +mod issue9191 { + use std::cell::Cell; + use std::collections::HashSet; + use std::hash::Hash; + use std::marker::PhantomData; + use std::ops::Deref; + + fn captures_ref_mut(xs: Vec<i32>, mut ys: HashSet<i32>) { + if xs.iter().map(|x| ys.remove(x)).collect::<Vec<_>>().contains(&true) { + todo!() + } + } + + #[derive(Debug, Clone)] + struct MyRef<'a>(PhantomData<&'a mut Cell<HashSet<i32>>>, *mut Cell<HashSet<i32>>); + + impl MyRef<'_> { + fn new(target: &mut Cell<HashSet<i32>>) -> Self { + MyRef(PhantomData, target) + } + + fn get(&mut self) -> &mut Cell<HashSet<i32>> { + unsafe { &mut *self.1 } + } + } + + fn captures_phantom(xs: Vec<i32>, mut ys: Cell<HashSet<i32>>) { + let mut ys_ref = MyRef::new(&mut ys); + if xs + .iter() + .map({ + let mut ys_ref = ys_ref.clone(); + move |x| ys_ref.get().get_mut().remove(x) + }) + .collect::<Vec<_>>() + .contains(&true) + { + todo!() + } + } +} + +pub fn issue8055(v: impl IntoIterator<Item = i32>) -> Result<impl Iterator<Item = i32>, usize> { + let mut zeros = 0; + + let res: Vec<_> = v + .into_iter() + .filter(|i| { + if *i == 0 { + zeros += 1 + }; + *i != 0 + }) + .collect(); + + if zeros != 0 { + return Err(zeros); + } + Ok(res.into_iter()) +} + +mod issue8055_regression { + struct Foo<T> { + inner: T, + marker: core::marker::PhantomData<Self>, + } + + impl<T: Iterator> Iterator for Foo<T> { + type Item = T::Item; + fn next(&mut self) -> Option<Self::Item> { + self.inner.next() + } + } + + fn foo() { + Foo { + inner: [].iter(), + marker: core::marker::PhantomData, + } + .collect::<Vec<&i32>>() + .len(); + } +} diff --git a/tests/ui/needless_if.fixed b/tests/ui/needless_if.fixed index 6208ca19b82..c839156bed9 100644 --- a/tests/ui/needless_if.fixed +++ b/tests/ui/needless_if.fixed @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, @@ -46,9 +45,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s ({ //~^ needless_if diff --git a/tests/ui/needless_if.rs b/tests/ui/needless_if.rs index b459ff877be..11103af5c55 100644 --- a/tests/ui/needless_if.rs +++ b/tests/ui/needless_if.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, @@ -46,9 +45,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s if { //~^ needless_if diff --git a/tests/ui/needless_if.stderr b/tests/ui/needless_if.stderr index eeb8d044526..4b56843bd52 100644 --- a/tests/ui/needless_if.stderr +++ b/tests/ui/needless_if.stderr @@ -1,5 +1,5 @@ error: this `if` branch is empty - --> tests/ui/needless_if.rs:27:5 + --> tests/ui/needless_if.rs:26:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it @@ -8,13 +8,13 @@ LL | if (true) {} = help: to override `-D warnings` add `#[allow(clippy::needless_if)]` error: this `if` branch is empty - --> tests/ui/needless_if.rs:30:5 + --> tests/ui/needless_if.rs:29:5 | LL | if maybe_side_effect() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();` error: this `if` branch is empty - --> tests/ui/needless_if.rs:36:5 + --> tests/ui/needless_if.rs:35:5 | LL | / if { LL | | @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:53:5 + --> tests/ui/needless_if.rs:50:5 | LL | / if { LL | | @@ -57,19 +57,19 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:98:5 + --> tests/ui/needless_if.rs:95:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_if.rs:101:5 + --> tests/ui/needless_if.rs:98:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:106:5 + --> tests/ui/needless_if.rs:103:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index 391d4bc3fcc..b686a8e9f1a 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, @@ -246,9 +244,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 6096e8300e1..23772ff7029 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, @@ -246,9 +244,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index e7c36136847..e3e25cdc8d7 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> tests/ui/needless_late_init.rs:27:5 + --> tests/ui/needless_late_init.rs:25:5 | LL | let a; | ^^^^^^ created here @@ -17,7 +17,7 @@ LL ~ let a = "zero"; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:31:5 + --> tests/ui/needless_late_init.rs:29:5 | LL | let b; | ^^^^^^ created here @@ -35,7 +35,7 @@ LL ~ let b = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:33:5 + --> tests/ui/needless_late_init.rs:31:5 | LL | let c; | ^^^^^^ created here @@ -52,7 +52,7 @@ LL ~ let c = 2; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:38:5 + --> tests/ui/needless_late_init.rs:36:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -68,7 +68,7 @@ LL ~ let d: usize = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:42:5 + --> tests/ui/needless_late_init.rs:40:5 | LL | let e; | ^^^^^^ created here @@ -84,7 +84,7 @@ LL ~ let e = format!("{}", d); | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:48:5 + --> tests/ui/needless_late_init.rs:46:5 | LL | let a; | ^^^^^^ @@ -103,7 +103,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:58:5 + --> tests/ui/needless_late_init.rs:56:5 | LL | let b; | ^^^^^^ @@ -120,7 +120,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:66:5 + --> tests/ui/needless_late_init.rs:64:5 | LL | let d; | ^^^^^^ @@ -138,7 +138,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:75:5 + --> tests/ui/needless_late_init.rs:73:5 | LL | let e; | ^^^^^^ @@ -155,7 +155,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:83:5 + --> tests/ui/needless_late_init.rs:81:5 | LL | let f; | ^^^^^^ @@ -169,7 +169,7 @@ LL ~ 1 => "three", | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:90:5 + --> tests/ui/needless_late_init.rs:88:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -186,7 +186,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:99:5 + --> tests/ui/needless_late_init.rs:97:5 | LL | let x; | ^^^^^^ created here @@ -203,7 +203,7 @@ LL ~ let x = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:104:5 + --> tests/ui/needless_late_init.rs:102:5 | LL | let x; | ^^^^^^ created here @@ -220,7 +220,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:109:5 + --> tests/ui/needless_late_init.rs:107:5 | LL | let x; | ^^^^^^ created here @@ -238,7 +238,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:129:5 + --> tests/ui/needless_late_init.rs:127:5 | LL | let a; | ^^^^^^ @@ -257,7 +257,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:147:5 + --> tests/ui/needless_late_init.rs:145:5 | LL | let a; | ^^^^^^ @@ -276,7 +276,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:302:5 + --> tests/ui/needless_late_init.rs:298:5 | LL | let r; | ^^^^^^ created here diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index d59393fb3f3..e9d811986aa 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -534,4 +534,11 @@ mod issue13749bis { impl<'a, T: 'a> Generic<T> {} } +pub fn issue14607<'s>(x: &'s u8) { + #[expect(clippy::redundant_closure_call)] + (|| { + let _: &'s u8 = x; + })(); +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index e24907ab5fc..0b6eb9755b9 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -534,4 +534,11 @@ mod issue13749bis { impl<'a, T: 'a> Generic<T> {} } +pub fn issue14607<'s>(x: &'s u8) { + #[expect(clippy::redundant_closure_call)] + (|| { + let _: &'s u8 = x; + })(); +} + fn main() {} diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index 5d9936fdac4..bdad3e3d5b0 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -301,7 +301,7 @@ struct Data<T: ?Sized> { } // Unsafe functions should not warn. unsafe fn get_mut_unchecked<T>(ptr: &mut NonNull<Data<T>>) -> &mut T { - &mut (*ptr.as_ptr()).value + unsafe { &mut (*ptr.as_ptr()).value } } // Unsafe blocks should not warn. fn get_mut_unchecked2<T>(ptr: &mut NonNull<Data<T>>) -> &mut T { diff --git a/tests/ui/needless_pass_by_ref_mut_2021.rs b/tests/ui/needless_pass_by_ref_mut_2021.rs new file mode 100644 index 00000000000..994eba9cae3 --- /dev/null +++ b/tests/ui/needless_pass_by_ref_mut_2021.rs @@ -0,0 +1,12 @@ +//@edition: 2021 +//@check-pass +#![warn(clippy::needless_pass_by_ref_mut)] + +struct Data<T: ?Sized> { + value: T, +} + +// Unsafe functions should not warn. +unsafe fn get_mut_unchecked<T>(ptr: &mut std::ptr::NonNull<Data<T>>) -> &mut T { + &mut (*ptr.as_ptr()).value +} diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 55da4f28976..8516cee48e6 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,100 +1,188 @@ -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:20:12 | LL | return Some(to.magic?); - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-question-mark` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_question_mark)]` +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?); +LL + return to.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:29:12 | LL | return Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?) +LL + return to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:35:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - to.and_then(|t| Some(t.magic?)) +LL + to.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:51:9 | LL | Some(t.magic?) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:57:12 | LL | return Ok(tr.magic?); - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?); +LL + return tr.magic; + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:65:12 | LL | return Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?) +LL + return tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:70:5 | LL | Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(tr.magic?) +LL + tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:75:21 | LL | tr.and_then(|t| Ok(t.magic?)) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - tr.and_then(|t| Ok(t.magic?)) +LL + tr.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:84:9 | LL | Ok(t.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:92:16 | LL | return Ok(t.magic?); - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(t.magic?); +LL + return t.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:128:27 | LL | || -> Option<_> { Some(Some($expr)?) }() - | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)` + | ^^^^^^^^^^^^^^^^^^ ... LL | let _x = some_and_qmark_in_macro!(x?); | ---------------------------- in this macro invocation | = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: remove the enclosing `Some` and `?` operator + | +LL - || -> Option<_> { Some(Some($expr)?) }() +LL + || -> Option<_> { Some($expr) }() + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:140:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:149:5 | LL | Ok(s.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(s.magic?) +LL + s.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:154:7 | LL | { Some(a?) } - | ^^^^^^^^ help: try removing question mark and `Some()`: `a` + | ^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - { Some(a?) } +LL + { a } + | error: aborting due to 15 previous errors diff --git a/tests/ui/no_mangle_with_rust_abi.rs b/tests/ui/no_mangle_with_rust_abi.rs index 0d09b3ceecd..f4248ffc0f4 100644 --- a/tests/ui/no_mangle_with_rust_abi.rs +++ b/tests/ui/no_mangle_with_rust_abi.rs @@ -43,7 +43,7 @@ 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" { +unsafe extern "C" { fn c_abi_in_block(arg_one: u32, arg_two: usize); } diff --git a/tests/ui/non_send_fields_in_send_ty.rs b/tests/ui/non_send_fields_in_send_ty.rs index 046ea70b08f..31778f74509 100644 --- a/tests/ui/non_send_fields_in_send_ty.rs +++ b/tests/ui/non_send_fields_in_send_ty.rs @@ -35,7 +35,7 @@ unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {} //~^ ERROR: some fields in `ArcGuard<RC, T>` are not safe to be sent to another thread // rusb / RUSTSEC-2020-0098 -extern "C" { +unsafe extern "C" { type libusb_device_handle; } @@ -90,7 +90,7 @@ unsafe impl<A, B> Send for MultiParam<A, B> {} //~^ ERROR: some fields in `MultiParam<A, B>` are not safe to be sent to another thread // Tests for raw pointer heuristic -extern "C" { +unsafe extern "C" { type NonSend; } diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed index 2b30c8f984e..d6c35d8097c 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase()); +} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs index c52338eee83..996ef050d69 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase()); +} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 1794ac57fe5..a1119d75c23 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -179,16 +179,20 @@ fn f() -> Option<()> { mod issue6675 { unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T { - #[allow(unused)] - let x = vec![0; 1000]; // future-proofing, make this function expensive. - &*p + unsafe { + #[allow(unused)] + let x = vec![0; 1000]; // future-proofing, make this function expensive. + &*p + } } unsafe fn foo() { - let s = "test".to_owned(); - let s = &s as *const _; - None.unwrap_or_else(|| ptr_to_ref(s)); - //~^ or_fun_call + unsafe { + let s = "test".to_owned(); + let s = &s as *const _; + None.unwrap_or_else(|| ptr_to_ref(s)); + //~^ or_fun_call + } } fn bar() { diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 256db343c05..a7cd632bf16 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -179,16 +179,20 @@ fn f() -> Option<()> { mod issue6675 { unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T { - #[allow(unused)] - let x = vec![0; 1000]; // future-proofing, make this function expensive. - &*p + unsafe { + #[allow(unused)] + let x = vec![0; 1000]; // future-proofing, make this function expensive. + &*p + } } unsafe fn foo() { - let s = "test".to_owned(); - let s = &s as *const _; - None.unwrap_or(ptr_to_ref(s)); - //~^ or_fun_call + unsafe { + let s = "test".to_owned(); + let s = &s as *const _; + None.unwrap_or(ptr_to_ref(s)); + //~^ or_fun_call + } } fn bar() { diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 93c87b2f12c..35bda7e4d33 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -125,91 +125,91 @@ LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:190:14 + --> tests/ui/or_fun_call.rs:193:18 | -LL | None.unwrap_or(ptr_to_ref(s)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` +LL | None.unwrap_or(ptr_to_ref(s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:197:14 + --> tests/ui/or_fun_call.rs:201:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:200:14 + --> tests/ui/or_fun_call.rs:204:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:276:25 + --> tests/ui/or_fun_call.rs:280:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:278:25 + --> tests/ui/or_fun_call.rs:282:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:310:18 + --> tests/ui/or_fun_call.rs:314:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:314:28 + --> tests/ui/or_fun_call.rs:318:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:318:27 + --> tests/ui/or_fun_call.rs:322:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:322:22 + --> tests/ui/or_fun_call.rs:326:22 | LL | real_default.unwrap_or_else(<FakeDefault as Default>::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:326:23 + --> tests/ui/or_fun_call.rs:330:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:330:25 + --> tests/ui/or_fun_call.rs:334:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:334:25 + --> tests/ui/or_fun_call.rs:338:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:376:17 + --> tests/ui/or_fun_call.rs:380:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:381:17 + --> tests/ui/or_fun_call.rs:385:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:386:17 + --> tests/ui/or_fun_call.rs:390:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -229,19 +229,19 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:392:17 + --> tests/ui/or_fun_call.rs:396:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:397:17 + --> tests/ui/or_fun_call.rs:401:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:404:21 + --> tests/ui/or_fun_call.rs:408:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` diff --git a/tests/ui/pointers_in_nomem_asm_block.rs b/tests/ui/pointers_in_nomem_asm_block.rs index 171716be260..7f69c61b028 100644 --- a/tests/ui/pointers_in_nomem_asm_block.rs +++ b/tests/ui/pointers_in_nomem_asm_block.rs @@ -6,29 +6,37 @@ use core::arch::asm; unsafe fn nomem_bad(p: &i32) { - asm!( - "asdf {p1}, {p2}, {p3}", - p1 = in(reg) p, - //~^ pointers_in_nomem_asm_block + unsafe { + asm!( + "asdf {p1}, {p2}, {p3}", + p1 = in(reg) p, + //~^ pointers_in_nomem_asm_block - p2 = in(reg) p as *const _ as usize, - p3 = in(reg) p, - options(nomem, nostack, preserves_flags) - ); + p2 = in(reg) p as *const _ as usize, + p3 = in(reg) p, + options(nomem, nostack, preserves_flags) + ); + } } unsafe fn nomem_good(p: &i32) { - asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); - let p = p as *const i32 as usize; - asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + unsafe { + asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32 as usize; + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + } } unsafe fn nomem_bad2(p: &mut i32) { - asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); - //~^ pointers_in_nomem_asm_block + unsafe { + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ pointers_in_nomem_asm_block + } } unsafe fn nomem_fn(p: extern "C" fn()) { - asm!("call {p}", p = in(reg) p, options(nomem)); - //~^ pointers_in_nomem_asm_block + unsafe { + asm!("call {p}", p = in(reg) p, options(nomem)); + //~^ pointers_in_nomem_asm_block + } } diff --git a/tests/ui/pointers_in_nomem_asm_block.stderr b/tests/ui/pointers_in_nomem_asm_block.stderr index ca24e34f63c..eabac2444ec 100644 --- a/tests/ui/pointers_in_nomem_asm_block.stderr +++ b/tests/ui/pointers_in_nomem_asm_block.stderr @@ -1,11 +1,11 @@ error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:11:9 + --> tests/ui/pointers_in_nomem_asm_block.rs:12:13 | -LL | p1 = in(reg) p, - | ^^^^^^^^^^^^^^ +LL | p1 = in(reg) p, + | ^^^^^^^^^^^^^^ ... -LL | p3 = in(reg) p, - | ^^^^^^^^^^^^^^ +LL | p3 = in(reg) p, + | ^^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint @@ -13,19 +13,19 @@ LL | p3 = in(reg) p, = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]` error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:27:22 + --> tests/ui/pointers_in_nomem_asm_block.rs:32:26 | -LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); - | ^^^^^^^^^^^^^ +LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:32:22 + --> tests/ui/pointers_in_nomem_asm_block.rs:39:26 | -LL | asm!("call {p}", p = in(reg) p, options(nomem)); - | ^^^^^^^^^^^^^ +LL | asm!("call {p}", p = in(reg) p, options(nomem)); + | ^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 6dded72d3e1..79bfae1f7eb 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -12,11 +12,13 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; unsafe fn ptr_to_ref<T, U>(p: *const T, om: *mut U) { - let _: &mut T = std::mem::transmute(p.cast_mut()); - //~^ ptr_cast_constness - let _ = &mut *p.cast_mut(); - //~^ ptr_cast_constness - let _: &T = &*(om as *const T); + unsafe { + let _: &mut T = std::mem::transmute(p.cast_mut()); + //~^ ptr_cast_constness + let _ = &mut *p.cast_mut(); + //~^ ptr_cast_constness + let _: &T = &*(om as *const T); + } } #[inline_macros] @@ -98,3 +100,9 @@ fn null_pointers() { let _ = external!(ptr::null::<u32>() as *mut u32); let _ = external!(ptr::null::<u32>().cast_mut()); } + +fn issue14621() { + let mut local = 4; + let _ = std::ptr::addr_of_mut!(local).cast_const(); + //~^ ptr_cast_constness +} diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index e9629f5290e..f6590dabd5b 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -12,11 +12,13 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; unsafe fn ptr_to_ref<T, U>(p: *const T, om: *mut U) { - let _: &mut T = std::mem::transmute(p as *mut T); - //~^ ptr_cast_constness - let _ = &mut *(p as *mut T); - //~^ ptr_cast_constness - let _: &T = &*(om as *const T); + unsafe { + let _: &mut T = std::mem::transmute(p as *mut T); + //~^ ptr_cast_constness + let _ = &mut *(p as *mut T); + //~^ ptr_cast_constness + let _: &T = &*(om as *const T); + } } #[inline_macros] @@ -98,3 +100,9 @@ fn null_pointers() { let _ = external!(ptr::null::<u32>() as *mut u32); let _ = external!(ptr::null::<u32>().cast_mut()); } + +fn issue14621() { + let mut local = 4; + let _ = std::ptr::addr_of_mut!(local) as *const _; + //~^ ptr_cast_constness +} diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 1eeeef74701..0b1644168ff 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -1,74 +1,74 @@ error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:15:41 + --> tests/ui/ptr_cast_constness.rs:16:45 | -LL | let _: &mut T = std::mem::transmute(p as *mut T); - | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` +LL | let _: &mut T = std::mem::transmute(p as *mut T); + | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` | = note: `-D clippy::ptr-cast-constness` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_cast_constness)]` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:17:19 + --> tests/ui/ptr_cast_constness.rs:18:23 | -LL | let _ = &mut *(p as *mut T); - | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` +LL | let _ = &mut *(p as *mut T); + | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:33:17 + --> tests/ui/ptr_cast_constness.rs:35:17 | LL | let _ = *ptr_ptr as *mut u32; | ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:37:13 + --> tests/ui/ptr_cast_constness.rs:39:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:39:13 + --> tests/ui/ptr_cast_constness.rs:41:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:73:13 + --> tests/ui/ptr_cast_constness.rs:75:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:75:13 + --> tests/ui/ptr_cast_constness.rs:77:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:82:13 + --> tests/ui/ptr_cast_constness.rs:84:13 | LL | let _ = ptr::null::<String>() as *mut String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<String>()` error: `as` casting to make a mutable null pointer into a const null pointer - --> tests/ui/ptr_cast_constness.rs:84:13 + --> tests/ui/ptr_cast_constness.rs:86:13 | LL | let _ = ptr::null_mut::<u32>() as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::<u32>()` error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:86:13 + --> tests/ui/ptr_cast_constness.rs:88:13 | LL | let _ = ptr::null::<u32>().cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()` error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:88:13 + --> tests/ui/ptr_cast_constness.rs:90:13 | LL | let _ = ptr::null_mut::<u32>().cast_const(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::<u32>()` error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:92:21 + --> tests/ui/ptr_cast_constness.rs:94:21 | LL | let _ = inline!(ptr::null::<u32>() as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()` @@ -76,12 +76,18 @@ LL | let _ = inline!(ptr::null::<u32>() as *mut u32); = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:94:21 + --> tests/ui/ptr_cast_constness.rs:96:21 | LL | let _ = inline!(ptr::null::<u32>().cast_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()` | = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 13 previous errors +error: `as` casting between raw pointers while changing only its constness + --> tests/ui/ptr_cast_constness.rs:106:13 + | +LL | let _ = std::ptr::addr_of_mut!(local) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `std::ptr::addr_of_mut!(local).cast_const()` + +error: aborting due to 14 previous errors diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed index 484ff307323..9629b3eea58 100644 --- a/tests/ui/ptr_eq.fixed +++ b/tests/ui/ptr_eq.fixed @@ -23,23 +23,25 @@ fn main() { //~^ ptr_eq let _ = std::ptr::eq(a, b); //~^ ptr_eq - let _ = std::ptr::eq(a.as_ptr(), b as *const _); - //~^ ptr_eq - let _ = std::ptr::eq(a.as_ptr(), b.as_ptr()); - //~^ ptr_eq - // Do not lint + // Do not lint: the rhs conversion is needed + let _ = a.as_ptr() == b as *const _; + // Do not lint: we have two raw pointers already + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); - //~^ ptr_eq - let _ = std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); - //~^ ptr_eq + // Do not lint: the rhs conversion is needed + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already + let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); @@ -51,8 +53,12 @@ fn main() { let _ = !std::ptr::eq(x, y); //~^ ptr_eq - #[allow(clippy::eq_op)] - let _issue14337 = std::ptr::eq(main as *const (), main as *const ()); + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type + let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = std::ptr::eq(mac!(cast a), mac!(cast b)); //~^ ptr_eq // Do not peel the content of macros diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs index f28707cc3e9..2b741d8df46 100644 --- a/tests/ui/ptr_eq.rs +++ b/tests/ui/ptr_eq.rs @@ -23,23 +23,25 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); - //~^ ptr_eq // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); - //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); @@ -51,8 +53,12 @@ fn main() { let _ = x as *const u32 != y as *mut u32 as *const u32; //~^ ptr_eq - #[allow(clippy::eq_op)] + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; //~^ ptr_eq // Do not peel the content of macros diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr index 906831b9e03..e7340624b59 100644 --- a/tests/ui/ptr_eq.stderr +++ b/tests/ui/ptr_eq.stderr @@ -14,52 +14,28 @@ LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:26:13 - | -LL | let _ = a.as_ptr() == b as *const _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b as *const _)` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:28:13 - | -LL | let _ = a.as_ptr() == b.as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b.as_ptr())` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:39:13 - | -LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:41:13 - | -LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:48:13 + --> tests/ui/ptr_eq.rs:50:13 | LL | let _ = x as *const u32 == y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:51:13 + --> tests/ui/ptr_eq.rs:53:13 | LL | let _ = x as *const u32 != y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:55:23 + --> tests/ui/ptr_eq.rs:61:13 | -LL | let _issue14337 = main as *const () == main as *const (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(main as *const (), main as *const ())` +LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:59:13 + --> tests/ui/ptr_eq.rs:65:13 | LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` -error: aborting due to 10 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/ptr_eq_no_std.fixed b/tests/ui/ptr_eq_no_std.fixed index d8ee4ea88f8..48cbad62e1a 100644 --- a/tests/ui/ptr_eq_no_std.fixed +++ b/tests/ui/ptr_eq_no_std.fixed @@ -32,23 +32,25 @@ fn main() { //~^ ptr_eq let _ = core::ptr::eq(a, b); //~^ ptr_eq - let _ = core::ptr::eq(a.as_ptr(), b as *const _); - //~^ ptr_eq - let _ = core::ptr::eq(a.as_ptr(), b.as_ptr()); - //~^ ptr_eq - // Do not lint + // Do not lint: the rhs conversion is needed + let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already + let _ = a.as_ptr() == b.as_ptr(); + // Do not lint let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); - //~^ ptr_eq - let _ = core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); - //~^ ptr_eq + // Do not lint: the rhs conversion is needed + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already + let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.rs b/tests/ui/ptr_eq_no_std.rs index a236314c29b..3827178640e 100644 --- a/tests/ui/ptr_eq_no_std.rs +++ b/tests/ui/ptr_eq_no_std.rs @@ -32,23 +32,25 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); - //~^ ptr_eq // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); - //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.stderr b/tests/ui/ptr_eq_no_std.stderr index 5b8135dc8e8..8c7b1ff7666 100644 --- a/tests/ui/ptr_eq_no_std.stderr +++ b/tests/ui/ptr_eq_no_std.stderr @@ -13,29 +13,5 @@ error: use `core::ptr::eq` when comparing raw pointers LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:35:13 - | -LL | let _ = a.as_ptr() == b as *const _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b as *const _)` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:37:13 - | -LL | let _ = a.as_ptr() == b.as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b.as_ptr())` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:48:13 - | -LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:50:13 - | -LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` - -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index fff41f57828..507bc2b29d8 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -3,6 +3,8 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] +use std::sync::MutexGuard; + fn some_func(a: Option<u32>) -> Option<u32> { a?; @@ -299,6 +301,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option<usize>) -> Option<usize> { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block @@ -430,3 +437,9 @@ fn msrv_1_13(arg: Option<i32>) -> Option<i32> { println!("{}", val); Some(val) } + +fn issue_14615(a: MutexGuard<Option<u32>>) -> Option<String> { + let a = (*a)?; + //~^^^ question_mark + Some(format!("{a}")) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index c71c8ee984e..64b51b849ed 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -3,6 +3,8 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] +use std::sync::MutexGuard; + fn some_func(a: Option<u32>) -> Option<u32> { if a.is_none() { //~^ question_mark @@ -369,6 +371,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option<usize>) -> Option<usize> { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block @@ -524,3 +531,11 @@ fn msrv_1_13(arg: Option<i32>) -> Option<i32> { println!("{}", val); Some(val) } + +fn issue_14615(a: MutexGuard<Option<u32>>) -> Option<String> { + let Some(a) = *a else { + return None; + }; + //~^^^ question_mark + Some(format!("{a}")) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 183b8866a74..d8ce4420aee 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:7:5 + --> tests/ui/question_mark.rs:9:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:53:9 + --> tests/ui/question_mark.rs:55:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:58:9 + --> tests/ui/question_mark.rs:60:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:63:17 + --> tests/ui/question_mark.rs:65:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:70:17 + --> tests/ui/question_mark.rs:72:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:88:9 + --> tests/ui/question_mark.rs:90:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:97:9 + --> tests/ui/question_mark.rs:99:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:106:9 + --> tests/ui/question_mark.rs:108:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:114:26 + --> tests/ui/question_mark.rs:116:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:125:17 + --> tests/ui/question_mark.rs:127:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:147:5 + --> tests/ui/question_mark.rs:149:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:152:16 + --> tests/ui/question_mark.rs:154:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:163:5 + --> tests/ui/question_mark.rs:165:5 | LL | / match f() { LL | | @@ -134,7 +134,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:169:5 + --> tests/ui/question_mark.rs:171:5 | LL | / match opt_none!() { LL | | @@ -144,13 +144,13 @@ LL | | }; | |_____^ help: try instead: `opt_none!()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:196:13 + --> tests/ui/question_mark.rs:198:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:199:5 + --> tests/ui/question_mark.rs:201:5 | LL | / if x.is_err() { LL | | @@ -159,7 +159,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:204:16 + --> tests/ui/question_mark.rs:206:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:210:5 + --> tests/ui/question_mark.rs:212:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +180,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:302:5 + --> tests/ui/question_mark.rs:304:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:310:5 + --> tests/ui/question_mark.rs:312:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:388:13 + --> tests/ui/question_mark.rs:395:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:449:5 + --> tests/ui/question_mark.rs:456:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:464:5 + --> tests/ui/question_mark.rs:471:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:474:5 + --> tests/ui/question_mark.rs:481:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:485:5 + --> tests/ui/question_mark.rs:492:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:489:5 + --> tests/ui/question_mark.rs:496:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:493:5 + --> tests/ui/question_mark.rs:500:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:515:5 + --> tests/ui/question_mark.rs:522:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:519:15 + --> tests/ui/question_mark.rs:526:15 | LL | let val = match arg { | _______________^ @@ -275,5 +275,13 @@ LL | | None => return None, LL | | }; | |_____^ help: try instead: `arg?` -error: aborting due to 29 previous errors +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:536:5 + | +LL | / let Some(a) = *a else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let a = (*a)?;` + +error: aborting due to 30 previous errors diff --git a/tests/ui/question_mark_used.stderr b/tests/ui/question_mark_used.stderr index 53cb59c0216..82f0d325040 100644 --- a/tests/ui/question_mark_used.stderr +++ b/tests/ui/question_mark_used.stderr @@ -1,4 +1,4 @@ -error: question mark operator was used +error: the `?` operator was used --> tests/ui/question_mark_used.rs:11:5 | LL | other_function()?; diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 33a5308bd35..dc9d6491691 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -1,4 +1,4 @@ -#![feature(let_chains, if_let_guard)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 60bce2994ea..2e9714ad8e7 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -1,4 +1,4 @@ -#![feature(let_chains, if_let_guard)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, diff --git a/tests/ui/redundant_test_prefix.fixed b/tests/ui/redundant_test_prefix.fixed new file mode 100644 index 00000000000..b99771f0640 --- /dev/null +++ b/tests/ui/redundant_test_prefix.fixed @@ -0,0 +1,158 @@ +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +mod m1 { + pub fn f5() {} +} + +#[cfg(test)] +#[test] +fn f6() { + //~^ redundant_test_prefix + + use m1::f5; + + f5(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn foo() { + //~^ redundant_test_prefix + } + + #[test] + fn foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn f1() { + //~^ redundant_test_prefix + } + + #[test] + fn f2() { + //~^ redundant_test_prefix + } + + #[test] + fn f3() { + //~^ redundant_test_prefix + } + + #[test] + fn f4() { + //~^ redundant_test_prefix + } + + #[test] + fn f5() { + //~^ redundant_test_prefix + } + + #[test] + fn f6() { + //~^ redundant_test_prefix + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn foo() { + //~^ redundant_test_prefix + } + + #[test] + fn foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn f1() { + //~^ redundant_test_prefix + } + + #[test] + fn f2() { + //~^ redundant_test_prefix + } + + #[test] + fn f3() { + //~^ redundant_test_prefix + } + + #[test] + fn f4() { + //~^ redundant_test_prefix + } + + #[test] + fn f5() { + //~^ redundant_test_prefix + } + + #[test] + fn f6() { + //~^ redundant_test_prefix + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/tests/ui/redundant_test_prefix.rs b/tests/ui/redundant_test_prefix.rs new file mode 100644 index 00000000000..3aec577cffa --- /dev/null +++ b/tests/ui/redundant_test_prefix.rs @@ -0,0 +1,158 @@ +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn test_f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn test_f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +mod m1 { + pub fn f5() {} +} + +#[cfg(test)] +#[test] +fn test_f6() { + //~^ redundant_test_prefix + + use m1::f5; + + f5(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/tests/ui/redundant_test_prefix.stderr b/tests/ui/redundant_test_prefix.stderr new file mode 100644 index 00000000000..d156af586df --- /dev/null +++ b/tests/ui/redundant_test_prefix.stderr @@ -0,0 +1,119 @@ +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:17:4 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + | + = note: `-D clippy::redundant-test-prefix` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_test_prefix)]` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:26:4 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:39:4 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:54:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:59:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:66:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:71:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:76:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:81:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:86:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:91:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:100:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:105:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:112:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:117:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:122:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:127:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:132:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:137:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: aborting due to 19 previous errors + diff --git a/tests/ui/redundant_test_prefix_noautofix.rs b/tests/ui/redundant_test_prefix_noautofix.rs new file mode 100644 index 00000000000..6ad5d011d8b --- /dev/null +++ b/tests/ui/redundant_test_prefix_noautofix.rs @@ -0,0 +1,288 @@ +//@no-rustfix: name conflicts + +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn test_f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn test_f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +fn f5() {} + +#[cfg(test)] +#[test] +fn test_f5() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // Collision with existing function. +} + +mod m1 { + pub fn f6() {} + pub fn f7() {} +} + +#[cfg(test)] +#[test] +fn test_f6() { + //~^ redundant_test_prefix + + use m1::f6; + + f6(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, but has a function call that will result in recursion. +} + +#[cfg(test)] +#[test] +fn test_f8() { + //~^ redundant_test_prefix + + use m1::f7; + + f7(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +// Although there's no direct call of `f` in the test, name collision still exists, +// since all `m3` functions are imported and then `map` is used to call `f`. +mod m2 { + mod m3 { + pub fn f(_: i32) -> i32 { + 0 + } + } + + use m3::*; + + #[cfg(test)] + #[test] + fn test_f() { + //~^ redundant_test_prefix + let a = Some(3); + let _ = a.map(f); + } +} + +mod m3 { + fn test_m3_1() { + // Has prefix, but no `#[test]` attribute, ignore. + } + + #[test] + fn test_m3_2() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } + + #[test] + fn test_1() { + //~^ redundant_test_prefix + + // `1` is invalid function name, so suggestion to rename is emitted + } + + #[test] + fn test_const() { + //~^ redundant_test_prefix + + // `const` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_async() { + //~^ redundant_test_prefix + + // `async` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_yield() { + //~^ redundant_test_prefix + + // `yield` is reserved keyword for future use, so suggestion to rename is emitted + } + + #[test] + fn test_() { + //~^ redundant_test_prefix + + // `` is invalid function name, so suggestion to rename is emitted + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } + + #[test] + fn test_1() { + //~^ redundant_test_prefix + + // `1` is invalid function name, so suggestion to rename is emitted + } + + #[test] + fn test_const() { + //~^ redundant_test_prefix + + // `const` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_async() { + //~^ redundant_test_prefix + + // `async` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_yield() { + //~^ redundant_test_prefix + + // `yield` is reserved keyword for future use, so suggestion to rename is emitted + } + + #[test] + fn test_() { + //~^ redundant_test_prefix + + // `` is invalid function name, so suggestion to rename is emitted + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/tests/ui/redundant_test_prefix_noautofix.stderr b/tests/ui/redundant_test_prefix_noautofix.stderr new file mode 100644 index 00000000000..6440faf1b3c --- /dev/null +++ b/tests/ui/redundant_test_prefix_noautofix.stderr @@ -0,0 +1,241 @@ +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:19:4 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + | + = note: `-D clippy::redundant-test-prefix` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_test_prefix)]` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:28:4 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:39:4 + | +LL | fn test_f5() { + | ^^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f5() { +LL + fn f5_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:53:4 + | +LL | fn test_f6() { + | ^^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f6() { +LL + fn f6_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:65:4 + | +LL | fn test_f8() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f8` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:88:8 + | +LL | fn test_f() { + | ^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f() { +LL + fn f_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:101:8 + | +LL | fn test_m3_2() { + | ^^^^^^^^^ help: consider removing the `test_` prefix: `m3_2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:114:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:119:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:126:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:131:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:136:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:141:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:146:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:151:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:156:8 + | +LL | fn test_1() { + | ^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:163:8 + | +LL | fn test_const() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:170:8 + | +LL | fn test_async() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:177:8 + | +LL | fn test_yield() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:184:8 + | +LL | fn test_() { + | ^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:195:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:200:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:207:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:212:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:217:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:222:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:227:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:232:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:237:8 + | +LL | fn test_1() { + | ^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:244:8 + | +LL | fn test_const() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:251:8 + | +LL | fn test_async() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:258:8 + | +LL | fn test_yield() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:265:8 + | +LL | fn test_() { + | ^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: aborting due to 33 previous errors + diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index acf7914d253..ff81c642602 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -7,84 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(clippy::manual_find_map)] #![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] +#![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(dangling_pointers_from_temporaries)] #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(invalid_null_arguments)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] -#![allow(non_fmt_panics)] -#![allow(named_arguments_used_positionally)] -#![allow(dangling_pointers_from_temporaries)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_collection)] //~ ERROR: lint `clippy::box_vec` +#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::redundant_static_lifetimes)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cognitive_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derived_hash_with_manual_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` +#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` +#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` +#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::non_canonical_clone_impl)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::non_canonical_partial_ord_impl)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::arithmetic_side_effects)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::overly_complex_bool_expr)] //~ ERROR: lint `clippy::logic_bug` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -92,45 +115,27 @@ #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::panicking_overflow_checks)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` +#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::needless_borrow)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` +#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::recursive_format_impl)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` -#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` -#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` -#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` -#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` -#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` -#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` -#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` +#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 32641a684a4..b5d5d07e639 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -7,84 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(clippy::manual_find_map)] #![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] +#![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(dangling_pointers_from_temporaries)] #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(invalid_null_arguments)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] -#![allow(non_fmt_panics)] -#![allow(named_arguments_used_positionally)] -#![allow(dangling_pointers_from_temporaries)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::block_in_if_condition_stmt)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_if_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_vec)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::const_static_lifetime)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cyclomatic_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derive_hash_xor_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` +#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` +#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` +#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::incorrect_clone_impl_on_copy_type)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::integer_arithmetic)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::logic_bug)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -92,45 +115,27 @@ #![warn(clippy::option_map_unwrap_or_else)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::option_unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::overflow_check_conditional)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` +#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::ref_in_deref)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::to_string_in_display)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` -#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` -#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` -#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` -#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` -#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` -#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` -#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` +#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index e9d2debff91..2487dfc8eba 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,412 +8,436 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` +error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` + --> tests/ui/rename.rs:73:9 + | +LL | #![warn(clippy::cast_ref_to_mut)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` + +error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` + --> tests/ui/rename.rs:74:9 + | +LL | #![warn(clippy::clone_double_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` + +error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` + --> tests/ui/rename.rs:75:9 + | +LL | #![warn(clippy::cmp_nan)] + | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` + error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` -error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:77:9 - | -LL | #![warn(clippy::eval_order_dependence)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` - -error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:78:9 - | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` - -error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:79:9 - | -LL | #![warn(clippy::filter_map)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` - -error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:80:9 - | -LL | #![warn(clippy::fn_address_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` - -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` +error: lint `clippy::double_neg` has been renamed to `double_negations` --> tests/ui/rename.rs:81:9 | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` +LL | #![warn(clippy::double_neg)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` -error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` --> tests/ui/rename.rs:82:9 | -LL | #![warn(clippy::if_let_redundant_pattern_matching)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` +LL | #![warn(clippy::drop_bounds)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` +error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` --> tests/ui/rename.rs:83:9 | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +LL | #![warn(clippy::drop_copy)] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` -error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` +error: lint `clippy::drop_ref` has been renamed to `dropping_references` --> tests/ui/rename.rs:84:9 | -LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` +LL | #![warn(clippy::drop_ref)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` -error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` +error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` --> tests/ui/rename.rs:85:9 | -LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` +LL | #![warn(clippy::eval_order_dependence)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` +error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` --> tests/ui/rename.rs:86:9 | -LL | #![warn(clippy::integer_arithmetic)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` +LL | #![warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` -error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` --> tests/ui/rename.rs:87:9 | -LL | #![warn(clippy::logic_bug)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` +error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` --> tests/ui/rename.rs:88:9 | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` +LL | #![warn(clippy::fn_address_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` -error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` +error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` --> tests/ui/rename.rs:89:9 | -LL | #![warn(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` +LL | #![warn(clippy::fn_null_check)] + | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` -error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:90:9 | -LL | #![warn(clippy::option_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:91:9 | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:92:9 | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` --> tests/ui/rename.rs:93:9 | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::forget_copy)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` -error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` +error: lint `clippy::forget_ref` has been renamed to `forgetting_references` --> tests/ui/rename.rs:94:9 | -LL | #![warn(clippy::overflow_check_conditional)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` +LL | #![warn(clippy::forget_ref)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` -error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> tests/ui/rename.rs:95:9 | -LL | #![warn(clippy::ref_in_deref)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` --> tests/ui/rename.rs:96:9 | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::if_let_redundant_pattern_matching)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` --> tests/ui/rename.rs:97:9 | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` --> tests/ui/rename.rs:98:9 | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` +error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` --> tests/ui/rename.rs:99:9 | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` +LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` +error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` --> tests/ui/rename.rs:100:9 | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +LL | #![warn(clippy::integer_arithmetic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` -error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> tests/ui/rename.rs:101:9 | -LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` +LL | #![warn(clippy::into_iter_on_array)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` --> tests/ui/rename.rs:102:9 | -LL | #![warn(clippy::to_string_in_display)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` +LL | #![warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` -error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` +error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` --> tests/ui/rename.rs:103:9 | -LL | #![warn(clippy::unwrap_or_else_default)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` +LL | #![warn(clippy::invalid_null_ptr_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> tests/ui/rename.rs:104:9 | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` +LL | #![warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` +error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` --> tests/ui/rename.rs:105:9 | -LL | #![warn(clippy::cast_ref_to_mut)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` +LL | #![warn(clippy::invalid_utf8_in_unchecked)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` -error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` +error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` --> tests/ui/rename.rs:106:9 | -LL | #![warn(clippy::clone_double_ref)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` +LL | #![warn(clippy::let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` -error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` +error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` --> tests/ui/rename.rs:107:9 | -LL | #![warn(clippy::cmp_nan)] - | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` +LL | #![warn(clippy::logic_bug)] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` -error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` +error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` --> tests/ui/rename.rs:108:9 | -LL | #![warn(clippy::invalid_null_ptr_usage)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` +LL | #![warn(clippy::maybe_misused_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` -error: lint `clippy::double_neg` has been renamed to `double_negations` +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` --> tests/ui/rename.rs:109:9 | -LL | #![warn(clippy::double_neg)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` -error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` +error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` --> tests/ui/rename.rs:110:9 | -LL | #![warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` +LL | #![warn(clippy::mismatched_target_os)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` -error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` --> tests/ui/rename.rs:111:9 | -LL | #![warn(clippy::drop_copy)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` -error: lint `clippy::drop_ref` has been renamed to `dropping_references` +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` --> tests/ui/rename.rs:112:9 | -LL | #![warn(clippy::drop_ref)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +LL | #![warn(clippy::option_and_then_some)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` -error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` +error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` --> tests/ui/rename.rs:113:9 | -LL | #![warn(clippy::fn_null_check)] - | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` +LL | #![warn(clippy::option_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:114:9 | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:115:9 | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` --> tests/ui/rename.rs:116:9 | -LL | #![warn(clippy::for_loops_over_fallibles)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` +error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` --> tests/ui/rename.rs:117:9 | -LL | #![warn(clippy::forget_copy)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` +LL | #![warn(clippy::overflow_check_conditional)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` -error: lint `clippy::forget_ref` has been renamed to `forgetting_references` +error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` --> tests/ui/rename.rs:118:9 | -LL | #![warn(clippy::forget_ref)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` +LL | #![warn(clippy::panic_params)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` +error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` --> tests/ui/rename.rs:119:9 | -LL | #![warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` +LL | #![warn(clippy::positional_named_format_parameters)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` -error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` --> tests/ui/rename.rs:120:9 | -LL | #![warn(clippy::invalid_atomic_ordering)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` +LL | #![warn(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` --> tests/ui/rename.rs:121:9 | -LL | #![warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:122:9 | -LL | #![warn(clippy::invalid_utf8_in_unchecked)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` --> tests/ui/rename.rs:123:9 | -LL | #![warn(clippy::let_underscore_drop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` --> tests/ui/rename.rs:124:9 | -LL | #![warn(clippy::maybe_misused_cfg)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` --> tests/ui/rename.rs:125:9 | -LL | #![warn(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` -error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` --> tests/ui/rename.rs:126:9 | -LL | #![warn(clippy::mismatched_target_os)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` -error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` --> tests/ui/rename.rs:127:9 | -LL | #![warn(clippy::panic_params)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` +LL | #![warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` -error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` +error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` --> tests/ui/rename.rs:128:9 | -LL | #![warn(clippy::positional_named_format_parameters)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` +LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` --> tests/ui/rename.rs:129:9 | -LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` +LL | #![warn(clippy::to_string_in_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` -error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` +error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` --> tests/ui/rename.rs:130:9 | +LL | #![warn(clippy::transmute_float_to_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:131:9 + | +LL | #![warn(clippy::transmute_int_to_char)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:132:9 + | +LL | #![warn(clippy::transmute_int_to_float)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:133:9 + | +LL | #![warn(clippy::transmute_num_to_bytes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` + --> tests/ui/rename.rs:134:9 + | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` + --> tests/ui/rename.rs:137:9 + | +LL | #![warn(clippy::unwrap_or_else_default)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` + error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:134:9 +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` + --> tests/ui/rename.rs:139:9 | -LL | #![warn(clippy::reverse_range_loop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 69 previous errors +error: aborting due to 73 previous errors diff --git a/tests/ui/result_unit_error_no_std.rs b/tests/ui/result_unit_error_no_std.rs index 8a1849b8490..a64e8414d78 100644 --- a/tests/ui/result_unit_error_no_std.rs +++ b/tests/ui/result_unit_error_no_std.rs @@ -14,7 +14,7 @@ pub fn returns_unit_error_lint() -> Result<u32, ()> { Err(()) } -#[no_mangle] +#[unsafe(no_mangle)] extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { 0 } diff --git a/tests/ui/return_and_then.fixed b/tests/ui/return_and_then.fixed index 74efa14eeec..8d9481d1595 100644 --- a/tests/ui/return_and_then.fixed +++ b/tests/ui/return_and_then.fixed @@ -67,8 +67,60 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option<i32> { + if shortcut { + return { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + }; + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option<i32> { + if shortcut { + return { + let mut x = Some("")?; + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }; + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option<i32> { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/tests/ui/return_and_then.rs b/tests/ui/return_and_then.rs index 188dc57e588..beada921a91 100644 --- a/tests/ui/return_and_then.rs +++ b/tests/ui/return_and_then.rs @@ -63,8 +63,55 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option<i32> { + if shortcut { + return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option<i32> { + if shortcut { + return Some("").and_then(|mut x| { + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }); + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option<i32> { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/tests/ui/return_and_then.stderr b/tests/ui/return_and_then.stderr index cc611c3dba6..5feca882860 100644 --- a/tests/ui/return_and_then.stderr +++ b/tests/ui/return_and_then.stderr @@ -1,4 +1,4 @@ -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:5:9 | LL | / opt.and_then(|n| { @@ -20,7 +20,7 @@ LL + ret += n; LL + if n > 1 { Some(ret) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:14:9 | LL | opt.and_then(|n| test_opt_block(Some(n))) @@ -32,7 +32,7 @@ LL ~ let n = opt?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:19:9 | LL | gen_option(1).and_then(|n| test_opt_block(Some(n))) @@ -44,7 +44,7 @@ LL ~ let n = gen_option(1)?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:24:9 | LL | opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) @@ -56,7 +56,7 @@ LL ~ let n = opt?; LL + if n > 1 { Ok(n + 1) } else { Err(n) } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:29:9 | LL | opt.and_then(|n| test_res_block(Ok(n))) @@ -68,7 +68,7 @@ LL ~ let n = opt?; LL + test_res_block(Ok(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:35:9 | LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) @@ -80,7 +80,7 @@ LL ~ let x = Some("")?; LL + if x.len() > 2 { Some(3) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:41:9 | LL | / Some(match (vec![1, 2, 3], vec![1, 2, 4]) { @@ -101,5 +101,50 @@ LL + })?; LL + if x.len() > 2 { Some(3) } else { None } | -error: aborting due to 7 previous errors +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:69:13 + | +LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:77:20 + | +LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ return { +LL + let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:85:20 + | +LL | return Some("").and_then(|mut x| { + | ____________________^ +LL | | let x = format!("{x}."); +LL | | if x.len() > 2 { Some(3) } else { None } +LL | | }); + | |______________^ + | +help: try + | +LL ~ return { +LL + let mut x = Some("")?; +LL + let x = format!("{x}."); +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: aborting due to 10 previous errors diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index a98b73c9e1c..3f205b322ab 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -31,34 +31,34 @@ fn ifs_same_cond_fn() { let obj = Struct; if function() { - } else if function() { //~^ same_functions_in_if_condition + } else if function() { } if fn_arg(a) { - } else if fn_arg(a) { //~^ same_functions_in_if_condition + } else if fn_arg(a) { } if obj.method() { - } else if obj.method() { //~^ same_functions_in_if_condition + } else if obj.method() { } if obj.method_arg(a) { - } else if obj.method_arg(a) { //~^ same_functions_in_if_condition + } else if obj.method_arg(a) { } let mut v = vec![1]; if v.pop().is_none() { - } else if v.pop().is_none() { //~^ same_functions_in_if_condition + } else if v.pop().is_none() { } if v.len() == 42 { - } else if v.len() == 42 { //~^ same_functions_in_if_condition + } else if v.len() == 42 { } if v.len() == 1 { diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 35dcbadce59..59f4511757d 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,79 +1,62 @@ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:34:15 - | -LL | } else if function() { - | ^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:33:8 | LL | if function() { | ^^^^^^^^^^ +LL | +LL | } else if function() { + | ^^^^^^^^^^ + | note: the lint level is defined here --> tests/ui/same_functions_in_if_condition.rs:2:9 | LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:39:15 - | -LL | } else if fn_arg(a) { - | ^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:38:8 | LL | if fn_arg(a) { | ^^^^^^^^^ +LL | +LL | } else if fn_arg(a) { + | ^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:44:15 - | -LL | } else if obj.method() { - | ^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:43:8 | LL | if obj.method() { | ^^^^^^^^^^^^ +LL | +LL | } else if obj.method() { + | ^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:49:15 - | -LL | } else if obj.method_arg(a) { - | ^^^^^^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:48:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:55:15 - | -LL | } else if v.pop().is_none() { +LL | +LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - | -note: same as this + +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:54:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ +LL | +LL | } else if v.pop().is_none() { + | ^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:60:15 - | -LL | } else if v.len() == 42 { - | ^^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:59:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ +LL | +LL | } else if v.len() == 42 { + | ^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/search_is_some_fixable_none.fixed b/tests/ui/search_is_some_fixable_none.fixed index 847e5140d3e..cc4dbc919d8 100644 --- a/tests/ui/search_is_some_fixable_none.fixed +++ b/tests/ui/search_is_some_fixable_none.fixed @@ -214,10 +214,9 @@ mod issue7392 { } fn ref_bindings() { - let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some - let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter().any(|&&(&x, ref y)| x == *y); } fn test_string_1(s: &str) -> bool { diff --git a/tests/ui/search_is_some_fixable_none.rs b/tests/ui/search_is_some_fixable_none.rs index e976d12600c..fa31a9ddedc 100644 --- a/tests/ui/search_is_some_fixable_none.rs +++ b/tests/ui/search_is_some_fixable_none.rs @@ -221,10 +221,11 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter() + .find(|&&&(&x, ref y)| x == *y) + .is_none(); } fn test_string_1(s: &str) -> bool { diff --git a/tests/ui/search_is_some_fixable_none.stderr b/tests/ui/search_is_some_fixable_none.stderr index ccc17025222..b079cf7ea36 100644 --- a/tests/ui/search_is_some_fixable_none.stderr +++ b/tests/ui/search_is_some_fixable_none.stderr @@ -248,116 +248,122 @@ LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none(); error: called `is_none()` after searching an `Iterator` with `find` --> tests/ui/search_is_some_fixable_none.rs:224:17 | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` - -error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:226:17 +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + | _________________^ +LL | | +LL | | .iter() +LL | | .find(|&&&(&x, ref y)| x == *y) +LL | | .is_none(); + | |______________________^ + | +help: consider using + | +LL ~ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] +LL + +LL ~ .iter().any(|&&(&x, ref y)| x == *y); | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:246:17 + --> tests/ui/search_is_some_fixable_none.rs:247:17 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| s[0].is_empty())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:248:17 + --> tests/ui/search_is_some_fixable_none.rs:249:17 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| test_string_1(&s[0]))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:258:17 + --> tests/ui/search_is_some_fixable_none.rs:259:17 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| fp.field.is_power_of_two())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:260:17 + --> tests/ui/search_is_some_fixable_none.rs:261:17 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_1(fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:262:17 + --> tests/ui/search_is_some_fixable_none.rs:263:17 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:279:17 + --> tests/ui/search_is_some_fixable_none.rs:280:17 | LL | let _ = v.iter().find(|x| **x == 42).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:281:17 + --> tests/ui/search_is_some_fixable_none.rs:282:17 | LL | Foo.bar(v.iter().find(|x| **x == 42).is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:287:9 + --> tests/ui/search_is_some_fixable_none.rs:288:9 | LL | v.iter().find(|x| **x == 42).is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:293:9 + --> tests/ui/search_is_some_fixable_none.rs:294:9 | LL | v.iter().find(|x| **x == 42).is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:299:17 + --> tests/ui/search_is_some_fixable_none.rs:300:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:301:17 + --> tests/ui/search_is_some_fixable_none.rs:302:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:304:17 + --> tests/ui/search_is_some_fixable_none.rs:305:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:306:17 + --> tests/ui/search_is_some_fixable_none.rs:307:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:312:17 + --> tests/ui/search_is_some_fixable_none.rs:313:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:315:17 + --> tests/ui/search_is_some_fixable_none.rs:316:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:321:17 + --> tests/ui/search_is_some_fixable_none.rs:322:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:324:17 + --> tests/ui/search_is_some_fixable_none.rs:325:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` -error: aborting due to 55 previous errors +error: aborting due to 54 previous errors diff --git a/tests/ui/search_is_some_fixable_none_2021.fixed b/tests/ui/search_is_some_fixable_none_2021.fixed new file mode 100644 index 00000000000..6e15244901c --- /dev/null +++ b/tests/ui/search_is_some_fixable_none_2021.fixed @@ -0,0 +1,14 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter().any(|&&(&x, ref y)| x == *y); + } +} diff --git a/tests/ui/search_is_some_fixable_none_2021.rs b/tests/ui/search_is_some_fixable_none_2021.rs new file mode 100644 index 00000000000..4b1db3f9fc3 --- /dev/null +++ b/tests/ui/search_is_some_fixable_none_2021.rs @@ -0,0 +1,16 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter() + .find(|&&&(&x, ref y)| x == *y) + .is_none(); + } +} diff --git a/tests/ui/search_is_some_fixable_none_2021.stderr b/tests/ui/search_is_some_fixable_none_2021.stderr new file mode 100644 index 00000000000..af93be1a707 --- /dev/null +++ b/tests/ui/search_is_some_fixable_none_2021.stderr @@ -0,0 +1,35 @@ +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:6:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` + +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:8:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:10:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + | _________________^ +LL | | +LL | | .iter() +LL | | .find(|&&&(&x, ref y)| x == *y) +LL | | .is_none(); + | |______________________^ + | +help: consider using + | +LL ~ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] +LL + +LL ~ .iter().any(|&&(&x, ref y)| x == *y); + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/search_is_some_fixable_some.fixed b/tests/ui/search_is_some_fixable_some.fixed index 05e88b8528f..42b39b33b57 100644 --- a/tests/ui/search_is_some_fixable_some.fixed +++ b/tests/ui/search_is_some_fixable_some.fixed @@ -214,10 +214,9 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + .iter() + .any(|&&(&x, ref y)| x == *y); } fn test_string_1(s: &str) -> bool { diff --git a/tests/ui/search_is_some_fixable_some.rs b/tests/ui/search_is_some_fixable_some.rs index caab816f243..ca4f4d941cb 100644 --- a/tests/ui/search_is_some_fixable_some.rs +++ b/tests/ui/search_is_some_fixable_some.rs @@ -220,10 +220,11 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + .iter() + .find(|&&&(&x, ref y)| x == *y) + //~^ search_is_some + .is_some(); } fn test_string_1(s: &str) -> bool { diff --git a/tests/ui/search_is_some_fixable_some.stderr b/tests/ui/search_is_some_fixable_some.stderr index af719b78831..8291f48d43c 100644 --- a/tests/ui/search_is_some_fixable_some.stderr +++ b/tests/ui/search_is_some_fixable_some.stderr @@ -227,70 +227,67 @@ LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:223:55 + --> tests/ui/search_is_some_fixable_some.rs:225:14 | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` - -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:225:55 - | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` +LL | .find(|&&&(&x, ref y)| x == *y) + | ______________^ +LL | | +LL | | .is_some(); + | |______________________^ help: consider using: `any(|&&(&x, ref y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:245:26 + --> tests/ui/search_is_some_fixable_some.rs:246:26 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:247:26 + --> tests/ui/search_is_some_fixable_some.rs:248:26 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:257:26 + --> tests/ui/search_is_some_fixable_some.rs:258:26 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:259:26 + --> tests/ui/search_is_some_fixable_some.rs:260:26 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:261:26 + --> tests/ui/search_is_some_fixable_some.rs:262:26 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:277:18 + --> tests/ui/search_is_some_fixable_some.rs:278:18 | LL | v.iter().find(|x: &&u32| func(x)).is_some() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:287:26 + --> tests/ui/search_is_some_fixable_some.rs:288:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:291:26 + --> tests/ui/search_is_some_fixable_some.rs:292:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:295:26 + --> tests/ui/search_is_some_fixable_some.rs:296:26 | LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` -error: aborting due to 47 previous errors +error: aborting due to 46 previous errors diff --git a/tests/ui/search_is_some_fixable_some_2021.fixed b/tests/ui/search_is_some_fixable_some_2021.fixed new file mode 100644 index 00000000000..d2b05db562a --- /dev/null +++ b/tests/ui/search_is_some_fixable_some_2021.fixed @@ -0,0 +1,11 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + } +} diff --git a/tests/ui/search_is_some_fixable_some_2021.rs b/tests/ui/search_is_some_fixable_some_2021.rs new file mode 100644 index 00000000000..c3f5ef769da --- /dev/null +++ b/tests/ui/search_is_some_fixable_some_2021.rs @@ -0,0 +1,11 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); + //~^ search_is_some + } +} diff --git a/tests/ui/search_is_some_fixable_some_2021.stderr b/tests/ui/search_is_some_fixable_some_2021.stderr new file mode 100644 index 00000000000..91d9540e6fc --- /dev/null +++ b/tests/ui/search_is_some_fixable_some_2021.stderr @@ -0,0 +1,17 @@ +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some_2021.rs:6:55 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` + +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some_2021.rs:8:55 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/single_call_fn.rs b/tests/ui/single_call_fn.rs index c1cc4032bec..a1ecd7bc166 100644 --- a/tests/ui/single_call_fn.rs +++ b/tests/ui/single_call_fn.rs @@ -94,7 +94,7 @@ trait Trait { //~^ single_call_fn fn foo(&self); } -extern "C" { +unsafe extern "C" { // test some kind of foreign item fn rand() -> std::ffi::c_int; } diff --git a/tests/ui/swap_with_temporary.fixed b/tests/ui/swap_with_temporary.fixed new file mode 100644 index 00000000000..4007d998ba0 --- /dev/null +++ b/tests/ui/swap_with_temporary.fixed @@ -0,0 +1,74 @@ +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + // No lint + swap(&mut x, &mut y); + + y = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + x = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + *z = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint + swap(z, func_returning_refmut(&mut x)); + + swap(&mut y, z); + + *z = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (funcall $f:ident) => { + $f() + }; + (wholeexpr) => { + swap(&mut 42, &mut 0) + }; + (ident $v:ident) => { + $v + }; + } + *z = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(ident z) = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(ident z) = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(refmut y) = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint if it comes from a macro as it may depend on the arguments + mac!(wholeexpr); +} + +struct S { + t: String, +} + +fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { + swap(&mut s.t, &mut v[0]); + swap(&mut s.t, v.get_mut(0).unwrap()); + swap(w.unwrap(), &mut s.t); +} diff --git a/tests/ui/swap_with_temporary.rs b/tests/ui/swap_with_temporary.rs new file mode 100644 index 00000000000..d403c086c0f --- /dev/null +++ b/tests/ui/swap_with_temporary.rs @@ -0,0 +1,74 @@ +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + // No lint + swap(&mut x, &mut y); + + swap(&mut func(), &mut y); + //~^ ERROR: swapping with a temporary value is inefficient + + swap(&mut x, &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + swap(z, &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint + swap(z, func_returning_refmut(&mut x)); + + swap(&mut y, z); + + swap(&mut func(), z); + //~^ ERROR: swapping with a temporary value is inefficient + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (funcall $f:ident) => { + $f() + }; + (wholeexpr) => { + swap(&mut 42, &mut 0) + }; + (ident $v:ident) => { + $v + }; + } + swap(&mut mac!(funcall func), z); + //~^ ERROR: swapping with a temporary value is inefficient + swap(&mut mac!(funcall func), mac!(ident z)); + //~^ ERROR: swapping with a temporary value is inefficient + swap(mac!(ident z), &mut mac!(funcall func)); + //~^ ERROR: swapping with a temporary value is inefficient + swap(mac!(refmut y), &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint if it comes from a macro as it may depend on the arguments + mac!(wholeexpr); +} + +struct S { + t: String, +} + +fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { + swap(&mut s.t, &mut v[0]); + swap(&mut s.t, v.get_mut(0).unwrap()); + swap(w.unwrap(), &mut s.t); +} diff --git a/tests/ui/swap_with_temporary.stderr b/tests/ui/swap_with_temporary.stderr new file mode 100644 index 00000000000..59355771a96 --- /dev/null +++ b/tests/ui/swap_with_temporary.stderr @@ -0,0 +1,100 @@ +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:22:5 + | +LL | swap(&mut func(), &mut y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `y = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:22:15 + | +LL | swap(&mut func(), &mut y); + | ^^^^^^ + = note: `-D clippy::swap-with-temporary` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::swap_with_temporary)]` + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:25:5 + | +LL | swap(&mut x, &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `x = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:25:23 + | +LL | swap(&mut x, &mut func()); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:28:5 + | +LL | swap(z, &mut func()); + | ^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:28:18 + | +LL | swap(z, &mut func()); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:36:5 + | +LL | swap(&mut func(), z); + | ^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:36:15 + | +LL | swap(&mut func(), z); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:53:5 + | +LL | swap(&mut mac!(funcall func), z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:53:15 + | +LL | swap(&mut mac!(funcall func), z); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:55:5 + | +LL | swap(&mut mac!(funcall func), mac!(ident z)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(ident z) = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:55:15 + | +LL | swap(&mut mac!(funcall func), mac!(ident z)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:57:5 + | +LL | swap(mac!(ident z), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(ident z) = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:57:30 + | +LL | swap(mac!(ident z), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:59:5 + | +LL | swap(mac!(refmut y), &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(refmut y) = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:59:31 + | +LL | swap(mac!(refmut y), &mut func()); + | ^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/swap_with_temporary_unfixable.rs b/tests/ui/swap_with_temporary_unfixable.rs new file mode 100644 index 00000000000..a974ca82abf --- /dev/null +++ b/tests/ui/swap_with_temporary_unfixable.rs @@ -0,0 +1,62 @@ +//@no-rustfix +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + swap(&mut func(), &mut func()); + //~^ ERROR: swapping temporary values has no effect + + if matches!(swap(&mut func(), &mut func()), ()) { + //~^ ERROR: swapping temporary values has no effect + println!("Yeah"); + } + + if matches!(swap(z, &mut func()), ()) { + //~^ ERROR: swapping with a temporary value is inefficient + println!("Yeah"); + } + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (refmut) => { + mac!(refmut String::new()) + }; + (funcall $f:ident) => { + $f() + }; + } + + swap(mac!(refmut func()), z); + //~^ ERROR: swapping with a temporary value is inefficient + swap(&mut mac!(funcall func), &mut mac!(funcall func)); + //~^ ERROR: swapping temporary values has no effect + swap(mac!(refmut), mac!(refmut)); + //~^ ERROR: swapping temporary values has no effect + swap(mac!(refmut y), mac!(refmut)); + //~^ ERROR: swapping with a temporary value is inefficient +} + +fn bug(v1: &mut [i32], v2: &mut [i32]) { + // Incorrect: swapping temporary references (`&mut &mut` passed to swap) + std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + //~^ ERROR: swapping temporary values has no effect + + // Correct + std::mem::swap(v1.last_mut().unwrap(), v2.last_mut().unwrap()); +} diff --git a/tests/ui/swap_with_temporary_unfixable.stderr b/tests/ui/swap_with_temporary_unfixable.stderr new file mode 100644 index 00000000000..856c5415d67 --- /dev/null +++ b/tests/ui/swap_with_temporary_unfixable.stderr @@ -0,0 +1,125 @@ +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:20:5 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:20:15 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:20:28 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^ + = note: `-D clippy::swap-with-temporary` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::swap_with_temporary)]` + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:23:17 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:23:27 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:23:40 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:28:17 + | +LL | if matches!(swap(z, &mut func()), ()) { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:28:30 + | +LL | if matches!(swap(z, &mut func()), ()) { + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:45:5 + | +LL | swap(mac!(refmut func()), z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:45:10 + | +LL | swap(mac!(refmut func()), z); + | ^^^^^^^^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:47:5 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:47:15 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:47:40 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:49:5 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:49:10 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^ +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:49:24 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:51:5 + | +LL | swap(mac!(refmut y), mac!(refmut)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:51:26 + | +LL | swap(mac!(refmut y), mac!(refmut)); + | ^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:57:5 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:57:25 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:57:54 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/to_digit_is_some.fixed b/tests/ui/to_digit_is_some.fixed index 627d54c5f73..ff6b32e6bd1 100644 --- a/tests/ui/to_digit_is_some.fixed +++ b/tests/ui/to_digit_is_some.fixed @@ -9,3 +9,20 @@ fn main() { let _ = char::is_digit(c, 8); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some +} diff --git a/tests/ui/to_digit_is_some.rs b/tests/ui/to_digit_is_some.rs index d4eccc9931f..5ba08617433 100644 --- a/tests/ui/to_digit_is_some.rs +++ b/tests/ui/to_digit_is_some.rs @@ -9,3 +9,20 @@ fn main() { let _ = char::to_digit(c, 8).is_some(); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some +} diff --git a/tests/ui/to_digit_is_some.stderr b/tests/ui/to_digit_is_some.stderr index f41382a60d5..5ffedb4683f 100644 --- a/tests/ui/to_digit_is_some.stderr +++ b/tests/ui/to_digit_is_some.stderr @@ -13,5 +13,17 @@ error: use of `.to_digit(..).is_some()` LL | let _ = char::to_digit(c, 8).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `char::is_digit(c, 8)` -error: aborting due to 2 previous errors +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:16:9 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:26:5 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: aborting due to 4 previous errors diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 1ec70db9c3c..e968e7a5924 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -3,6 +3,7 @@ #![allow( dead_code, clippy::borrow_as_ptr, + unnecessary_transmutes, clippy::needless_lifetimes, clippy::missing_transmute_annotations )] @@ -23,19 +24,21 @@ fn my_vec() -> MyVec<i32> { #[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { - // FIXME: should lint - // let _: &'a T = core::mem::transmute(t); + unsafe { + // FIXME: should lint + // let _: &'a T = core::mem::transmute(t); - let _: &'a U = core::mem::transmute(t); + let _: &'a U = core::mem::transmute(t); - let _: *const T = core::mem::transmute(t); - //~^ useless_transmute + let _: *const T = core::mem::transmute(t); + //~^ useless_transmute - let _: *mut T = core::mem::transmute(t); - //~^ useless_transmute + let _: *mut T = core::mem::transmute(t); + //~^ useless_transmute - let _: *const U = core::mem::transmute(t); - //~^ useless_transmute + let _: *const U = core::mem::transmute(t); + //~^ useless_transmute + } } #[warn(clippy::useless_transmute)] @@ -68,19 +71,19 @@ fn useless() { } unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator<Item = u32> + 'a)) -> *const (dyn Iterator<Item = u32> + 'b) { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } } @@ -113,138 +116,6 @@ fn int_to_bool() { //~^ transmute_int_to_bool } -#[warn(clippy::transmute_int_to_float)] -mod int_to_float { - fn test() { - let _: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - let _: f16 = unsafe { std::mem::transmute(0_i16) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_u64) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_u128) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - } - - mod issue_5747 { - const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - - const fn from_bits_16(v: i16) -> f16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_32(v: i32) -> f32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_64(v: u64) -> f64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_128(v: u128) -> f128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - } -} - -mod num_to_bytes { - fn test() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } - const fn test_const() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } -} - fn bytes_to_str(mb: &mut [u8]) { const B: &[u8] = b""; diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index e0d28437aaf..79528ec06f1 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,68 +1,68 @@ error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:31:23 + --> tests/ui/transmute.rs:33:27 | -LL | let _: *const T = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` +LL | let _: *const T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` | = note: `-D clippy::useless-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:34:21 + --> tests/ui/transmute.rs:36:25 | -LL | let _: *mut T = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` +LL | let _: *mut T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:37:23 + --> tests/ui/transmute.rs:39:27 | -LL | let _: *const U = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` +LL | let _: *const U = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:44:27 + --> tests/ui/transmute.rs:47:27 | LL | let _: Vec<i32> = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:47:27 + --> tests/ui/transmute.rs:50:27 | LL | let _: Vec<i32> = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:50:27 + --> tests/ui/transmute.rs:53:27 | LL | let _: Vec<i32> = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:53:27 + --> tests/ui/transmute.rs:56:27 | LL | let _: Vec<i32> = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:56:27 + --> tests/ui/transmute.rs:59:27 | LL | let _: Vec<i32> = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:59:31 + --> tests/ui/transmute.rs:62:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:64:31 + --> tests/ui/transmute.rs:67:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:96:24 + --> tests/ui/transmute.rs:99:24 | LL | let _: Usize = core::mem::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +71,25 @@ LL | let _: Usize = core::mem::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:99:24 + --> tests/ui/transmute.rs:102:24 | LL | let _: Usize = core::mem::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> tests/ui/transmute.rs:102:31 + --> tests/ui/transmute.rs:105:31 | LL | let _: *const Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> tests/ui/transmute.rs:105:29 + --> tests/ui/transmute.rs:108:29 | LL | let _: *mut Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> tests/ui/transmute.rs:112:28 + --> tests/ui/transmute.rs:115:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -97,230 +97,8 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:119:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - | - = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:122:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_i16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_i16 as u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:125:31 - | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:128:31 - | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` - -error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:131:31 - | -LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` - -error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:134:31 - | -LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:137:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_u128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_u128)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:140:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:145:39 - | -LL | const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:148:39 - | -LL | const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:151:39 - | -LL | const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:154:41 - | -LL | const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:158:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(v as u16)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:163:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(v as u32)` - -error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:168:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(v)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:173:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(v)` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:182:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - | - = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:185:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:188:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:191:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:194:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:197:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:200:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:203:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:206:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:209:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:215:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:218:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:221:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:224:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:227:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:230:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:233:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:236:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:239:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:242:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:251:28 + --> tests/ui/transmute.rs:122:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -329,16 +107,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:254:32 + --> tests/ui/transmute.rs:125:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:257:30 + --> tests/ui/transmute.rs:128:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 54 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/transmute_float_to_int.fixed b/tests/ui/transmute_float_to_int.fixed deleted file mode 100644 index 1f97b997eaa..00000000000 --- a/tests/ui/transmute_float_to_int.fixed +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { 1f32.to_bits() }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { 1f64.to_bits() as i64 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1.0f64.to_bits() }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { (-1.0f64).to_bits() }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { 1f16.to_bits() as i16 }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { 1f128.to_bits() }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { v.to_bits() as i64 } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { v.to_bits() as i128 } - //~^ transmute_float_to_int - } -} - -fn main() {} diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs deleted file mode 100644 index 788a7e1026c..00000000000 --- a/tests/ui/transmute_float_to_int.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1.0) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(-1.0) }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } -} - -fn main() {} diff --git a/tests/ui/transmute_float_to_int.stderr b/tests/ui/transmute_float_to_int.stderr deleted file mode 100644 index 223cbc4e90c..00000000000 --- a/tests/ui/transmute_float_to_int.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:7:27 - | -LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` - | - = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_float_to_int)]` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:10:27 - | -LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:13:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:16:27 - | -LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:19:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:22:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` - -error: transmute from a `f16` to a `i16` - --> tests/ui/transmute_float_to_int.rs:27:35 - | -LL | const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f16.to_bits() as i16` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:30:35 - | -LL | const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:33:35 - | -LL | const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f128` to a `u128` - --> tests/ui/transmute_float_to_int.rs:36:37 - | -LL | const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f128.to_bits()` - -error: transmute from a `f16` to a `u16` - --> tests/ui/transmute_float_to_int.rs:40:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:45:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:50:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i64` - -error: transmute from a `f128` to a `i128` - --> tests/ui/transmute_float_to_int.rs:55:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i128` - -error: aborting due to 14 previous errors - diff --git a/tests/ui/transmute_int_to_char.fixed b/tests/ui/transmute_int_to_char.fixed deleted file mode 100644 index b5425a2e9e8..00000000000 --- a/tests/ui/transmute_int_to_char.fixed +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -fn int_to_char() { - let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char.rs b/tests/ui/transmute_int_to_char.rs deleted file mode 100644 index b24bb177c9f..00000000000 --- a/tests/ui/transmute_int_to_char.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -fn int_to_char() { - let _: char = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char.stderr b/tests/ui/transmute_int_to_char.stderr deleted file mode 100644 index e3a3620f28b..00000000000 --- a/tests/ui/transmute_int_to_char.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char.rs:5:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char.rs:8:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/transmute_int_to_char_no_std.fixed b/tests/ui/transmute_int_to_char_no_std.fixed deleted file mode 100644 index e525751e306..00000000000 --- a/tests/ui/transmute_int_to_char_no_std.fixed +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char_no_std.rs b/tests/ui/transmute_int_to_char_no_std.rs deleted file mode 100644 index 7cb508ceaf3..00000000000 --- a/tests/ui/transmute_int_to_char_no_std.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char_no_std.stderr b/tests/ui/transmute_int_to_char_no_std.stderr deleted file mode 100644 index d94580a84d7..00000000000 --- a/tests/ui/transmute_int_to_char_no_std.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:17:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:20:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/transmute_ptr_to_ptr.fixed b/tests/ui/transmute_ptr_to_ptr.fixed index 3a67be5f45d..476e7e35a1f 100644 --- a/tests/ui/transmute_ptr_to_ptr.fixed +++ b/tests/ui/transmute_ptr_to_ptr.fixed @@ -8,12 +8,12 @@ use std::mem::transmute; // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - transmute::<&'a T, &'static T>(t) + unsafe { transmute::<&'a T, &'static T>(t) } } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - transmute::<&'a T, &'b T>(t) + unsafe { transmute::<&'a T, &'b T>(t) } } struct LifetimeParam<'a> { diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 01ad3a3296b..7356668bcab 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -8,12 +8,12 @@ use std::mem::transmute; // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - transmute::<&'a T, &'static T>(t) + unsafe { transmute::<&'a T, &'static T>(t) } } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - transmute::<&'a T, &'b T>(t) + unsafe { transmute::<&'a T, &'b T>(t) } } struct LifetimeParam<'a> { diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index 1bd45bc10a3..61e3ac2fe88 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -6,33 +6,35 @@ )] unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) { - let _: &T = &*p; - //~^ transmute_ptr_to_ref - let _: &T = &*p; + unsafe { + let _: &T = &*p; + //~^ transmute_ptr_to_ref + let _: &T = &*p; - let _: &mut T = &mut *m; - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *m; + let _: &mut T = &mut *m; + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *m; - let _: &T = &*m; - //~^ transmute_ptr_to_ref - let _: &T = &*m; + let _: &T = &*m; + //~^ transmute_ptr_to_ref + let _: &T = &*m; - let _: &mut T = &mut *(p as *mut T); - //~^ transmute_ptr_to_ref - let _ = &mut *(p as *mut T); + let _: &mut T = &mut *(p as *mut T); + //~^ transmute_ptr_to_ref + let _ = &mut *(p as *mut T); - let _: &T = &*(o as *const T); - //~^ transmute_ptr_to_ref - let _: &T = &*(o as *const T); + let _: &T = &*(o as *const T); + //~^ transmute_ptr_to_ref + let _: &T = &*(o as *const T); - let _: &mut T = &mut *(om as *mut T); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *(om as *mut T); + let _: &mut T = &mut *(om as *mut T); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *(om as *mut T); - let _: &T = &*(om as *const T); - //~^ transmute_ptr_to_ref - let _: &T = &*(om as *const T); + let _: &T = &*(om as *const T); + //~^ transmute_ptr_to_ref + let _: &T = &*(om as *const T); + } } fn _issue1231() { @@ -54,47 +56,53 @@ fn _issue1231() { } unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { - match 0 { - 0 => &*x.cast::<&u32>(), - //~^ transmute_ptr_to_ref - 1 => &*y.cast::<&u32>(), - //~^ transmute_ptr_to_ref - 2 => &*x.cast::<&'b u32>(), - //~^ transmute_ptr_to_ref - _ => &*y.cast::<&'b u32>(), - //~^ transmute_ptr_to_ref + unsafe { + match 0 { + 0 => &*x.cast::<&u32>(), + //~^ transmute_ptr_to_ref + 1 => &*y.cast::<&u32>(), + //~^ transmute_ptr_to_ref + 2 => &*x.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + _ => &*y.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = &*a; - //~^ transmute_ptr_to_ref - let _: &u32 = &*a.cast::<u32>(); - //~^ transmute_ptr_to_ref - match 0 { - 0 => &*x.cast::<&u32>(), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; //~^ transmute_ptr_to_ref - _ => &*x.cast::<&'b u32>(), + let _: &u32 = &*a.cast::<u32>(); //~^ transmute_ptr_to_ref + match 0 { + 0 => &*x.cast::<&u32>(), + //~^ transmute_ptr_to_ref + _ => &*x.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = &*a; - //~^ transmute_ptr_to_ref - let _: &u32 = &*(a as *const u32); - //~^ transmute_ptr_to_ref - match 0 { - 0 => &*(x as *const () as *const &u32), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; //~^ transmute_ptr_to_ref - _ => &*(x as *const () as *const &'b u32), + let _: &u32 = &*(a as *const u32); //~^ transmute_ptr_to_ref + match 0 { + 0 => &*(x as *const () as *const &u32), + //~^ transmute_ptr_to_ref + _ => &*(x as *const () as *const &'b u32), + //~^ transmute_ptr_to_ref + } } } diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index cbe64bf1ea6..48e2f527b55 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -6,33 +6,35 @@ )] unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) { - let _: &T = std::mem::transmute(p); - //~^ transmute_ptr_to_ref - let _: &T = &*p; + unsafe { + let _: &T = std::mem::transmute(p); + //~^ transmute_ptr_to_ref + let _: &T = &*p; - let _: &mut T = std::mem::transmute(m); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *m; + let _: &mut T = std::mem::transmute(m); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *m; - let _: &T = std::mem::transmute(m); - //~^ transmute_ptr_to_ref - let _: &T = &*m; + let _: &T = std::mem::transmute(m); + //~^ transmute_ptr_to_ref + let _: &T = &*m; - let _: &mut T = std::mem::transmute(p as *mut T); - //~^ transmute_ptr_to_ref - let _ = &mut *(p as *mut T); + let _: &mut T = std::mem::transmute(p as *mut T); + //~^ transmute_ptr_to_ref + let _ = &mut *(p as *mut T); - let _: &T = std::mem::transmute(o); - //~^ transmute_ptr_to_ref - let _: &T = &*(o as *const T); + let _: &T = std::mem::transmute(o); + //~^ transmute_ptr_to_ref + let _: &T = &*(o as *const T); - let _: &mut T = std::mem::transmute(om); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *(om as *mut T); + let _: &mut T = std::mem::transmute(om); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *(om as *mut T); - let _: &T = std::mem::transmute(om); - //~^ transmute_ptr_to_ref - let _: &T = &*(om as *const T); + let _: &T = std::mem::transmute(om); + //~^ transmute_ptr_to_ref + let _: &T = &*(om as *const T); + } } fn _issue1231() { @@ -54,47 +56,53 @@ fn _issue1231() { } unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { - match 0 { - 0 => std::mem::transmute(x), - //~^ transmute_ptr_to_ref - 1 => std::mem::transmute(y), - //~^ transmute_ptr_to_ref - 2 => std::mem::transmute::<_, &&'b u32>(x), - //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(y), - //~^ transmute_ptr_to_ref + unsafe { + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + 1 => std::mem::transmute(y), + //~^ transmute_ptr_to_ref + 2 => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(y), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = std::mem::transmute(a); - //~^ transmute_ptr_to_ref - let _: &u32 = std::mem::transmute::<_, &u32>(a); - //~^ transmute_ptr_to_ref - match 0 { - 0 => std::mem::transmute(x), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(x), + let _: &u32 = std::mem::transmute::<_, &u32>(a); //~^ transmute_ptr_to_ref + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = std::mem::transmute(a); - //~^ transmute_ptr_to_ref - let _: &u32 = std::mem::transmute::<_, &u32>(a); - //~^ transmute_ptr_to_ref - match 0 { - 0 => std::mem::transmute(x), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(x), + let _: &u32 = std::mem::transmute::<_, &u32>(a); //~^ transmute_ptr_to_ref + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + } } } diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 7fad9b4065a..7685c345c86 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -1,137 +1,137 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:9:17 + --> tests/ui/transmute_ptr_to_ref.rs:10:21 | -LL | let _: &T = std::mem::transmute(p); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` +LL | let _: &T = std::mem::transmute(p); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` | = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ref)]` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:13:21 + --> tests/ui/transmute_ptr_to_ref.rs:14:25 | -LL | let _: &mut T = std::mem::transmute(m); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` +LL | let _: &mut T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:17:17 + --> tests/ui/transmute_ptr_to_ref.rs:18:21 | -LL | let _: &T = std::mem::transmute(m); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` +LL | let _: &T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:21:21 + --> tests/ui/transmute_ptr_to_ref.rs:22:25 | -LL | let _: &mut T = std::mem::transmute(p as *mut T); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` +LL | let _: &mut T = std::mem::transmute(p as *mut T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:25:17 + --> tests/ui/transmute_ptr_to_ref.rs:26:21 | -LL | let _: &T = std::mem::transmute(o); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` +LL | let _: &T = std::mem::transmute(o); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:29:21 + --> tests/ui/transmute_ptr_to_ref.rs:30:25 | -LL | let _: &mut T = std::mem::transmute(om); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` +LL | let _: &mut T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:33:17 + --> tests/ui/transmute_ptr_to_ref.rs:34:21 | -LL | let _: &T = std::mem::transmute(om); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` +LL | let _: &T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:44:32 + --> tests/ui/transmute_ptr_to_ref.rs:46:32 | LL | let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<_>>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:47:33 + --> tests/ui/transmute_ptr_to_ref.rs:49:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<&_>>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> tests/ui/transmute_ptr_to_ref.rs:52:14 + --> tests/ui/transmute_ptr_to_ref.rs:54:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:58:14 + --> tests/ui/transmute_ptr_to_ref.rs:61:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:60:14 + --> tests/ui/transmute_ptr_to_ref.rs:63:18 | -LL | 1 => std::mem::transmute(y), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` +LL | 1 => std::mem::transmute(y), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:62:14 + --> tests/ui/transmute_ptr_to_ref.rs:65:18 | -LL | 2 => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` +LL | 2 => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:64:14 + --> tests/ui/transmute_ptr_to_ref.rs:67:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(y), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` +LL | _ => std::mem::transmute::<_, &&'b u32>(y), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:73:19 + --> tests/ui/transmute_ptr_to_ref.rs:78:23 | -LL | let _: &u32 = std::mem::transmute(a); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:75:19 + --> tests/ui/transmute_ptr_to_ref.rs:80:23 | -LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()` +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:78:14 + --> tests/ui/transmute_ptr_to_ref.rs:83:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:80:14 + --> tests/ui/transmute_ptr_to_ref.rs:85:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:89:19 + --> tests/ui/transmute_ptr_to_ref.rs:96:23 | -LL | let _: &u32 = std::mem::transmute(a); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:91:19 + --> tests/ui/transmute_ptr_to_ref.rs:98:23 | -LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:94:14 + --> tests/ui/transmute_ptr_to_ref.rs:101:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:96:14 + --> tests/ui/transmute_ptr_to_ref.rs:103:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` error: aborting due to 22 previous errors diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index d325887bfba..e75678d5fd9 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -12,7 +12,7 @@ pub fn foo<T>(_t: T) where T: Copy, T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { unimplemented!(); } @@ -30,7 +30,7 @@ trait LintBounds where Self: Clone, Self: Copy + Default + Ord, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign, Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign, { @@ -105,13 +105,13 @@ where pub fn f<T: ?Sized>() where T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } pub fn g<T: Clone>() where T: ?Sized, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } @@ -137,10 +137,27 @@ mod issue8772_pass { pub fn f<T: ?Sized, U>(arg: usize) where T: Trait<Option<usize>, Box<[String]>, bool> + 'static, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds U: Clone + Sync + 'static, { } } +struct Issue14744<'a, K: 'a> +where + K: Clone, +{ + phantom: std::marker::PhantomData<&'a K>, +} +//~^^^^ type_repetition_in_bounds + +struct ComplexType<T> +where + Vec<T>: Clone, + Vec<T>: Clone, +{ + t: T, +} +//~^^^^ type_repetition_in_bounds + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 77944c95045..de1b14da198 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:14:5 | LL | T: Clone, @@ -11,7 +11,7 @@ note: the lint level is defined here LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this type has already been used as a bound predicate +error: type `Self` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:32:5 | LL | Self: Copy + Default + Ord, @@ -19,7 +19,7 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:107:5 | LL | T: Clone, @@ -27,7 +27,7 @@ LL | T: Clone, | = help: consider combining the bounds: `T: ?Sized + Clone` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:113:5 | LL | T: ?Sized, @@ -35,13 +35,29 @@ LL | T: ?Sized, | = help: consider combining the bounds: `T: Clone + ?Sized` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:139:9 | LL | T: Trait<Option<usize>, Box<[String]>, bool> + 'static, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider combining the bounds: `T: ?Sized + Trait<Option<usize>, Box<[String]>, bool>` + = help: consider combining the bounds: `T: ?Sized + Trait<Option<usize>, Box<[String]>, bool> + 'static` -error: aborting due to 5 previous errors +error: type `K` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:148:5 + | +LL | K: Clone, + | ^^^^^^^^ + | + = help: consider combining the bounds: `K: 'a + Clone` + +error: type `Vec<T>` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:157:5 + | +LL | Vec<T>: Clone, + | ^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `Vec<T>: Clone + Clone` + +error: aborting due to 7 previous errors diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index a4839713799..eeb281322da 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -15,9 +15,17 @@ union MyOwnMaybeUninit { // https://github.com/rust-lang/rust/issues/119620 unsafe fn requires_paramenv<S>() { - let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1); + unsafe { + let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1); + //~^ uninit_vec + vec.set_len(1); + } + + let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(2); //~^ uninit_vec - vec.set_len(1); + unsafe { + vec.set_len(2); + } } fn main() { diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index 7ff6140a2c3..1b821ef004e 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -1,18 +1,29 @@ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:18:5 + --> tests/ui/uninit_vec.rs:24:5 | -LL | let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1); +LL | let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | vec.set_len(1); - | ^^^^^^^^^^^^^^ +... +LL | vec.set_len(2); + | ^^^^^^^^^^^^^^ | = help: initialize the buffer or wrap the content in `MaybeUninit` = note: `-D clippy::uninit-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:25:5 + --> tests/ui/uninit_vec.rs:19:9 + | +LL | let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:33:5 | LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +34,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:33:5 + --> tests/ui/uninit_vec.rs:41:5 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -34,7 +45,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:41:5 + --> tests/ui/uninit_vec.rs:49:5 | LL | let mut vec: Vec<u8> = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +54,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:49:5 + --> tests/ui/uninit_vec.rs:57:5 | LL | let mut vec: Vec<u8> = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +63,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:56:5 + --> tests/ui/uninit_vec.rs:64:5 | LL | let mut vec: Vec<u8> = Vec::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +72,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:76:5 + --> tests/ui/uninit_vec.rs:84:5 | LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +83,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:87:5 + --> tests/ui/uninit_vec.rs:95:5 | LL | my_vec.vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +94,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:94:5 + --> tests/ui/uninit_vec.rs:102:5 | LL | my_vec.vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +105,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:65:9 + --> tests/ui/uninit_vec.rs:73:9 | LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +116,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:70:9 + --> tests/ui/uninit_vec.rs:78:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -116,7 +127,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:150:9 + --> tests/ui/uninit_vec.rs:158:9 | LL | let mut vec: Vec<T> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +138,7 @@ LL | vec.set_len(10); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:178:9 + --> tests/ui/uninit_vec.rs:186:9 | LL | let mut vec: Vec<Recursive<T>> = Vec::with_capacity(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +149,7 @@ LL | vec.set_len(1); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:192:9 + --> tests/ui/uninit_vec.rs:200:9 | LL | let mut vec: Vec<Enum<T>> = Vec::with_capacity(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,5 +159,5 @@ LL | vec.set_len(1); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index ba167e79a30..91ff4b9ee77 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -266,7 +266,21 @@ mod fixable { // Issue #11968: The suggestion for this lint removes the parentheses and leave the code as // `*x.pow(2)` which tries to dereference the return value rather than `x`. fn issue_11968(x: &usize) -> usize { - { *x }.pow(2) + (*x).pow(2) + //~^ unnecessary_cast + } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec<u64> = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x); + //~^ unnecessary_cast + + let _ = (5i32 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64; //~^ unnecessary_cast } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 0f90a8b0596..5444a914db1 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -269,4 +269,18 @@ mod fixable { (*x as usize).pow(2) //~^ unnecessary_cast } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec<u64> = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x as usize); + //~^ unnecessary_cast + + let _ = (5i32 as i64 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64 as i64; + //~^ unnecessary_cast + } } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index c83770c1a29..3e3c5eb81c1 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -245,7 +245,25 @@ error: casting to the same type is unnecessary (`usize` -> `usize`) --> tests/ui/unnecessary_cast.rs:269:9 | LL | (*x as usize).pow(2) - | ^^^^^^^^^^^^^ help: try: `{ *x }` + | ^^^^^^^^^^^^^ help: try: `(*x)` -error: aborting due to 41 previous errors +error: casting to the same type is unnecessary (`usize` -> `usize`) + --> tests/ui/unnecessary_cast.rs:277:31 + | +LL | assert_eq!(vec.len(), x as usize); + | ^^^^^^^^^^ help: try: `x` + +error: casting to the same type is unnecessary (`i64` -> `i64`) + --> tests/ui/unnecessary_cast.rs:280:17 + | +LL | let _ = (5i32 as i64 as i64).abs(); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` + +error: casting to the same type is unnecessary (`i64` -> `i64`) + --> tests/ui/unnecessary_cast.rs:283:17 + | +LL | let _ = 5i32 as i64 as i64; + | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` + +error: aborting due to 44 previous errors diff --git a/tests/ui/unnecessary_cast_unfixable.rs b/tests/ui/unnecessary_cast_unfixable.rs index 2bb64c3e80e..4b1f4f76cc4 100644 --- a/tests/ui/unnecessary_cast_unfixable.rs +++ b/tests/ui/unnecessary_cast_unfixable.rs @@ -17,8 +17,10 @@ mod issue11113 { impl TearOff { unsafe fn query(&self) { - ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() - //~^ unnecessary_cast + unsafe { + ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + //~^ unnecessary_cast + } } } } diff --git a/tests/ui/unnecessary_cast_unfixable.stderr b/tests/ui/unnecessary_cast_unfixable.stderr index 6ba1c787306..6b26bea9de2 100644 --- a/tests/ui/unnecessary_cast_unfixable.stderr +++ b/tests/ui/unnecessary_cast_unfixable.stderr @@ -8,10 +8,10 @@ LL | let _ = std::ptr::null() as *const u8; = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` error: casting raw pointers to the same type and constness is unnecessary (`*mut issue11113::Vtbl` -> `*mut issue11113::Vtbl`) - --> tests/ui/unnecessary_cast_unfixable.rs:20:16 + --> tests/ui/unnecessary_cast_unfixable.rs:21:20 | -LL | ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*(self.object as *mut *mut _)` +LL | ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*(self.object as *mut *mut _)` error: aborting due to 2 previous errors diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 9a329081638..409a8efbfeb 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -321,3 +321,7 @@ fn panicky_arithmetic_ops(x: usize, y: isize) { let _x = false.then_some(f1 + f2); //~^ unnecessary_lazy_evaluations } + +fn issue14578() { + let _: Box<dyn std::future::Future<Output = i32>> = Box::new(true.then(async || 42).unwrap()); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 2d05ef5c291..54735023a93 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -321,3 +321,7 @@ fn panicky_arithmetic_ops(x: usize, y: isize) { let _x = false.then(|| f1 + f2); //~^ unnecessary_lazy_evaluations } + +fn issue14578() { + let _: Box<dyn std::future::Future<Output = i32>> = Box::new(true.then(async || 42).unwrap()); +} diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index ba562f5a50b..13d71271e21 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -1,13 +1,8 @@ error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:9:1 | -LL | / fn func1(a: bool, b: bool) -> Option<i32> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn func1(a: bool, b: bool) -> Option<i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_wraps)]` @@ -16,7 +11,7 @@ help: remove `Option` from the return type... LL - fn func1(a: bool, b: bool) -> Option<i32> { LL + fn func1(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 42; LL | } @@ -30,21 +25,15 @@ LL ~ return 1337; error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:24:1 | -LL | / fn func2(a: bool, b: bool) -> Option<i32> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | if a { Some(20) } else { Some(30) } -LL | | } - | |_^ +LL | fn func2(a: bool, b: bool) -> Option<i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func2(a: bool, b: bool) -> Option<i32> { LL + fn func2(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 10; LL | } @@ -54,19 +43,15 @@ LL ~ if a { 20 } else { 30 } error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:44:1 | -LL | / fn func5() -> Option<i32> { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_^ +LL | fn func5() -> Option<i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func5() -> Option<i32> { LL + fn func5() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -75,19 +60,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Result` --> tests/ui/unnecessary_wraps.rs:56:1 | -LL | / fn func7() -> Result<i32, ()> { -LL | | -LL | | -LL | | Ok(1) -LL | | } - | |_^ +LL | fn func7() -> Result<i32, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Result` from the return type... | LL - fn func7() -> Result<i32, ()> { LL + fn func7() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Ok()` from returning expressions | LL - Ok(1) LL + 1 @@ -96,19 +77,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:86:5 | -LL | / fn func12() -> Option<i32> { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_____^ +LL | fn func12() -> Option<i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func12() -> Option<i32> { LL + fn func12() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -117,13 +94,8 @@ LL + 1 error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:115:1 | -LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | @@ -144,13 +116,8 @@ LL ~ return ; error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:130:1 | -LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs index 5aaf7b9f5b5..433459253dd 100644 --- a/tests/ui/unused_async.rs +++ b/tests/ui/unused_async.rs @@ -119,3 +119,11 @@ fn main() { foo(); bar(); } + +mod issue14704 { + use std::sync::Arc; + + trait Action { + async fn cancel(self: Arc<Self>) {} + } +} diff --git a/tests/ui/unused_unit.edition2021.fixed b/tests/ui/unused_unit.edition2021.fixed new file mode 100644 index 00000000000..93dd58b8e9d --- /dev/null +++ b/tests/ui/unused_unit.edition2021.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit<F: Fn(), G>(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.edition2021.stderr b/tests/ui/unused_unit.edition2021.stderr new file mode 100644 index 00000000000..13cc20d4d7a --- /dev/null +++ b/tests/ui/unused_unit.edition2021.stderr @@ -0,0 +1,128 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:136:15 + | +LL | run(|| -> () { todo!() }); + | ^^^^^^ help: remove the `-> ()` + +error: aborting due to 20 previous errors + diff --git a/tests/ui/unused_unit.edition2024.fixed b/tests/ui/unused_unit.edition2024.fixed new file mode 100644 index 00000000000..987d901b97d --- /dev/null +++ b/tests/ui/unused_unit.edition2024.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit<F: Fn(), G>(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.edition2024.stderr b/tests/ui/unused_unit.edition2024.stderr new file mode 100644 index 00000000000..a79e70e066b --- /dev/null +++ b/tests/ui/unused_unit.edition2024.stderr @@ -0,0 +1,122 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors + diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index e3c02681c9f..6668bf90c09 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -120,3 +120,24 @@ mod issue9949 { () } } + +#[clippy::msrv = "1.85"] +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + fn bar() { + run(|| -> () { todo!() }); + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 4353026c594..b7645f7b6a2 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -1,4 +1,6 @@ - +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 // The output for humans should just highlight the whole span without showing // the suggested replacement, but we also want to test that suggested @@ -120,3 +122,25 @@ mod issue9949 { () } } + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run<R: Unit>(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 172fe065502..366f2142095 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ -error: unneeded unit return type - --> tests/ui/unused_unit.rs:20:58 +error: unneeded unit expression + --> tests/ui/unused_unit.rs:35:9 | -LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () - | ^^^^^^ help: remove the `-> ()` +LL | () + | ^^ help: remove the final `()` | note: the lint level is defined here --> tests/ui/unused_unit.rs:13:9 @@ -10,6 +10,12 @@ note: the lint level is defined here LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:60:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + error: unneeded unit return type --> tests/ui/unused_unit.rs:20:28 | @@ -23,6 +29,12 @@ LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type + --> tests/ui/unused_unit.rs:20:58 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type --> tests/ui/unused_unit.rs:25:26 | LL | let _y: &dyn Fn() -> () = &f; @@ -34,12 +46,6 @@ error: unneeded unit return type LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:35:9 - | -LL | () - | ^^ help: remove the final `()` - error: unneeded unit return type --> tests/ui/unused_unit.rs:41:29 | @@ -82,12 +88,6 @@ error: unneeded unit return type LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:60:26 - | -LL | fn return_unit() -> () { () } - | ^^ help: remove the final `()` - error: unneeded `()` --> tests/ui/unused_unit.rs:72:14 | diff --git a/tests/ui/unwrap_expect_used.rs b/tests/ui/unwrap_expect_used.rs index d0bb571273b..b429f3a8a0b 100644 --- a/tests/ui/unwrap_expect_used.rs +++ b/tests/ui/unwrap_expect_used.rs @@ -66,3 +66,20 @@ fn main() { SOME.expect("Still not three?"); } } + +mod with_expansion { + macro_rules! open { + ($file:expr) => { + std::fs::File::open($file) + }; + } + + fn test(file: &str) { + use std::io::Read; + let mut s = String::new(); + let _ = open!(file).unwrap(); //~ unwrap_used + let _ = open!(file).expect("can open"); //~ expect_used + let _ = open!(file).unwrap_err(); //~ unwrap_used + let _ = open!(file).expect_err("can open"); //~ expect_used + } +} diff --git a/tests/ui/unwrap_expect_used.stderr b/tests/ui/unwrap_expect_used.stderr index 79eac3f58cc..6fd1b84d812 100644 --- a/tests/ui/unwrap_expect_used.stderr +++ b/tests/ui/unwrap_expect_used.stderr @@ -50,5 +50,37 @@ LL | a.expect_err("Hello error!"); | = note: if this value is an `Ok`, it will panic -error: aborting due to 6 previous errors +error: used `unwrap()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:80:17 + | +LL | let _ = open!(file).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `expect()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:81:17 + | +LL | let _ = open!(file).expect("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `unwrap_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:82:17 + | +LL | let _ = open!(file).unwrap_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: used `expect_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:83:17 + | +LL | let _ = open!(file).expect_err("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: aborting due to 10 previous errors diff --git a/tests/ui/used_underscore_items.rs b/tests/ui/used_underscore_items.rs index 3401df6ae74..7e8289f1406 100644 --- a/tests/ui/used_underscore_items.rs +++ b/tests/ui/used_underscore_items.rs @@ -73,7 +73,7 @@ fn external_item_call() { // should not lint foreign functions. // issue #14156 -extern "C" { +unsafe extern "C" { pub fn _exit(code: i32) -> !; } diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 489caacf212..ad30c94f347 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range<u32> = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.for_each(|_x| {}); + //~^ useless_conversion + let _ = R.map(|_x| 0); + //~^ useless_conversion +} diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 4f3a3b00ea2..505afb34000 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range<u32> = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.into_iter().for_each(|_x| {}); + //~^ useless_conversion + let _ = R.into_iter().map(|_x| 0); + //~^ useless_conversion +} diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 3cde2a786e4..3bfaf1411c2 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -377,5 +377,17 @@ LL - takes_into_iter(self.my_field.into_iter()); LL + takes_into_iter(&mut *self.my_field); | -error: aborting due to 41 previous errors +error: useless conversion to the same type: `std::ops::Range<u32>` + --> tests/ui/useless_conversion.rs:440:5 + | +LL | R.into_iter().for_each(|_x| {}); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: useless conversion to the same type: `std::ops::Range<u32>` + --> tests/ui/useless_conversion.rs:442:13 + | +LL | let _ = R.into_iter().map(|_x| 0); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: aborting due to 43 previous errors diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index a26b4a34190..17510683f03 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -16,7 +16,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports #[macro_use] use crate::struct_mod::{A, inner_struct_mod}; @@ -26,7 +26,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -138,7 +138,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -160,7 +160,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -190,7 +190,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index f774126102b..26434656a50 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:19:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:22:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:29:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:100:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:141:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:154:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:163:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:193:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:203:9 diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed index a3d1aebba8a..f97b883ea23 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -14,7 +14,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports use crate::struct_mod::{A, inner_struct_mod}; //~^ wildcard_imports @@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports use wildcard_imports_helper::prelude::v1::*; -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -132,7 +132,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -154,7 +154,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -184,7 +184,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr index a1b557f39f0..873ce41b04f 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:17:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:19:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:26:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:95:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:135:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:148:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:157:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:187:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed index a3d1aebba8a..f97b883ea23 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -14,7 +14,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports use crate::struct_mod::{A, inner_struct_mod}; //~^ wildcard_imports @@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports use wildcard_imports_helper::prelude::v1::*; -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -132,7 +132,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -154,7 +154,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -184,7 +184,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr index a1b557f39f0..873ce41b04f 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:17:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:19:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:26:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:95:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:135:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:148:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:157:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:187:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 diff --git a/tests/ui/zombie_processes.rs b/tests/ui/zombie_processes.rs index 25bbc02ffb7..395f9dd2def 100644 --- a/tests/ui/zombie_processes.rs +++ b/tests/ui/zombie_processes.rs @@ -176,3 +176,25 @@ fn return_wait() -> ExitStatus { let mut x = Command::new("").spawn().unwrap(); return x.wait().unwrap(); } + +mod issue14677 { + use std::io; + use std::process::Command; + + fn do_something<F: Fn() -> Result<(), ()>>(f: F) { + todo!() + } + + fn foo() { + let mut child = Command::new("true").spawn().unwrap(); + let some_condition = true; + do_something(|| { + if some_condition { + return Err(()); + } + Ok(()) + }); + child.kill().unwrap(); + child.wait().unwrap(); + } +} diff --git a/triagebot.toml b/triagebot.toml index 7c07bce8bef..eb2f9f9dd61 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -11,7 +11,11 @@ allow-unauthenticated = [ [merge-conflicts] -[canonicalize-issue-links] +[note] + +[close] + +[issue-links] # Prevents mentions in commits to avoid users being spammed [no-mentions] @@ -40,6 +44,7 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", + "Manishearth", ] [assign.owners] diff --git a/util/etc/pre-commit.sh b/util/etc/pre-commit.sh index 5dd2ba3d5f5..528f8953b25 100755 --- a/util/etc/pre-commit.sh +++ b/util/etc/pre-commit.sh @@ -6,7 +6,6 @@ set -e # Update lints cargo dev update_lints git add clippy_lints/src/lib.rs -git add clippy_lints/src/lib.*.rs # Formatting: # Git will not automatically add the formatted code to the staged changes once |
