diff --git a/Cargo.lock b/Cargo.lock index 018d03ed26c96..b358b21a5f985 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -645,6 +645,7 @@ version = "0.0.1" dependencies = [ "clippy_config", "clippy_utils", + "itertools", "regex", "rustc-semver", ] diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index c9d350ee0b304..c2cc48ab95116 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -17,9 +17,9 @@ jobs: persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length@^3.1.3 remark-preset-lint-recommended remark-gfm diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 30781d3d33fba..37d46d3496677 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -28,19 +28,19 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Enhancements -* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods - [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) - [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) -* [`incompatible_msrv`] now recognizes types exceeding MSRV +* [`incompatible_msrv`] now recognizes types exceeding MSRV [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) -* [`incompatible_msrv`] now checks the right MSRV when in a `const` context +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) -* [`zero_ptr`] now lints in `const` context as well +* [`zero_ptr`] now lints in `const` context as well [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) * [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) -* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) ### False Positive Fixes @@ -51,7 +51,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) * [`unused_async`] fixed FP on function with `todo!` [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) -* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes on `use` statements [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) * [`pattern_type_mismatch`] fixed FP in external macro @@ -64,7 +64,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) * [`ptr_as_ptr`] fixed wrong suggestions with turbo fish [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) -* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe to switch the range type [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) * [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument @@ -129,7 +129,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) * [`collapsible_else_if`] fixed FP on conditionally compiled stmt [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) -* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing non-`Sized` trait bounds [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) * [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` @@ -137,7 +137,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### ICE Fixes -* [`single_match`] fixed ICE with deref patterns and string literals +* [`single_match`] fixed ICE with deref patterns and string literals [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) * [`needless_doctest_main`] fixed panic when doctest is invalid [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) @@ -146,11 +146,11 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Documentation Improvements -* [`manual_is_variant_and`] improved documentation to include equality comparison patterns +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) * [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) -* [`undocumented_unsafe_blocks`] improved documentation wording +* [`undocumented_unsafe_blocks`] improved documentation wording [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 @@ -292,7 +292,7 @@ Current stable, released 2025-06-26 [#14408](https://github.com/rust-lang/rust-clippy/pull/14408) * [`iter_kv_map`] now recognizes references on maps [#14596](https://github.com/rust-lang/rust-clippy/pull/14596) -* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used +* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used as functions within same crate [#12971](https://github.com/rust-lang/rust-clippy/pull/12971) * [`needless_lifetimes`] now checks for lifetime uses in closures [#14608](https://github.com/rust-lang/rust-clippy/pull/14608) @@ -928,50 +928,50 @@ Released 2024-02-08 ### New Lints -- [`infinite_loop`] +* [`infinite_loop`] [#11829](https://github.com/rust-lang/rust-clippy/pull/11829) -- [`ineffective_open_options`] +* [`ineffective_open_options`] [#11902](https://github.com/rust-lang/rust-clippy/pull/11902) -- [`uninhabited_references`] +* [`uninhabited_references`] [#11878](https://github.com/rust-lang/rust-clippy/pull/11878) -- [`repeat_vec_with_capacity`] +* [`repeat_vec_with_capacity`] [#11597](https://github.com/rust-lang/rust-clippy/pull/11597) -- [`test_attr_in_doctest`] +* [`test_attr_in_doctest`] [#11872](https://github.com/rust-lang/rust-clippy/pull/11872) -- [`option_map_or_err_ok`] +* [`option_map_or_err_ok`] [#11864](https://github.com/rust-lang/rust-clippy/pull/11864) -- [`join_absolute_paths`] +* [`join_absolute_paths`] [#11453](https://github.com/rust-lang/rust-clippy/pull/11453) -- [`impl_hash_borrow_with_str_and_bytes`] +* [`impl_hash_borrow_with_str_and_bytes`] [#11781](https://github.com/rust-lang/rust-clippy/pull/11781) -- [`iter_over_hash_type`] +* [`iter_over_hash_type`] [#11791](https://github.com/rust-lang/rust-clippy/pull/11791) ### Moves and Deprecations -- Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] +* Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] [#11853](https://github.com/rust-lang/rust-clippy/pull/11853) -- Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) +* Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) [#11867](https://github.com/rust-lang/rust-clippy/pull/11867) -- Moved [`if_same_then_else`] to `style` (Now warn-by-default) +* Moved [`if_same_then_else`] to `style` (Now warn-by-default) [#11809](https://github.com/rust-lang/rust-clippy/pull/11809) ### Enhancements -- [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: +* [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: Added the [`check-private-items`] configuration to enable lints on private items [#11842](https://github.com/rust-lang/rust-clippy/pull/11842) ### ICE Fixes -- [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters +* [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters [#11804](https://github.com/rust-lang/rust-clippy/pull/11804) -- [`unused_enumerate_index`]: No longer crashes on empty tuples +* [`unused_enumerate_index`]: No longer crashes on empty tuples [#11756](https://github.com/rust-lang/rust-clippy/pull/11756) ### Others -- Clippy now respects the `CARGO` environment value +* Clippy now respects the `CARGO` environment value [#11944](https://github.com/rust-lang/rust-clippy/pull/11944) ## Rust 1.75 @@ -997,7 +997,6 @@ Released 2023-12-28 * [`manual_hash_one`] [#11556](https://github.com/rust-lang/rust-clippy/pull/11556) - ### Moves and Deprecations * Moved [`read_zero_byte_vec`] to `nursery` (Now allow-by-default) @@ -1073,7 +1072,7 @@ Released 2023-11-16 ### Enhancements -* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and +* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and [`accept-comment-above-attributes`] are now `true` by default [#11170](https://github.com/rust-lang/rust-clippy/pull/11170) * [`explicit_iter_loop`]: Added [`enforce-iter-loop-reborrow`] to disable reborrow linting by default @@ -2254,7 +2253,6 @@ Released 2022-09-22 * [`explicit_auto_deref`] [#8355](https://github.com/rust-lang/rust-clippy/pull/8355) - ### Moves and Deprecations * Moved [`format_push_string`] to `restriction` (now allow-by-default) @@ -2455,10 +2453,10 @@ Released 2022-08-11 * [`redundant_allocation`]: No longer lints on fat pointers that would become thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813) * [`derive_partial_eq_without_eq`]: - * Handle differing predicates applied by `#[derive(PartialEq)]` and + * Handle differing predicates applied by `#[derive(PartialEq)]` and `#[derive(Eq)]` [#8869](https://github.com/rust-lang/rust-clippy/pull/8869) - * No longer lints on non-public types and better handles generics + * No longer lints on non-public types and better handles generics [#8950](https://github.com/rust-lang/rust-clippy/pull/8950) * [`empty_line_after_outer_attr`]: No longer lints empty lines in inner string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892) @@ -2952,12 +2950,12 @@ Released 2022-02-24 [#7957](https://github.com/rust-lang/rust-clippy/pull/7957) * [`needless_borrow`] [#7977](https://github.com/rust-lang/rust-clippy/pull/7977) - * Lint when a borrow is auto-dereffed more than once - * Lint in the trailing expression of a block for a match arm + * Lint when a borrow is auto-dereffed more than once + * Lint in the trailing expression of a block for a match arm * [`strlen_on_c_strings`] [8001](https://github.com/rust-lang/rust-clippy/pull/8001) - * Lint when used without a fully-qualified path - * Suggest removing the surrounding unsafe block when possible + * Lint when used without a fully-qualified path + * Suggest removing the surrounding unsafe block when possible * [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s [#8034](https://github.com/rust-lang/rust-clippy/pull/8034) * [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`, @@ -3068,7 +3066,7 @@ Released 2022-02-24 [#7813](https://github.com/rust-lang/rust-clippy/pull/7813) * New and improved issue templates [#8032](https://github.com/rust-lang/rust-clippy/pull/8032) -* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a +* *Dev:* Add `cargo dev lint` command, to run your modified Clippy version on a file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917) ## Rust 1.58 @@ -3278,15 +3276,15 @@ Released 2021-12-02 [#7566](https://github.com/rust-lang/rust-clippy/pull/7566) * [`option_if_let_else`]: Multiple fixes [#7573](https://github.com/rust-lang/rust-clippy/pull/7573) - * `break` and `continue` statements local to the would-be closure are + * `break` and `continue` statements local to the would-be closure are allowed - * Don't lint in const contexts - * Don't lint when yield expressions are used - * Don't lint when the captures made by the would-be closure conflict with + * Don't lint in const contexts + * Don't lint when yield expressions are used + * Don't lint when the captures made by the would-be closure conflict with the other branch - * Don't lint when a field of a local is used when the type could be + * Don't lint when a field of a local is used when the type could be potentially moved from - * In some cases, don't lint when scrutinee expression conflicts with the + * In some cases, don't lint when scrutinee expression conflicts with the captures of the would-be closure * [`redundant_allocation`]: No longer lints on `Box>` which replaces wide pointers with thin pointers @@ -3535,124 +3533,124 @@ Released 2021-07-29 ### New Lints -- [`ref_binding_to_reference`] +* [`ref_binding_to_reference`] [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`needless_bitwise_bool`] +* [`needless_bitwise_bool`] [#7133](https://github.com/rust-lang/rust-clippy/pull/7133) -- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) -- [`manual_str_repeat`] +* [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) +* [`manual_str_repeat`] [#7265](https://github.com/rust-lang/rust-clippy/pull/7265) -- [`suspicious_splitn`] +* [`suspicious_splitn`] [#7292](https://github.com/rust-lang/rust-clippy/pull/7292) ### Moves and Deprecations -- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of +* Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of the new `avoid-breaking-exported-api` config option (see [Enhancements](#1-54-enhancements)) [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- Move [`inconsistent_struct_constructor`] to `pedantic` +* Move [`inconsistent_struct_constructor`] to `pedantic` [#7193](https://github.com/rust-lang/rust-clippy/pull/7193) -- Move [`needless_borrow`] to `style` (now warn-by-default) +* Move [`needless_borrow`] to `style` (now warn-by-default) [#7254](https://github.com/rust-lang/rust-clippy/pull/7254) -- Move [`suspicious_operation_groupings`] to `nursery` +* Move [`suspicious_operation_groupings`] to `nursery` [#7266](https://github.com/rust-lang/rust-clippy/pull/7266) -- Move [`semicolon_if_nothing_returned`] to `pedantic` +* Move [`semicolon_if_nothing_returned`] to `pedantic` [#7268](https://github.com/rust-lang/rust-clippy/pull/7268) ### Enhancements -- [`while_let_on_iterator`]: Now also lints in nested loops +* [`while_let_on_iterator`]: Now also lints in nested loops [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` +* [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` [#7156](https://github.com/rust-lang/rust-clippy/pull/7156) -- [`needless_collect`]: Now also lints on assignments with type annotations +* [`needless_collect`]: Now also lints on assignments with type annotations [#7163](https://github.com/rust-lang/rust-clippy/pull/7163) -- [`if_then_some_else_none`]: Now works with the MSRV config +* [`if_then_some_else_none`]: Now works with the MSRV config [#7177](https://github.com/rust-lang/rust-clippy/pull/7177) -- Add `avoid-breaking-exported-api` config option for the lints +* Add `avoid-breaking-exported-api` config option for the lints [`enum_variant_names`], [`large_types_passed_by_value`], [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`], [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set this configuration option to `false` before a major release (1.0/2.0/...) to clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- [`needless_collect`]: Now lints on even more data structures +* [`needless_collect`]: Now lints on even more data structures [#7188](https://github.com/rust-lang/rust-clippy/pull/7188) -- [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like +* [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like attributes as sufficient documentation [#7281](https://github.com/rust-lang/rust-clippy/pull/7281) -- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: +* [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: Now work as expected when used with `allow` [#7282](https://github.com/rust-lang/rust-clippy/pull/7282) ### False Positive Fixes -- [`implicit_return`]: Now takes all diverging functions in account to avoid +* [`implicit_return`]: Now takes all diverging functions in account to avoid false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) -- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field +* [`while_let_on_iterator`]: No longer lints when the iterator is a struct field and the struct is used in the loop [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`multiple_inherent_impl`]: No longer lints with generic arguments +* [`multiple_inherent_impl`]: No longer lints with generic arguments [#7089](https://github.com/rust-lang/rust-clippy/pull/7089) -- [`comparison_chain`]: No longer lints in a `const` context +* [`comparison_chain`]: No longer lints in a `const` context [#7118](https://github.com/rust-lang/rust-clippy/pull/7118) -- [`while_immutable_condition`]: Fix false positive where mutation in the loop +* [`while_immutable_condition`]: Fix false positive where mutation in the loop variable wasn't picked up [#7144](https://github.com/rust-lang/rust-clippy/pull/7144) -- [`default_trait_access`]: No longer lints in macros +* [`default_trait_access`]: No longer lints in macros [#7150](https://github.com/rust-lang/rust-clippy/pull/7150) -- [`needless_question_mark`]: No longer lints when the inner value is implicitly +* [`needless_question_mark`]: No longer lints when the inner value is implicitly dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165) -- [`unused_unit`]: No longer lints when multiple macro contexts are involved +* [`unused_unit`]: No longer lints when multiple macro contexts are involved [#7167](https://github.com/rust-lang/rust-clippy/pull/7167) -- [`eval_order_dependence`]: Fix false positive in async context +* [`eval_order_dependence`]: Fix false positive in async context [#7174](https://github.com/rust-lang/rust-clippy/pull/7174) -- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the +* [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175) -- [`wrong_self_convention`]: No longer lints in trait implementations of +* [`wrong_self_convention`]: No longer lints in trait implementations of non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182) -- [`suboptimal_flops`]: No longer lints on `powi(2)` +* [`suboptimal_flops`]: No longer lints on `powi(2)` [#7201](https://github.com/rust-lang/rust-clippy/pull/7201) -- [`wrong_self_convention`]: No longer lints if there is no implicit `self` +* [`wrong_self_convention`]: No longer lints if there is no implicit `self` [#7215](https://github.com/rust-lang/rust-clippy/pull/7215) -- [`option_if_let_else`]: No longer lints on `else if let` pattern +* [`option_if_let_else`]: No longer lints on `else if let` pattern [#7216](https://github.com/rust-lang/rust-clippy/pull/7216) -- [`use_self`], [`useless_conversion`]: Fix false positives when generic +* [`use_self`], [`useless_conversion`]: Fix false positives when generic arguments are involved [#7223](https://github.com/rust-lang/rust-clippy/pull/7223) -- [`manual_unwrap_or`]: Fix false positive with deref coercion +* [`manual_unwrap_or`]: Fix false positive with deref coercion [#7233](https://github.com/rust-lang/rust-clippy/pull/7233) -- [`similar_names`]: No longer lints on `wparam`/`lparam` +* [`similar_names`]: No longer lints on `wparam`/`lparam` [#7255](https://github.com/rust-lang/rust-clippy/pull/7255) -- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a +* [`redundant_closure`]: No longer lints on using the `vec![]` macro in a closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263) ### Suggestion Fixes/Improvements -- [`implicit_return`] +* [`implicit_return`] [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) - - Fix suggestion for async functions - - Improve suggestion with macros - - Suggest to change `break` to `return` when appropriate -- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary + * Fix suggestion for async functions + * Improve suggestion with macros + * Suggest to change `break` to `return` when appropriate +* [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`match_single_binding`]: Improve suggestion when match scrutinee has side +* [`match_single_binding`]: Improve suggestion when match scrutinee has side effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095) -- [`needless_borrow`]: Now suggests to also change usage sites as needed +* [`needless_borrow`]: Now suggests to also change usage sites as needed [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`write_with_newline`]: Improve suggestion when only `\n` is written to the +* [`write_with_newline`]: Improve suggestion when only `\n` is written to the buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183) -- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also +* [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also when a `<_ as Trait>::_` is involved [#7264](https://github.com/rust-lang/rust-clippy/pull/7264) -- [`not_unsafe_ptr_arg_deref`]: Improved error message +* [`not_unsafe_ptr_arg_deref`]: Improved error message [#7294](https://github.com/rust-lang/rust-clippy/pull/7294) ### ICE Fixes -- Fix ICE when running Clippy on `libstd` +* Fix ICE when running Clippy on `libstd` [#7140](https://github.com/rust-lang/rust-clippy/pull/7140) -- [`implicit_return`] +* [`implicit_return`] [#7242](https://github.com/rust-lang/rust-clippy/pull/7242) ## Rust 1.53 @@ -3697,9 +3695,9 @@ Released 2021-06-17 [#6828](https://github.com/rust-lang/rust-clippy/pull/6828) * [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: [#6863](https://github.com/rust-lang/rust-clippy/pull/6863) - * Attempt to find a common path prefix in suggestion - * Don't lint on `Option` and `Result` - * Consider `Self` prefix + * Attempt to find a common path prefix in suggestion + * Don't lint on `Option` and `Result` + * Consider `Self` prefix * [`explicit_deref_methods`]: Also lint on chained `deref` calls [#6865](https://github.com/rust-lang/rust-clippy/pull/6865) * [`or_fun_call`]: Also lint on `unsafe` blocks @@ -3959,6 +3957,7 @@ Released 2021-05-06 [#6782](https://github.com/rust-lang/rust-clippy/pull/6782) ### Others + * Running `cargo clippy` after `cargo check` now works as expected (`cargo clippy` and `cargo check` no longer shares the same build cache) [#6687](https://github.com/rust-lang/rust-clippy/pull/6687) @@ -4174,7 +4173,6 @@ Released 2021-02-11 * [`field_reassign_with_default`] No longer lint for private fields [#6537](https://github.com/rust-lang/rust-clippy/pull/6537) - ### Suggestion Fixes/Improvements * [`vec_box`]: Provide correct type scope suggestion @@ -4319,8 +4317,8 @@ Released 2020-12-31 ### Documentation Improvements * Some doc improvements: - * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) - * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) + * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) + * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) * [`doc_markdown`]: Document problematic link text style [#6107](https://github.com/rust-lang/rust-clippy/pull/6107) @@ -4703,7 +4701,6 @@ Released 2020-06-04 * [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) * [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) - ### Moves and Deprecations * Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380) @@ -4718,7 +4715,7 @@ Released 2020-06-04 ### Enhancements -* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to +* On *nightly* you can now use `cargo clippy --fix -Z unstable-options` to auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363) * Make [`redundant_clone`] also trigger on cases where the cloned value is not consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304) @@ -4745,7 +4742,6 @@ Released 2020-06-04 * [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287) * [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451) - ### Suggestion Improvements * Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481) @@ -4823,7 +4819,6 @@ Released 2020-04-23 * Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190) - ## Rust 1.42 Released 2020-03-12 @@ -4890,7 +4885,6 @@ Released 2020-03-12 * Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`] - ## Rust 1.41 Released 2020-01-30 @@ -5106,7 +5100,6 @@ Released 2019-07-04 * Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960) * Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931) - ## Rust 1.35 Released 2019-05-20 @@ -5129,7 +5122,7 @@ Released 2019-05-20 * Fix false positive in [`needless_continue`] pertaining to loop labels * Fix false positive for [`boxed_local`] pertaining to arguments moved into closures * Fix false positive for [`use_self`] in nested functions -* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846) +* Fix suggestion for [`expect_fun_call`] () * Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables * Fix suggestion for [`single_char_pattern`] to correctly escape single quotes * Avoid triggering [`redundant_closure`] in macros @@ -5273,6 +5266,7 @@ Released 2018-12-06 Released 2018-10-25 [View all 88 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2018-08-02T16%3A54%3A12Z..2018-09-17T09%3A44%3A06Z+base%3Amaster) + * Deprecate `assign_ops` lint * New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`], [`needless_collect`], [`copy_iterator`] @@ -5322,255 +5316,326 @@ Released 2018-09-13 * Improve website header ## 0.0.212 (2018-07-10) + * Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)* ## 0.0.211 + * Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)* ## 0.0.210 + * Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)* ## 0.0.209 + * Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)* ## 0.0.208 + * Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)* ## 0.0.207 + * Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)* ## 0.0.206 + * Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)* ## 0.0.205 + * Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)* * Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint ## 0.0.204 + * Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)* ## 0.0.203 + * Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)* * Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)` ## 0.0.202 + * Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)* ## 0.0.201 + * Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)* ## 0.0.200 + * Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)* ## 0.0.199 + * Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)* ## 0.0.198 + * Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)* ## 0.0.197 + * Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)* ## 0.0.196 + * Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)* ## 0.0.195 + * Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)* ## 0.0.194 + * Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)* * New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`] ## 0.0.193 + * Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)* ## 0.0.192 + * Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)* * New lint: [`print_literal`] ## 0.0.191 + * Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)* * Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction. ## 0.0.190 + * Fix a bunch of intermittent cargo bugs ## 0.0.189 + * Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)* ## 0.0.188 + * Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)* * New lint: [`while_immutable_condition`] ## 0.0.187 + * Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)* * New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`] ## 0.0.186 + * Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)* * Various false positive fixes ## 0.0.185 + * Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)* * New lint: [`question_mark`] ## 0.0.184 + * Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)* * New lints: [`double_comparisons`], [`empty_line_after_outer_attr`] ## 0.0.183 + * Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)* * New lint: [`misaligned_transmute`] ## 0.0.182 + * Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)* * New lint: [`decimal_literal_representation`] ## 0.0.181 + * Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)* * New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`] * Removed `unit_expr` * Various false positive fixes for [`needless_pass_by_value`] ## 0.0.180 + * Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)* ## 0.0.179 + * Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)* ## 0.0.178 + * Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)* ## 0.0.177 + * Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)* * New lint: [`match_as_ref`] ## 0.0.176 + * Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)* ## 0.0.175 + * Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)* ## 0.0.174 + * Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)* ## 0.0.173 + * Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)* ## 0.0.172 + * Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)* ## 0.0.171 + * Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)* ## 0.0.170 + * Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)* ## 0.0.169 + * Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* * New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] ## 0.0.168 + * Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* ## 0.0.167 + * Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)* * New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`] ## 0.0.166 + * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* * New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] ## 0.0.165 + * Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27) * New lint: [`mut_range_bound`] ## 0.0.164 + * Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)* * New lint: [`int_plus_one`] ## 0.0.163 + * Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)* ## 0.0.162 + * Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)* * New lint: [`chars_last_cmp`] * Improved suggestions for [`needless_borrow`], [`ptr_arg`], ## 0.0.161 + * Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)* ## 0.0.160 + * Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)* ## 0.0.159 + * Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)* * New lint: [`clone_on_ref_ptr`] ## 0.0.158 + * New lint: [`manual_memcpy`] * [`cast_lossless`] no longer has redundant parentheses in its suggestions * Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)* ## 0.0.157 - 2017-09-04 + * Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)* * New lint: `unit_expr` ## 0.0.156 - 2017-09-03 + * Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)* ## 0.0.155 + * Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)* * New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`] ## 0.0.154 + * Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)* * Fix [`use_self`] triggering inside derives * Add support for linting an entire workspace with `cargo clippy --all` * New lint: [`naive_bytecount`] ## 0.0.153 + * Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)* * New lint: [`use_self`] ## 0.0.152 + * Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)* ## 0.0.151 + * Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)* ## 0.0.150 + * Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)* ## 0.0.148 + * Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)* * New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`] ## 0.0.147 + * Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)* ## 0.0.146 + * Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)* * Fixes false positives in `inline_always` * Fixes false negatives in `panic_params` ## 0.0.145 + * Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)* ## 0.0.144 + * Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)* ## 0.0.143 + * Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)* * Fix `cargo clippy` crashing on `dylib` projects * Fix false positives around `nested_while_let` and `never_loop` ## 0.0.142 + * Update to *rustc 1.20.0-nightly (067971139 2017-07-02)* ## 0.0.141 + * Rewrite of the `doc_markdown` lint. * Deprecated [`range_step_by_zero`] * New lint: [`iterator_step_by_zero`] @@ -5578,151 +5643,195 @@ Released 2018-09-13 * Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)* ## 0.0.140 - 2017-06-16 + * Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)* ## 0.0.139 — 2017-06-10 + * Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)* * Fix bugs with for loop desugaring * Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`] ## 0.0.138 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)* ## 0.0.137 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)* ## 0.0.136 — 2017—05—26 + * Update to *rustc 1.19.0-nightly (557967766 2017-05-26)* ## 0.0.135 — 2017—05—24 + * Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)* ## 0.0.134 — 2017—05—19 + * Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)* ## 0.0.133 — 2017—05—14 + * Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)* ## 0.0.132 — 2017—05—05 + * Fix various bugs and some ices ## 0.0.131 — 2017—05—04 + * Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)* ## 0.0.130 — 2017—05—03 + * Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)* ## 0.0.129 — 2017-05-01 + * Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)* ## 0.0.128 — 2017-04-28 + * Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)* ## 0.0.127 — 2017-04-27 + * Update to *rustc 1.18.0-nightly (036983201 2017-04-26)* * New lint: [`needless_continue`] ## 0.0.126 — 2017-04-24 + * Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)* ## 0.0.125 — 2017-04-19 + * Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)* ## 0.0.124 — 2017-04-16 + * Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)* ## 0.0.123 — 2017-04-07 + * Fix various false positives ## 0.0.122 — 2017-04-07 + * Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)* * New lint: [`op_ref`] ## 0.0.121 — 2017-03-21 + * Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)* ## 0.0.120 — 2017-03-17 + * Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)* ## 0.0.119 — 2017-03-13 + * Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)* ## 0.0.118 — 2017-03-05 + * Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)* ## 0.0.117 — 2017-03-01 + * Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)* ## 0.0.116 — 2017-02-28 + * Fix `cargo clippy` on 64 bit windows systems ## 0.0.115 — 2017-02-27 + * Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)* * New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`] ## 0.0.114 — 2017-02-08 + * Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)* * Tests are now ui tests (testing the exact output of rustc) ## 0.0.113 — 2017-02-04 + * Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)* * New lint: [`large_enum_variant`] * `explicit_into_iter_loop` provides suggestions ## 0.0.112 — 2017-01-27 + * Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)* ## 0.0.111 — 2017-01-21 + * Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)* ## 0.0.110 — 2017-01-20 + * Add badges and categories to `Cargo.toml` ## 0.0.109 — 2017-01-19 + * Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)* ## 0.0.108 — 2017-01-12 + * Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)* ## 0.0.107 — 2017-01-11 + * Update regex dependency * Fix FP when matching `&&mut` by `&ref` * Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()` * New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`] ## 0.0.106 — 2017-01-04 + * Fix FP introduced by rustup in [`wrong_self_convention`] ## 0.0.105 — 2017-01-04 + * Update to *rustc 1.16.0-nightly (468227129 2017-01-03)* * New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`] * Fix suggestion in [`new_without_default`] * FP fix in [`absurd_extreme_comparisons`] ## 0.0.104 — 2016-12-15 + * Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)* ## 0.0.103 — 2016-11-25 + * Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)* ## 0.0.102 — 2016-11-24 + * Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)* ## 0.0.101 — 2016-11-23 + * Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)* * New lint: [`string_extend_chars`] ## 0.0.100 — 2016-11-20 + * Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)* ## 0.0.99 — 2016-11-18 + * Update to rustc 1.15.0-nightly (0ed951993 2016-11-14) * New lint: [`get_unwrap`] ## 0.0.98 — 2016-11-08 + * Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy` ## 0.0.97 — 2016-11-03 + * For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was previously added for a short time under the name `clippy` but removed for compatibility. @@ -5731,34 +5840,43 @@ Released 2018-09-13 * New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`] ## 0.0.96 — 2016-10-22 + * Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)* * New lint: [`iter_skip_next`] ## 0.0.95 — 2016-10-06 + * Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)* ## 0.0.94 — 2016-10-04 + * Fixes bustage on Windows due to forbidden directory name ## 0.0.93 — 2016-10-03 + * Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* * `option_map_unwrap_or` and `option_map_unwrap_or_else` are now allowed by default. * New lint: [`explicit_into_iter_loop`] ## 0.0.92 — 2016-09-30 + * Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)* ## 0.0.91 — 2016-09-28 + * Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)* ## 0.0.90 — 2016-09-09 + * Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)* ## 0.0.89 — 2016-09-06 + * Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)* ## 0.0.88 — 2016-09-04 + * Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* * The following lints are not new but were only usable through the `clippy` lint groups: [`filter_next`], `for_loop_over_option`, @@ -5767,30 +5885,37 @@ Released 2018-09-13 through `cargo clippy`. ## 0.0.87 — 2016-08-31 + * Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)* * New lints: [`builtin_type_shadow`] * Fix FP in [`zero_prefixed_literal`] and `0b`/`0o` ## 0.0.86 — 2016-08-28 + * Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)* * New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`] ## 0.0.85 — 2016-08-19 + * Fix ICE with [`useless_attribute`] * [`useless_attribute`] ignores `unused_imports` on `use` statements ## 0.0.84 — 2016-08-18 + * Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)* ## 0.0.83 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)* * New lints: [`print_with_newline`], [`useless_attribute`] ## 0.0.82 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)* * New lint: [`module_inception`] ## 0.0.81 — 2016-08-14 + * Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)* * New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`] * False positive fix in [`too_many_arguments`] @@ -5800,14 +5925,17 @@ Released 2018-09-13 * Doc improvements ## 0.0.80 — 2016-07-31 + * Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)* * New lints: [`misrefactored_assign_op`], [`serde_api_misuse`] ## 0.0.79 — 2016-07-10 + * Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)* * Major suggestions refactoring ## 0.0.78 — 2016-07-02 + * Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)* * New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`] * For compatibility, `cargo clippy` does not defines the `clippy` feature @@ -5815,118 +5943,148 @@ Released 2018-09-13 * [`collapsible_if`] now considers `if let` ## 0.0.77 — 2016-06-21 + * Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)* * New lints: `stutter` and [`iter_nth`] ## 0.0.76 — 2016-06-10 + * Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)* * `cargo clippy` now automatically defines the `clippy` feature * New lint: [`not_unsafe_ptr_arg_deref`] ## 0.0.75 — 2016-06-08 + * Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)* ## 0.0.74 — 2016-06-07 + * Fix bug with `cargo-clippy` JSON parsing * Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the “for further information visit *lint-link*” message. ## 0.0.73 — 2016-06-05 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.72 — 2016-06-04 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.71 — 2016-05-31 + * Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)* * New lint: [`useless_let_if_seq`] ## 0.0.70 — 2016-05-28 + * Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)* * [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`, `RegexBuilder::new` and byte regexes ## 0.0.69 — 2016-05-20 + * Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)* * [`used_underscore_binding`] has been made `Allow` temporarily ## 0.0.68 — 2016-05-17 + * Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)* * New lint: [`unnecessary_operation`] ## 0.0.67 — 2016-05-12 + * Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)* ## 0.0.66 — 2016-05-11 + * New `cargo clippy` subcommand * New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`] ## 0.0.65 — 2016-05-08 + * Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)* * New lints: [`float_arithmetic`], [`integer_arithmetic`] ## 0.0.64 — 2016-04-26 + * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* * New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`] ## 0.0.63 — 2016-04-08 + * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* ## 0.0.62 — 2016-04-07 + * Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)* ## 0.0.61 — 2016-04-03 + * Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)* * New lint: [`invalid_upcast_comparisons`] ## 0.0.60 — 2016-04-01 + * Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)* ## 0.0.59 — 2016-03-31 + * Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)* * New lints: [`logic_bug`], [`nonminimal_bool`] * Fixed: [`match_same_arms`] now ignores arms with guards * Improved: [`useless_vec`] now warns on `for … in vec![…]` ## 0.0.58 — 2016-03-27 + * Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)* * New lint: [`doc_markdown`] ## 0.0.57 — 2016-03-27 + * Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)* * Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`] * New lint: [`crosspointer_transmute`] ## 0.0.56 — 2016-03-23 + * Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)* * New lints: [`many_single_char_names`] and [`similar_names`] ## 0.0.55 — 2016-03-21 + * Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)* ## 0.0.54 — 2016-03-16 + * Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)* ## 0.0.53 — 2016-03-15 + * Add a [configuration file] ## ~~0.0.52~~ ## 0.0.51 — 2016-03-13 + * Add `str` to types considered by [`len_zero`] * New lints: [`indexing_slicing`] ## 0.0.50 — 2016-03-11 + * Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)* ## 0.0.49 — 2016-03-09 + * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)* * New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`] ## 0.0.48 — 2016-03-07 + * Fixed: ICE in [`needless_range_loop`] with globals ## 0.0.47 — 2016-03-07 + * Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)* * New lint: [`redundant_closure_call`] @@ -6573,6 +6731,7 @@ Released 2018-09-13 [`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity +[`replace_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_box [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`repr_packed_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#repr_packed_without_abi [`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization @@ -6738,6 +6897,7 @@ Released 2018-09-13 [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_option_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_option_map_or_else [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_result_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_result_map_or_else [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment @@ -6798,6 +6958,7 @@ Released 2018-09-13 [`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads +[`volatile_composites`]: https://rust-lang.github.io/rust-clippy/master/index.html#volatile_composites [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake [`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float @@ -6874,6 +7035,7 @@ Released 2018-09-13 [`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability +[`inherent-impl-lint-scope`]: https://doc.rust-lang.org/clippy/lint_configuration.html#inherent-impl-lint-scope [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold [`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold @@ -6891,6 +7053,7 @@ Released 2018-09-13 [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior +[`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline [`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline [`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index 2b89e94cf8f4f..d9a5f04c3e1c0 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -759,8 +759,7 @@ for some users. Adding a configuration is done in the following steps: Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here ([`is_type_diagnostic_item`], [`implements_trait`], - [`snippet`], etc) + is already in here ([`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] * [Let chains][let-chains] * [`from_expansion`][from_expansion] and @@ -790,7 +789,6 @@ get away with copying things from existing similar lints. If you are stuck, don't hesitate to ask on [Zulip] or in the issue/PR. [utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html -[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html [`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html [`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html [let-chains]: https://github.com/rust-lang/rust/pull/94927 diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 3bec3ce33af36..b5958f802e384 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { // Check our expr is calling a method if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind // Check the name of this method is `some_method` - && path.ident.name.as_str() == "some_method" + && path.ident.name == sym::some_method // Optionally, check the type of the self argument. // - See "Checking for a specific type" { @@ -85,9 +85,8 @@ to check for. All of these methods only check for the base type, generic arguments have to be checked separately. ```rust -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::paths; -use rustc_span::symbol::sym; +use clippy_utils::{paths, sym}; +use clippy_utils::res::MaybeDef; use rustc_hir::LangItem; impl LateLintPass<'_> for MyStructLint { @@ -97,12 +96,12 @@ impl LateLintPass<'_> for MyStructLint { // 1. Using diagnostic items // The last argument is the diagnostic item to check for - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { // The type is an `Option` } // 2. Using lang items - if is_type_lang_item(cx, ty, LangItem::RangeFull) { + if ty.is_lang_item(cx, LangItem::RangeFull) { // The type is a full range like `.drain(..)` } @@ -123,27 +122,29 @@ There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; -use clippy_utils::is_trait_method; -use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using diagnostic items with the expression - // we use `is_trait_method` function from Clippy's utils - if is_trait_method(cx, expr, sym::Iterator) { - // method call in `expr` belongs to `Iterator` trait - } - // 2. Using lang items with the expression type + // 1. Get the `DefId` of the trait. + // via lang items + let trait_id = cx.tcx.lang_items().drop_trait(); + // via diagnostic items + let trait_id = cx.tcx.get_diagnostic_item(sym::Eq); + + // 2. Check for the trait implementation via the `implements_trait` util. let ty = cx.typeck_results().expr_ty(expr); - if cx.tcx.lang_items() - // we are looking for the `DefId` of `Drop` trait in lang items - .drop_trait() - // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils - .is_some_and(|id| implements_trait(cx, ty, id, &[])) { - // `expr` implements `Drop` trait - } + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[])) { + // `ty` implements the trait. + } + + // 3. If the trait requires additional generic arguments + let trait_id = cx.tcx.lang_items().eq_trait(); + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[ty])) { + // `ty` implements `PartialEq` + } } } ``` @@ -173,7 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // 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_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String) + && return_ty(cx, impl_item.hir_id).is_lang_item(cx, LangItem::String) { // ... } diff --git a/src/tools/clippy/book/src/development/macro_expansions.md b/src/tools/clippy/book/src/development/macro_expansions.md index ed547130b358e..63a96dc373f31 100644 --- a/src/tools/clippy/book/src/development/macro_expansions.md +++ b/src/tools/clippy/book/src/development/macro_expansions.md @@ -37,7 +37,7 @@ before emitting suggestions to the end user to avoid false positives. Several functions are available for working with macros. -### The `Span.from_expansion` method +### The `Span::from_expansion` method We could utilize a `span`'s [`from_expansion`] method, which detects if the `span` is from a macro expansion / desugaring. @@ -50,7 +50,7 @@ if expr.span.from_expansion() { } ``` -### `Span.ctxt` method +### `Span::ctxt` method The `span`'s context, given by the method [`ctxt`] and returning [SyntaxContext], represents if the span is from a macro expansion and, if it is, which diff --git a/src/tools/clippy/book/src/development/method_checking.md b/src/tools/clippy/book/src/development/method_checking.md index b3126024b990d..cca6d6ae7bf34 100644 --- a/src/tools/clippy/book/src/development/method_checking.md +++ b/src/tools/clippy/book/src/development/method_checking.md @@ -15,20 +15,20 @@ the [`ExprKind`] that we can access from `expr.kind`: ```rust use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::sym; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` - && path.ident.name.as_str() == "our_fancy_method" + && path.ident.name == sym::our_fancy_method // We can check the type of the self argument whenever necessary. // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) // See the next section for more information. - && is_trait_method(cx, self_arg, sym::OurFancyTrait) + && cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) { println!("`expr` is a method call for `our_fancy_method`"); } @@ -41,6 +41,10 @@ information on the pattern matching. As mentioned in [Define Lints](defining_lints.md#lint-types), the `methods` lint type is full of pattern matching with `MethodCall` in case the reader wishes to explore more. +New symbols such as `our_fancy_method` need to be added to the `clippy_utils::sym` module. +This module extends the list of symbols already provided by the compiler crates +in `rustc_span::sym`. + ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other @@ -56,11 +60,10 @@ Let us take a look at how we might check for the implementation of `our_fancy_method` on a type: ```rust -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::return_ty; +use clippy_utils::{return_ty, sym}; +use clippy_utils::res::MaybeDef; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { @@ -71,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go even further and even check if its return type is `String` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::String) + && return_ty(cx, impl_item.hir_id).is_diag_item(cx, sym::String) { println!("`our_fancy_method` is implemented!"); } diff --git a/src/tools/clippy/book/src/development/trait_checking.md b/src/tools/clippy/book/src/development/trait_checking.md index c6f6f6bd2f99e..714607ef25e5f 100644 --- a/src/tools/clippy/book/src/development/trait_checking.md +++ b/src/tools/clippy/book/src/development/trait_checking.md @@ -17,10 +17,10 @@ providing the `LateContext` (`cx`), our expression at hand, and the symbol of the trait in question: ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -53,7 +53,7 @@ For instance, if we want to examine whether an expression `expr` implements we can check that the `Ty` of the `expr` implements the trait: ```rust -use clippy_utils::implements_trait; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +79,8 @@ If neither diagnostic item nor a language item is available, we can use 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::{implements_trait, paths}; +use clippy_utils::paths; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; @@ -124,8 +125,8 @@ The following code demonstrates how to do this: ```rust use rustc_middle::ty::Ty; +use clippy_utils::sym; use clippy_utils::ty::implements_trait; -use rustc_span::symbol::sym; let ty = todo!("Get the `Foo` type to check for a trait implementation"); let borrow_id = cx.tcx.get_diagnostic_item(sym::Borrow).unwrap(); // avoid unwrap in real code diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index b2ba19631f133..b9ecff1fa3643 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -671,6 +671,16 @@ A list of paths to types that should be treated as if they do not contain interi * [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) +## `inherent-impl-lint-scope` +Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted. + +**Default Value:** `"crate"` + +--- +**Affected lints:** +* [`multiple_inherent_impl`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl) + + ## `large-error-threshold` The maximum size of the `Err`-variant in a `Result` returned from a function @@ -927,6 +937,16 @@ exported visibility, or whether they are marked as "pub". * [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) +## `recursive-self-in-type-definitions` +Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + +**Default Value:** `true` + +--- +**Affected lints:** +* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) + + ## `semicolon-inside-block-ignore-singleline` Whether to lint only if it's multiline. diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 9ad434604dfcf..9993765b4bdc4 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,9 +1,9 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, - Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, - SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, - SourceItemOrderingWithinModuleItemGroupings, + DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, + PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, + SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; @@ -248,7 +248,7 @@ macro_rules! define_Conf { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "kebab-case")] - #[allow(non_camel_case_types)] + #[expect(non_camel_case_types)] enum Field { $($name,)* third_party, } struct ConfVisitor<'a>(&'a SourceFile); @@ -663,6 +663,9 @@ define_Conf! { /// A list of paths to types that should be treated as if they do not contain interior mutability #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)] ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()]), + /// Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted. + #[lints(multiple_inherent_impl)] + inherent_impl_lint_scope: InherentImplLintScope = InherentImplLintScope::Crate, /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, @@ -809,6 +812,9 @@ define_Conf! { /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + /// Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + #[lints(use_self)] + recursive_self_in_type_definitions: bool = true, /// Whether to lint only if it's multiline. #[lints(semicolon_inside_block)] semicolon_inside_block_ignore_singleline: bool = false, @@ -1213,7 +1219,7 @@ mod tests { for entry in toml_files { let file = fs::read_to_string(entry.path()).unwrap(); - #[allow(clippy::zero_sized_map_values)] + #[expect(clippy::zero_sized_map_values)] if let Ok(map) = toml::from_str::>(&file) { for name in map.keys() { names.remove(name.as_str()); diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index f64eefa0c232d..8d9326a904b1e 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -131,7 +131,7 @@ impl DisallowedPathEnum { } /// Creates a map of disallowed items to the reason they were disallowed. -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] pub fn create_disallowed_map( tcx: TyCtxt<'_>, disallowed_paths: &'static [DisallowedPath], @@ -698,3 +698,11 @@ pub enum PubUnderscoreFieldsBehaviour { PubliclyExported, AllPubFields, } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum InherentImplLintScope { + Crate, + File, + Module, +} diff --git a/src/tools/clippy/clippy_dev/src/dogfood.rs b/src/tools/clippy/clippy_dev/src/dogfood.rs index d0fca952b9322..9eb323eaef5aa 100644 --- a/src/tools/clippy/clippy_dev/src/dogfood.rs +++ b/src/tools/clippy/clippy_dev/src/dogfood.rs @@ -4,7 +4,7 @@ use itertools::Itertools; /// # Panics /// /// Panics if unable to run the dogfood test -#[allow(clippy::fn_params_excessive_bools)] +#[expect(clippy::fn_params_excessive_bools)] pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { run_exit_on_err( "cargo test", diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 5fef231f6ca1c..1b6a590b896f4 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -192,7 +192,7 @@ enum DevCommand { /// Which lint's page to load initially (optional) lint: Option, }, - #[allow(clippy::doc_markdown)] + #[expect(clippy::doc_markdown)] /// Manually run clippy on a file or package /// /// ## Examples diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 4121daa85e6d7..a14afd8c5f416 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -443,7 +443,6 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R Ok(()) } -#[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { let lint_name_upper = lint.name.to_uppercase(); diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 36498adff502e..b8bf8b25b3233 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -167,7 +167,7 @@ declare_clippy_lint! { impl_lint_pass!(ArbitrarySourceItemOrdering => [ARBITRARY_SOURCE_ITEM_ORDERING]); #[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] // Bools are cached feature flags. +#[expect(clippy::struct_excessive_bools, reason = "Bools are cached feature flags")] pub struct ArbitrarySourceItemOrdering { assoc_types_order: SourceItemOrderingTraitAssocItemKinds, enable_ordering_for_enum: bool, diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs index 085029a744bec..acfdfa65baeda 100644 --- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs +++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -46,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { && let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind && func_name.ident.name == sym::new && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc) + && cx.typeck_results().node_type(func_ty.hir_id).is_diag_item(cx, sym::Arc) && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index 08253b0c4995e..cc62306b33b53 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; +use clippy_utils::sym; +use clippy_utils::ty::{has_debug_impl, is_copy}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Node}; @@ -55,13 +56,13 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { && let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind && let result_type_with_refs = cx.typeck_results().expr_ty(recv) && let result_type = result_type_with_refs.peel_refs() - && is_type_diagnostic_item(cx, result_type, sym::Result) + && result_type.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = result_type.kind() { if !is_copy(cx, result_type) { if result_type_with_refs != result_type { return; - } else if let Res::Local(binding_id) = path_res(cx, recv) + } else if let Res::Local(binding_id) = *recv.basic_res() && local_used_after_expr(cx, binding_id, recv) { return; diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 52287be34c784..efce23d13a382 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; +use clippy_utils::{is_in_test, last_path_segment, local_is_initialized, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -68,15 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && let typeck = cx.typeck_results() - && let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind { + && let (call_kind, fn_name, fn_def, fn_arg, fn_gen_args) = match rhs.kind { ExprKind::Call(f, [arg]) if let ExprKind::Path(fn_path) = &f.kind - && let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() => + && let Some(def) = typeck.qpath_res(fn_path, f.hir_id).opt_def(cx) => { - (CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id)) + (CallKind::Ufcs, last_path_segment(fn_path).ident.name, def, arg, typeck.node_args(f.hir_id)) }, - ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => { - (CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id)) + ExprKind::MethodCall(name, recv, [], _) if let Some(def) = typeck.type_dependent_def(rhs.hir_id) => { + (CallKind::Method, name.ident.name, def, recv, typeck.node_args(rhs.hir_id)) }, _ => return, } @@ -84,20 +85,20 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // Don't lint in macros. && ctxt.is_root() && let which_trait = match fn_name { - sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, + sym::clone if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Clone) => CloneTrait::Clone, sym::to_owned - if is_diag_trait_item(cx, fn_id, sym::ToOwned) + if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::ToOwned) && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, _ => return, } - && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_id, fn_gen_args) + && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_def.1, fn_gen_args) // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - && path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs)) + && lhs.res_local_id().is_none_or(|lhs| local_is_initialized(cx, lhs)) && let Some(resolved_impl) = cx.tcx.impl_of_assoc(resolved_fn.def_id()) // Derived forms don't implement `clone_from`/`clone_into`. // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 64aeb27df693f..f3985603c4d2e 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -431,9 +432,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio }, ExprKind::MethodCall(path, receiver, args, _) => { let type_of_receiver = cx.typeck_results().expr_ty(receiver); - if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option) - && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result) - { + if !type_of_receiver.is_diag_item(cx, sym::Option) && !type_of_receiver.is_diag_item(cx, sym::Result) { return None; } METHODS_WITH_NEGATION diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 3b861848f94a6..6f51f2343ab5b 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_default_equivalent; use clippy_utils::macros::macro_backtrace; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::expr_sig; -use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; -use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind}; +use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LangItem, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::{Span, sym}; @@ -44,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault { // And that method is `new` && seg.ident.name == sym::new // And the call is that of a `Box` method - && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) // And the single argument to the call is another function call // This is the `T::default()` (or default equivalent) of `Box::new(T::default())` && let ExprKind::Call(arg_path, _) = arg.kind diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index ff5320719aa28..be1f406770ce6 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym}; +use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; @@ -53,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 - && is_path_diagnostic_item(cx, fun, sym::mem_align_of) + && fun.basic_res().is_diag_item(cx, sym::mem_align_of) && let Some(args) = path.segments.last().and_then(|seg| seg.args) && let [GenericArg::Type(generic_ty)] = args.args { diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index c88a0539d70e1..7bfe9201d812a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SpanRangeExt, snippet_opt}; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -167,11 +168,11 @@ pub(super) fn check<'tcx>( sym::assert_ne_macro, sym::debug_assert_ne_macro, ]; - matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + 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) + if let Some(id) = cast_expr.res_local_id() && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs index 72ab292ee3c66..35b799aefb04c 100644 --- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; 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 clippy_utils::{is_in_const_context, is_mutable}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(_, val, _, _) = item.kind - && is_trait_method(cx, item, sym::Clone) + && cx.ty_based_def(item).opt_parent(cx).is_diag_item(cx, sym::Clone) // check for immutability or purity && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index c0f30e456d8db..595625c08bef9 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{IntoSpan, SpanRangeExt}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; @@ -93,7 +93,7 @@ impl CognitiveComplexity { }); let ret_ty = cx.typeck_results().node_type(expr.hir_id); - let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { + let ret_adjust = if ret_ty.is_diag_item(cx, sym::Result) { returns } else { #[expect(clippy::integer_division)] diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index 1279be34ed8f2..fd84ce70bd717 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::get_enclosing_block; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::{Visitable, for_each_expr}; -use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BTreeMap | sym::BTreeSet @@ -71,7 +71,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { | sym::Vec | sym::VecDeque ) - ) || is_type_lang_item(cx, ty, LangItem::String) + ) || ty.is_lang_item(cx, LangItem::String) } fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool { @@ -81,7 +81,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // Inspect all expressions and sub-expressions in the block. for_each_expr(cx, block, |expr| { // Ignore expressions that are not simply `id`. - if !path_to_local_id(expr, id) { + if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); } @@ -93,7 +93,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // id = ...; // Not reading `id`. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::Assign(lhs, ..) = parent.kind - && path_to_local_id(lhs, id) + && lhs.res_local_id() == Some(id) { return ControlFlow::Continue(()); } @@ -107,7 +107,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // have side effects, so consider them a read. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::MethodCall(_, receiver, args, _) = parent.kind - && path_to_local_id(receiver, id) + && receiver.res_local_id() == Some(id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && !method_def_id.is_local() { diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 0ec0aaaad4536..375d179681da3 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -488,6 +488,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO, crate::methods::UNNECESSARY_MAP_OR_INFO, crate::methods::UNNECESSARY_MIN_OR_MAX_INFO, + crate::methods::UNNECESSARY_OPTION_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, @@ -655,6 +656,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::regex::REGEX_CREATION_IN_LOOPS_INFO, crate::regex::TRIVIAL_REGEX_INFO, crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO, + crate::replace_box::REPLACE_BOX_INFO, crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO, crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, crate::returns::LET_AND_RETURN_INFO, @@ -780,6 +782,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::visibility::NEEDLESS_PUB_SELF_INFO, crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::visibility::PUB_WITH_SHORTHAND_INFO, + crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, crate::write::PRINTLN_EMPTY_STRING_INFO, diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 2147f72889093..f087a894d76ae 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -7,7 +7,6 @@ macro_rules! declare_with_version { $e:expr, )*]) => { pub static $name: &[(&str, &str)] = &[$($e),*]; - #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 9ebb8e6e15d9e..de1362081323a 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, - path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -239,7 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } - if let Some(local) = path_to_local(expr) { + if let Some(local) = expr.res_local_id() { self.check_local_usage(cx, expr, local); } diff --git a/src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index dfb723b86eb93..b2bc6402561f5 100644 --- a/src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::fulfill_or_allowed; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; @@ -60,14 +61,16 @@ pub(super) fn check<'tcx>( return; } - span_lint_hir_and_then( + if fulfill_or_allowed(cx, EXPL_IMPL_CLONE_ON_COPY, [adt_hir_id]) { + return; + } + + span_lint_and_help( cx, EXPL_IMPL_CLONE_ON_COPY, - adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - |diag| { - diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); - }, + None, + "consider deriving `Clone` or removing `Copy`", ); } diff --git a/src/tools/clippy/clippy_lints/src/derive/mod.rs b/src/tools/clippy/clippy_lints/src/derive/mod.rs index 06efc2709faa6..eafe7c4bb9f23 100644 --- a/src/tools/clippy/clippy_lints/src/derive/mod.rs +++ b/src/tools/clippy/clippy_lints/src/derive/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { self_ty, .. }) = item.kind - && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Res::Def(_, def_id) = *self_ty.basic_res() && let Some(local_def_id) = def_id.as_local() { let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 3033ac0d0b0b7..b164a9a99782b 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,7 +1,8 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait_with_env; use clippy_utils::visitors::for_each_expr; use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; @@ -62,7 +63,7 @@ pub fn check( ); } if !headers.errors { - if is_type_diagnostic_item(cx, return_ty(cx, owner_id), sym::Result) { + if return_ty(cx, owner_id).is_diag_item(cx, sym::Result) { span_lint( cx, MISSING_ERRORS_DOC, @@ -83,7 +84,7 @@ pub fn check( &[], ) && let ty::Coroutine(_, subs) = ret_ty.kind() - && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) + && subs.as_coroutine().return_ty().is_diag_item(cx, sym::Result) { span_lint( cx, @@ -119,10 +120,7 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]).or_else(|| method_chain_args(expr, &[sym::expect])) && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() - && matches!( - get_type_diagnostic_name(cx, receiver_ty), - Some(sym::Option | sym::Result) - ) + && matches!(receiver_ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) && panic_span.is_none() { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index cfdf9ca623688..2a3fb82946117 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -970,7 +970,7 @@ fn check_for_code_clusters<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, valid_idents: &FxHashSet, diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 5c360ce6a5f7e..3bb8c484ceec3 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_must_use_func_call; -use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::{is_copy, is_must_use_ty}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -97,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, sym::mem_forget if is_copy => return, - sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return, + sym::mem_drop if arg_ty.is_lang_item(cx, LangItem::ManuallyDrop) => return, sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.typing_env()) || is_must_use_func_call(cx, arg) diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs index 3018e1f127347..3d8650f05168d 100644 --- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs +++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::implements_trait; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Item, ItemKind}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_ref.trait_def_id()) && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id - && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) + && let Some(def_id) = imp.self_ty.basic_res().opt_def_id().and_then(DefId::as_local) && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) && ident.name == sym::Error && is_visible_outside_module(cx, def_id) => diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 752f39b4e6dc1..21385ee4fdc7f 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -1,11 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{ - get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, -}; +use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate}; use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::attrs::AttributeKind; @@ -86,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { let body = if let ExprKind::Closure(c) = expr.kind && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) @@ -144,7 +142,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { let callee_ty_raw = typeck.expr_ty(callee); let callee_ty = callee_ty_raw.peel_refs(); - if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + if matches!(callee_ty.opt_diag_name(cx), Some(sym::Arc | sym::Rc)) || !check_inputs(typeck, body.params, None, args) { return; @@ -218,7 +216,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).is_some_and(|l| { + if callee.res_local_id().is_some_and(|l| { // FIXME: Do we really need this `local_used_in` check? // Isn't it checking something like... `callee(callee)`? // If somehow this check is needed, add some test for it, @@ -307,7 +305,7 @@ fn check_inputs( matches!( p.pat.kind, PatKind::Binding(BindingMode::NONE, id, _, None) - if path_to_local_id(arg, id) + if arg.res_local_id() == Some(id) ) // Only allow adjustments which change regions (i.e. re-borrowing). && typeck diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 085ee4448a4ee..c59ffa14a5fee 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, path_def_id, sym}; +use clippy_utils::{is_expn_of, is_in_test, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; @@ -59,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) && let ExprKind::Call(write_recv_path, []) = write_recv.kind && write_fun.ident.name == sym::write_fmt - && let Some(def_id) = path_def_id(cx, write_recv_path) + && let Some(def_id) = write_recv_path.basic_res().opt_def_id() { // match calls to std::io::stdout() / std::io::stderr () let (dest_name, prefix) = match cx.tcx.get_diagnostic_name(def_id) { @@ -71,6 +72,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { return; }; + // Performing an explicit write in a test circumvent's libtest's capture of stdio and stdout. + if is_in_test(cx.tcx, expr.hir_id) { + return; + } + // ordering is important here, since `writeln!` uses `write!` internally let calling_macro = if is_expn_of(write_call.span, sym::writeln).is_some() { Some("writeln") diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index fdfcbb540bce6..4446b912bf7ee 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::method_chain_args; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -84,9 +84,7 @@ fn lint_impl_body(cx: &LateContext<'_>, item_def_id: hir::OwnerId, impl_span: Sp // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { + if receiver_ty.is_diag_item(self.lcx, sym::Option) || receiver_ty.is_diag_item(self.lcx, sym::Result) { self.result.push(expr.span); } } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 407a3f1306739..5f022ba307ff9 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::{ - eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, - is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, is_no_std_crate, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_ast::ast; use rustc_errors::Applicability; @@ -737,7 +738,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind { 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) { + if recv_ty.is_floating_point() && !is_no_std_crate(cx) && cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { match path.ident.name { sym::ln => check_ln1p(cx, expr, receiver), sym::log => check_log_base(cx, expr, receiver, args), diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 94e66769eb265..098bf4ba42f93 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -39,7 +39,6 @@ declare_clippy_lint! { "useless use of `format!`" } -#[allow(clippy::module_name_repetitions)] pub struct UselessFormat { format_args: FormatArgsStorage, } diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 3359aa603239e..011cbf8c5d41c 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -9,9 +9,10 @@ use clippy_utils::macros::{ root_macro_call_first_node, }; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_from_proc_macro, is_in_test, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -237,7 +238,7 @@ impl_lint_pass!(FormatArgs<'_> => [ POINTER_FORMAT, ]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] pub struct FormatArgs<'tcx> { format_args: FormatArgsStorage, msrv: Msrv, @@ -344,7 +345,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { if let Some(placeholder_span) = placeholder.span && *options != FormatOptions::default() && let ty = self.cx.typeck_results().expr_ty(arg).peel_refs() - && is_type_lang_item(self.cx, ty, LangItem::FormatArguments) + && ty.is_lang_item(self.cx, LangItem::FormatArguments) { span_lint_and_then( self.cx, @@ -497,8 +498,11 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { let cx = self.cx; if !value.span.from_expansion() && let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind - && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToString) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToString) && let receiver_ty = cx.typeck_results().expr_ty(receiver) && let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display) && let (n_needed_derefs, target) = diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 416aea51ea195..903d43e56c4b2 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -1,6 +1,7 @@ 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, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_as_impl, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -157,8 +158,12 @@ impl FormatImplExpr<'_, '_> { && path.ident.name == sym::to_string // Is the method a part of the ToString trait? (i.e. not to_string() implemented // separately) - && let Some(expr_def_id) = self.cx.typeck_results().type_dependent_def_id(self.expr.hir_id) - && is_diag_trait_item(self.cx, expr_def_id, sym::ToString) + && self + .cx + .typeck_results() + .type_dependent_def_id(self.expr.hir_id) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::ToString) // Is the method is called on self && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind && let [segment] = path.segments @@ -210,7 +215,7 @@ impl FormatImplExpr<'_, '_> { // Since the argument to fmt is itself a reference: &self let reference = peel_ref_operators(self.cx, arg); // Is the reference self? - if path_to_local(reference).map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { + if reference.res_local_id().map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { let FormatTraitNames { name, .. } = self.format_trait_impl; span_lint( self.cx, diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index b64d608c0c709..a23ba9ab837ae 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -41,7 +41,10 @@ declare_clippy_lint! { declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let e = e.peel_blocks().peel_borrows(); diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index e3bb5ee10db7c..fd807290dc090 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -4,7 +4,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; @@ -90,7 +90,12 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { |diag| { // If the target type is likely foreign mention the orphan rules as it's a common source of // confusion - if path_def_id(cx, target_ty.peel_refs()).is_none_or(|id| !id.is_local()) { + if target_ty + .peel_refs() + .basic_res() + .opt_def_id() + .is_none_or(|id| !id.is_local()) + { diag.help( "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs index 5e2e2c9dbf725..0e6eeb75ec573 100644 --- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeResPath; +use clippy_utils::sym; 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}; @@ -41,7 +42,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind && seg.ident.name == sym::from_raw - && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) + && let Some(type_str) = ty.basic_res().opt_def_id().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 && is_c_void(cx, *ty) diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index d5873b3f85aa1..df8a35d9658b0 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; 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}; @@ -89,5 +89,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_lang_item(cx, ty, LangItem::String) || ty.peel_refs().is_str() + ty.is_lang_item(cx, LangItem::String) || ty.peel_refs().is_str() } diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 8de68bfcb511b..68532de0368f8 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -132,7 +132,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr } // FIXME: needs to be an EARLY LINT. all attribute lints should be -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 72f8795304055..c6b0e7c54c06f 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -1,12 +1,13 @@ +use clippy_utils::res::MaybeResPath; use rustc_hir::{self as hir, HirId, HirIdSet, intravisit}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; +use clippy_utils::iter_input_pats; use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{iter_input_pats, path_to_local}; use core::ops::ControlFlow; @@ -87,7 +88,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option { } fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { - if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) { + if arg.res_local_id().is_some_and(|id| raw_ptrs.contains(&id)) { span_lint( cx, NOT_UNSAFE_PTR_ARG_DEREF, diff --git a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs index 5dc1b7269b761..cc9dc47e15f2b 100644 --- a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs +++ b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs @@ -62,7 +62,7 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, kind: FnKind<'a>, diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index 1f2fce687ed1b..fb80cc1a63a30 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -1,4 +1,5 @@ use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use rustc_errors::Diag; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size}; use clippy_utils::{is_no_std_crate, trait_ref_of_method}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -24,7 +25,7 @@ fn result_err_ty<'tcx>( && let ty = cx .tcx .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().output()) - && is_type_diagnostic_item(cx, ty, sym::Result) + && ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = ty.kind() { let err_ty = args.type_at(1); diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index a99118f90f88a..eed2d5d885353 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, higher, sym}; use core::ops::ControlFlow; @@ -95,7 +95,7 @@ fn mutex_lock_call<'tcx>( if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.name == sym::lock && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + && ty.is_diag_item(cx, sym::Mutex) && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op)) { ControlFlow::Break(self_arg) diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index f9fee292837ea..dfc8411baa005 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -2,11 +2,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, sym, + contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -73,8 +73,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && !expr.span.from_expansion() && !then_expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) + && then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + && peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) diff --git a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs index eb1025f71498e..b3f597cc8736e 100644 --- a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, - path_to_local, }; use core::iter; use core::ops::ControlFlow; @@ -149,7 +149,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { /// Checks if the statement modifies or moves any of the given locals. fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { for_each_expr_without_closures(s, |e| { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && locals.contains(&id) && !capture_local_usage(cx, e).is_imm_ref() { @@ -198,7 +198,7 @@ fn scan_block_for_eq<'tcx>( let mut cond_locals = HirIdSet::default(); for &cond in conds { let _: Option = for_each_expr_without_closures(cond, |e| { - if let Some(id) = path_to_local(e) { + if let Some(id) = e.res_local_id() { cond_locals.insert(id); } ControlFlow::Continue(()) diff --git a/src/tools/clippy/clippy_lints/src/ifs/ifs_same_cond.rs b/src/tools/clippy/clippy_lints/src/ifs/ifs_same_cond.rs index ca76fc2587db9..3ea941320a086 100644 --- a/src/tools/clippy/clippy_lints/src/ifs/ifs_same_cond.rs +++ b/src/tools/clippy/clippy_lints/src/ifs/ifs_same_cond.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::InteriorMut; -use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, search_same}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ fn method_caller_is_mutable<'tcx>( interior_mut.is_interior_mut_ty(cx, caller_ty) || caller_ty.is_mutable_ptr() // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) + || caller_expr.res_local_id() .and_then(|hid| find_binding_init(cx, hid)) .is_none() } diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index b3c90f364e834..d2bc0b6d99354 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::{Visitor, VisitorExt, walk_body, walk_expr, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -14,7 +15,6 @@ use rustc_span::Span; 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! { /// ### What it does @@ -227,14 +227,14 @@ impl<'tcx> ImplicitHasherType<'tcx> { let ty = lower_ty(cx.tcx, hir_ty); - if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 { + if ty.is_diag_item(cx, sym::HashMap) && params_len == 2 { Some(ImplicitHasherType::HashMap( hir_ty.span, ty, snippet(cx, params[0].span, "K"), snippet(cx, params[1].span, "V"), )) - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) && params_len == 1 { + } else if ty.is_diag_item(cx, sym::HashSet) && params_len == 1 { Some(ImplicitHasherType::HashSet( hir_ty.span, ty, diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index c634c12e1877b..678a29924e528 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_manual_check<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, @@ -165,7 +165,7 @@ fn check_manual_check<'tcx>( } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_gt( cx: &LateContext<'_>, condition_span: Span, @@ -196,7 +196,7 @@ fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { eq_expr_value(cx, expr, expr) } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 8f9f71a147694..919702c5714ad 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::is_lint_allowed; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::is_copy; -use clippy_utils::{is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -225,7 +226,7 @@ impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let Some(local_id) = path_to_local(expr) { + if let Some(local_id) = expr.res_local_id() { let Self { cx, ref mut slice_lint_info, diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs index a159f61571839..bc57d9e85478a 100644 --- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -47,7 +47,11 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { if let ExprKind::MethodCall(name, recv, [_], _) = expr.kind && name.ident.name == sym::open && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::FsOpenOptions) + && cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::FsOpenOptions) { let mut append = false; let mut write = None; diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index bf3eafe09b3d0..f193f065e68d5 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -235,7 +236,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BinaryHeap | sym::BTreeMap diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 309d2dfb28b87..a08efbc52d457 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -1,17 +1,23 @@ +use clippy_config::Conf; +use clippy_config::types::InherentImplLintScope; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_lint_allowed; +use clippy_utils::fulfill_or_allowed; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::{Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_session::impl_lint_pass; +use rustc_span::{FileName, Span}; use std::collections::hash_map::Entry; declare_clippy_lint! { /// ### What it does /// Checks for multiple inherent implementations of a struct /// + /// The config option controls the scope in which multiple inherent `impl` blocks for the same + /// struct are linted, allowing values of `module` (only within the same module), `file` + /// (within the same file), or `crate` (anywhere in the crate, default). + /// /// ### Why restrict this? /// Splitting the implementation of a type makes the code harder to navigate. /// @@ -41,7 +47,26 @@ declare_clippy_lint! { "Multiple inherent impl that could be grouped" } -declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); +impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); + +pub struct MultipleInherentImpl { + scope: InherentImplLintScope, +} + +impl MultipleInherentImpl { + pub fn new(conf: &'static Conf) -> Self { + Self { + scope: conf.inherent_impl_lint_scope, + } + } +} + +#[derive(Hash, Eq, PartialEq, Clone)] +enum Criterion { + Module(LocalModDefId), + File(FileName), + Crate, +} impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { @@ -55,18 +80,27 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { for (&id, impl_ids) in &impls.inherent_impls { if impl_ids.len() < 2 - // Check for `#[allow]` on the type definition - || is_lint_allowed( + // Check for `#[expect]` or `#[allow]` on the type definition + || fulfill_or_allowed( cx, MULTIPLE_INHERENT_IMPL, - cx.tcx.local_def_id_to_hir_id(id), + [cx.tcx.local_def_id_to_hir_id(id)], ) { continue; } for impl_id in impl_ids.iter().map(|id| id.expect_local()) { let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity(); - match type_map.entry(impl_ty) { + let hir_id = cx.tcx.local_def_id_to_hir_id(impl_id); + let criterion = match self.scope { + InherentImplLintScope::Module => Criterion::Module(cx.tcx.parent_module(hir_id)), + InherentImplLintScope::File => { + let span = cx.tcx.hir_span(hir_id); + Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).name.clone()) + }, + InherentImplLintScope::Crate => Criterion::Crate, + }; + match type_map.entry((impl_ty, criterion)) { Entry::Vacant(e) => { // Store the id for the first impl block of this type. The span is retrieved lazily. e.insert(IdOrSpan::Id(impl_id)); @@ -97,7 +131,6 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // Switching to the next type definition, no need to keep the current entries around. type_map.clear(); } - // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. lint_spans.sort_by_key(|x| x.0.lo()); for (span, first_span) in lint_spans { @@ -125,7 +158,7 @@ fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { { (!span.from_expansion() && impl_item.generics.params.is_empty() - && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id)) + && !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id])) .then_some(span) } else { None diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 7f2e25367a6a4..e569a5c7b6126 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{return_ty, trait_ref_of_method}; use rustc_abi::ExternAbi; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; @@ -104,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) && !impl_item.span.from_expansion() // Check if return type is String - && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) + && return_ty(cx, impl_item.owner_id).is_lang_item(cx, LangItem::String) // Filters instances of to_string which are required by a trait && trait_ref_of_method(cx, impl_item.owner_id).is_none() { diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs index b1cb6da9475ba..6bb46ac3b5542 100644 --- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs +++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::higher::ForLoop; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -55,9 +55,7 @@ impl LateLintPass<'_> for IterOverHashType { if let Some(for_loop) = ForLoop::hir(expr) && !for_loop.body.span.from_expansion() && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() - && hash_iter_tys - .into_iter() - .any(|sym| is_type_diagnostic_item(cx, ty, sym)) + && hash_iter_tys.into_iter().any(|sym| ty.is_diag_item(cx, sym)) { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 42c636505c015..8a5d97294d2bd 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -113,35 +113,18 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { // since this would only require removing a `use` import (which is already linted). && !is_numeric_const_path_canonical(path, [*mod_name, *name]) { - ( - vec![(expr.span, format!("{mod_name}::{name}"))], - "usage of a legacy numeric constant", - ) + (format!("{mod_name}::{name}"), "usage of a legacy numeric constant") // `::xxx_value` check } else if let ExprKind::Call(func, []) = &expr.kind && let ExprKind::Path(qpath) = &func.kind && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) + && let Some(mod_name) = ty.span.get_source_text(cx) + && ty.span.eq_ctxt(last_segment.ident.span) { - let mut sugg = vec![ - // Replace the function name up to the end by the constant name - ( - last_segment.ident.span.to(expr.span.shrink_to_hi()), - last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(), - ), - ]; - let before_span = expr.span.shrink_to_lo().until(ty.span); - if !before_span.is_empty() { - // Remove everything before the type name - sugg.push((before_span, String::new())); - } - // Use `::` between the type name and the constant - let between_span = ty.span.shrink_to_hi().until(last_segment.ident.span); - if !between_span.check_source_text(cx, |s| s == "::") { - sugg.push((between_span, String::from("::"))); - } - (sugg, "usage of a legacy numeric method") + let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); + (format!("{mod_name}::{name}"), "usage of a legacy numeric method") } else { return; }; @@ -151,7 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && !is_from_proc_macro(cx, expr) { span_lint_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.span, msg, |diag| { - diag.multipart_suggestion_verbose( + diag.span_suggestion_verbose( + expr.span, "use the associated constant instead", sugg, Applicability::MaybeIncorrect, diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 28a0fbc051158..04a8e4739b853 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; 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_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, -}; +use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, parent_item_name, peel_ref_operators, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -204,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } if let ExprKind::MethodCall(method, lhs_expr, [rhs_expr], _) = expr.kind - && is_trait_method(cx, expr, sym::PartialEq) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::PartialEq) && !expr.span.from_expansion() { check_empty_expr( diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs index e480c8fbed53c..2dbf55a8540ba 100644 --- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use rustc_errors::Applicability; @@ -145,7 +145,7 @@ fn check_assign<'tcx>( && let Some(expr) = block.stmts.iter().last() && let hir::StmtKind::Semi(expr) = expr.kind && let hir::ExprKind::Assign(var, value, _) = expr.kind - && path_to_local_id(var, decl) + && var.res_local_id() == Some(decl) { if block .stmts diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index dcc2d985f3fe9..a4ad9424b3eb1 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -318,6 +318,7 @@ mod ref_patterns; mod reference; mod regex; mod repeat_vec_with_capacity; +mod replace_box; mod reserve_after_initialization; mod return_self_not_must_use; mod returns; @@ -394,6 +395,7 @@ mod useless_conversion; mod vec; mod vec_init_then_push; mod visibility; +mod volatile_composites; mod wildcard_imports; mod write; mod zero_div_zero; @@ -581,7 +583,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); - store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); + store.register_late_pass(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); @@ -612,7 +614,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co 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)); - store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); + store.register_late_pass(|_| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); @@ -758,7 +760,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))); - store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); + store.register_late_pass(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))); store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))); store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf))); store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))); @@ -825,5 +827,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); + store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites)); + store.register_late_pass(|_| Box::new(replace_box::ReplaceBox)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index d8b186b6787d1..519ec228c8840 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, @@ -540,7 +540,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] struct Usage { lifetime: Lifetime, in_where_predicate: bool, diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index 14ccb6fce2239..65e922ac07d36 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,10 +73,13 @@ impl_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]); impl LateLintPass<'_> for LinesFilterMapOk { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let fm_method_name = fm_method.ident.name && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) + && cx + .typeck_results() + .expr_ty_adjusted(fm_receiver) + .is_diag_item(cx, sym::IoLines) && should_lint(cx, fm_args, fm_method_name) && self.msrv.meets(cx, msrvs::MAP_WHILE) { @@ -117,10 +120,15 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> params: [param], value, .. } = cx.tcx.hir_body(*body) && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - && path_to_local_id(receiver, param.pat.hir_id) - && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { - is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok + method.ident.name == sym::ok + && receiver.res_local_id() == Some(param.pat.hir_id) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Result) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs index a702e60f1c276..7acf2a5462220 100644 --- a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; +use clippy_utils::{eq_expr_value, higher, sym}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{Expr, ExprKind, LangItem, Node, Pat, PatKind}; use rustc_lint::LateContext; @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr { // Destructured iterator element `(idx, _)`, look for uses of the binding for_each_expr(cx, body, |expr| { - if path_to_local_id(expr, binding_id) { + if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } CONTINUE @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr // Bound as a tuple, look for `tup.0` for_each_expr(cx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind - && path_to_local_id(e, binding_id) + && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); @@ -81,7 +81,7 @@ fn check_index_usage<'tcx>( return; }; - let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); + let is_string_like = |ty: Ty<'_>| ty.is_str() || ty.is_lang_item(cx, LangItem::String); let message = match parent_expr.kind { ExprKind::MethodCall(segment, recv, ..) // We currently only lint `str` methods (which `String` can deref to), so a `.is_str()` check is sufficient here diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs index 4aa1c2e211d33..daca78e344748 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,6 +1,6 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -43,7 +43,11 @@ impl AdjustKind { } pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { - if !is_trait_method(cx, call_expr, sym::IntoIterator) { + if !cx + .ty_based_def(call_expr) + .opt_parent(cx) + .is_diag_item(cx, sym::IntoIterator) + { return; } diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index af475c40586fe..40d1d36bd162c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,10 +1,11 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; use clippy_utils::ty::{ - implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, + implements_trait, implements_trait_with_env, is_copy, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, }; use rustc_errors::Applicability; @@ -127,8 +128,7 @@ fn is_ref_iterable<'tcx>( let self_ty = typeck.expr_ty(self_arg); let self_is_copy = is_copy(cx, self_ty); - if is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox) - && !msrv.meets(cx, msrvs::BOX_INTO_ITER) + if self_ty.peel_refs().is_lang_item(cx, rustc_hir::LangItem::OwnedBox) && !msrv.meets(cx, msrvs::BOX_INTO_ITER) { return None; } diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs index e314bc2068b30..c6b650a1a88be 100644 --- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -1,7 +1,7 @@ use super::FOR_KV_MAP; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx _ => arg, }; - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + if ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) { span_lint_and_then( cx, FOR_KV_MAP, diff --git a/src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs b/src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs index b8a263817d297..8a4644cdf5efd 100644 --- a/src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs @@ -1,12 +1,12 @@ use super::ITER_NEXT_LOOP; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { - if is_trait_method(cx, arg, sym::Iterator) { + if cx.ty_based_def(arg).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint( cx, ITER_NEXT_LOOP, diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs index f99989ec6ba4b..c38cf83f44100 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -1,12 +1,12 @@ use super::MANUAL_FIND; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; +use clippy_utils::{higher, peel_blocks_with_stmt}; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::lang_items::LangItem; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -34,8 +34,8 @@ pub(super) fn check<'tcx>( && let StmtKind::Semi(semi) = stmt.kind && let ExprKind::Ret(Some(ret_value)) = semi.kind && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind - && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) - && path_res(cx, inner_ret) == Res::Local(binding_id) + && ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && inner_ret.res_local_id() == Some(binding_id) && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) { @@ -150,7 +150,7 @@ fn last_stmt_and_ret<'tcx>( && let Some((_, Node::Block(block))) = parent_iter.next() && let Some((last_stmt, last_ret)) = extract(block) && last_stmt.hir_id == node_hir - && is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone) + && last_ret.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionNone) && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header && let Some((_, func)) = parent_iter.next() diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index ddb8bb536c04e..96de118b5233b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -2,9 +2,10 @@ use super::MANUAL_FLATTEN; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt, span_contains_comment}; +use clippy_utils::{higher, is_refutable, peel_blocks_with_stmt, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( = higher::IfLet::hir(cx, inner_expr) // Ensure match_expr in `if let` statement is the same as the pat from the for-loop && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind - && path_to_local_id(let_expr, pat_hir_id) + && let_expr.res_local_id() == Some(pat_hir_id) // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` && let PatKind::TupleStruct(ref qpath, [inner_pat], _) = let_pat.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index d9c4b526da99e..a2da43c2ca245 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -1,10 +1,11 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::usage::local_used_in; -use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; +use clippy_utils::{get_enclosing_block, higher, sugg}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::intravisit::walk_block; @@ -67,7 +68,7 @@ pub(super) fn check<'tcx>( && !local_used_in(cx, canonical_id, base_left) && !local_used_in(cx, canonical_id, base_right) // Source and destination must be different - && path_to_local(base_left) != path_to_local(base_right) + && base_left.res_local_id() != base_right.res_local_id() { Some(( ty, @@ -128,7 +129,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { if let ExprKind::MethodCall(method, recv, [], _) = end.kind && method.ident.name == sym::len - && path_to_local(recv) == path_to_local(base) + && recv.res_local_id() == base.res_local_id() { if sugg.to_string() == end_str { sugg::EMPTY.into() @@ -364,7 +365,7 @@ fn get_details_from_idx<'tcx>( starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { - let id = path_to_local(e)?; + let id = e.res_local_id()?; starts.iter().find(|start| start.id == id).map(|start| start.kind) } @@ -425,7 +426,7 @@ fn get_assignments<'a, 'tcx>( .chain(*expr) .filter(move |e| { if let ExprKind::AssignOp(_, place, _) = e.kind { - path_to_local(place).is_some_and(|id| { + place.res_local_id().is_some_and(|id| { !loop_counters .iter() // skip the first item which should be `StartKind::Range` diff --git a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs index 8a2d0036203a2..d5dbcbe9dc701 100644 --- a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,7 +1,7 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::std_or_core; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) && let callee_ty = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool) + && callee_ty.is_diag_item(cx, sym::AtomicBool) && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 01c36b8cb12fc..a064a5910ef98 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -861,6 +861,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // check for `loop { if let {} else break }` that could be `while let` // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) + // (also matches on `let {} else break`) if let ExprKind::Loop(block, label, LoopSource::Loop, _) = expr.kind { // also check for empty `loop {}` statements, skipping those in #[panic_handler] empty_loop::check(cx, expr, block); @@ -870,7 +871,10 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) { + if let Some(higher::While { + condition, body, span, .. + }) = higher::While::hir(expr) + { while_immutable_condition::check(cx, condition, body); while_float::check(cx, condition); missing_spin_loop::check(cx, condition, body); @@ -880,7 +884,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } impl Loops { - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] fn check_for_loop<'tcx>( &self, cx: &LateContext<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index 70ca452013f91..daeda227f670c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -1,6 +1,7 @@ use super::MUT_RANGE_BOUND; use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::{get_enclosing_block, higher, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_enclosing_block, higher}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -39,7 +40,7 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option) { } fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option { - if let Some(hir_id) = path_to_local(bound) + if let Some(hir_id) = bound.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && let PatKind::Binding(BindingMode::MUT, ..) = pat.kind { diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 544c3c34d029c..528cc64fa7bc5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -240,7 +240,7 @@ fn is_label_for_block(cx: &LateContext<'_>, dest: &Destination) -> bool { .is_ok_and(|hir_id| matches!(cx.tcx.hir_node(hir_id), Node::Block(_))) } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn never_loop_expr<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index e792edbe23e0b..4135c63d4d7ff 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -1,9 +1,10 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeResPath}; 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, sym}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{msrvs, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -125,7 +126,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { if !self.non_deterministic_expr && !self.multiple_pushes && let Some((vec, _, _)) = self.vec_push - && let Some(hir_id) = path_to_local(vec) + && let Some(hir_id) = vec.res_local_id() { !self.used_locals.contains(&hir_id) } else { @@ -141,7 +142,7 @@ impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> { ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true, ExprKind::Block(block, _) => self.visit_block(block), _ => { - if let Some(hir_id) = path_to_local(expr) { + if let Some(hir_id) = expr.res_local_id() { self.used_locals.insert(hir_id); } walk_expr(self, expr); @@ -186,7 +187,7 @@ fn get_vec_push<'tcx>( && 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) + && cx.typeck_results().expr_ty(self_expr).is_diag_item(cx, sym::Vec) { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index 13b93d2c0097b..b893b0baad490 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) - && is_type_diagnostic_item(cx, ty, sym::Enumerate) + && ty.is_diag_item(cx, sym::Enumerate) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index 2f6950b4380c3..56d535c4f2625 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -1,5 +1,6 @@ +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::{has_iter_method, implements_trait}; -use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; +use clippy_utils::{get_parent_expr, is_integer_const, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; @@ -47,7 +48,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // If node is a variable - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); if *state == IncrementVisitorVarState::IncrOnce { @@ -175,7 +176,7 @@ impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> { } // If node is the desired variable, see how it's used - if path_to_local_id(expr, self.var_id) { + if expr.res_local_id() == Some(self.var_id) { if self.past_loop { self.state = InitializeVisitorState::DontWarn; return; @@ -255,7 +256,7 @@ fn is_conditional(expr: &Expr<'_>) -> bool { /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the /// actual `Iterator` that the loop uses. -pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { +pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applicability: &mut Applicability) -> String { let impls_iterator = cx .tcx .get_diagnostic_item(sym::Iterator) @@ -263,7 +264,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic if impls_iterator { format!( "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ) } else { // (&x).into_iter() ==> x.iter() @@ -281,12 +282,12 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{method_name}()", - sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_paren(), + sugg::Sugg::hir_with_applicability(cx, caller, "_", applicability).maybe_paren(), ) }, _ => format!( "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ), } } diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 845edb9cae158..d4285db0abfcd 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -10,19 +10,19 @@ use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind, Path, use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { - let (init, let_info) = match (loop_block.stmts, loop_block.expr) { + let (init, let_info, els) = match (loop_block.stmts, loop_block.expr) { ([stmt, ..], _) => match stmt.kind { StmtKind::Let(LetStmt { init: Some(e), - els: None, + els, pat, ty, .. - }) => (*e, Some((*pat, *ty))), - StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None), + }) => (*e, Some((*pat, *ty)), *els), + StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None, None), _ => return, }, - ([], Some(e)) => (e, None), + ([], Some(e)) => (e, None, None), _ => return, }; let has_trailing_exprs = loop_block.stmts.len() + usize::from(loop_block.expr.is_some()) > 1; @@ -38,14 +38,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo if_let.let_expr, has_trailing_exprs, let_info, - if_let.if_then, + Some(if_let.if_then), ); + } else if els.and_then(|x| x.expr).is_some_and(is_simple_break_expr) + && let Some((pat, _)) = let_info + { + could_be_while_let(cx, expr, pat, init, has_trailing_exprs, let_info, None); } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind && arm1.guard.is_none() && arm2.guard.is_none() && is_simple_break_expr(arm2.body) { - could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs, let_info, arm1.body); + could_be_while_let( + cx, + expr, + arm1.pat, + scrutinee, + has_trailing_exprs, + let_info, + Some(arm1.body), + ); } } @@ -70,7 +82,7 @@ fn could_be_while_let<'tcx>( let_expr: &'tcx Expr<'_>, has_trailing_exprs: bool, let_info: Option<(&Pat<'_>, Option<&Ty<'_>>)>, - inner_expr: &Expr<'_>, + inner_expr: Option<&Expr<'_>>, ) { if has_trailing_exprs && (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr)) @@ -85,7 +97,7 @@ fn could_be_while_let<'tcx>( // 1) it was ugly with big bodies; // 2) it was not indented properly; // 3) it wasn’t very smart (see #675). - let inner_content = if let Some((pat, ty)) = let_info + let inner_content = if let Some(((pat, ty), inner_expr)) = let_info.zip(inner_expr) // Prevent trivial reassignments such as `let x = x;` or `let _ = …;`, but // keep them if the type has been explicitly specified. && (!is_trivial_assignment(pat, peel_blocks(inner_expr)) || ty.is_some()) diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 6000ff7a36099..3ea6ba341bedb 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,9 +2,10 @@ use std::ops::ControlFlow; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; +use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -19,11 +20,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr) // get the loop containing the match expression && !uses_iter(cx, &iter_expr_struct, if_then) diff --git a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs index 5814b6815a1ea..22de5e8268bd9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs +++ b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -104,7 +105,7 @@ impl ManualAbsDiff { fn are_ty_eligible<'tcx>(&self, cx: &LateContext<'tcx>, a: &Expr<'_>, b: &Expr<'_>) -> Option<(Ty<'tcx>, usize)> { let is_int = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) && self.msrv.meets(cx, msrvs::ABS_DIFF); let is_duration = - |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); + |ty: Ty<'_>| ty.is_diag_item(cx, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 76cb22864779f..c34e0d33e713e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; +use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::{higher, is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -50,32 +51,32 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { // Should this have a config value? && !is_else_clause(cx.tcx, expr) { - let mut applicability = Applicability::MachineApplicable; - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); - let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; - let sugg = format!("assert!({cond_sugg}, {format_args_snip}){semicolon}"); - // we show to the user the suggestion without the comments, but when applying the fix, include the - // comments in the block span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", |diag| { - // comments can be noisy, do not show them to the user + let mut applicability = Applicability::MachineApplicable; + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability, - ); + comments += "\n"; } - diag.span_suggestion(expr.span, "try instead", sugg, applicability); + let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); + let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; + + let indent = indent_of(cx, expr.span); + let full_sugg = reindent_multiline( + format!("{comments}assert!({cond_sugg}, {format_args_snip}){semicolon}").as_str(), + true, + indent, + ); + diag.span_suggestion_verbose( + expr.span, + "replace `if`-then-`panic!` with `assert!`", + full_sugg, + applicability, + ); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 42fe386d2c3c4..54387e36d6c84 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -3,13 +3,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{ - MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, - peel_blocks, peel_blocks_with_stmt, sym, -}; +use clippy_utils::{eq_expr_value, is_in_const_context, peel_blocks, peel_blocks_with_stmt, sym}; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::Res; @@ -292,10 +290,12 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// # ; /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { - if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind - && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord)) + if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() + || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind - && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord)) + && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() + || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { @@ -331,18 +331,18 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option> { match func.kind { ExprKind::Path(QPath::Resolved(None, path)) => { - let id = path.res.opt_def_id()?; - match cx.tcx.get_diagnostic_name(id) { + let def = path.res.opt_def(cx)?; + match cx.tcx.get_diagnostic_name(def.1) { Some(sym::cmp_min) => Some(FunctionType::CmpMin), Some(sym::cmp_max) => Some(FunctionType::CmpMax), - _ if is_diag_trait_item(cx, id, sym::Ord) => { + _ if def.assoc_fn_parent(cx).is_diag_item(cx, sym::Ord) => { Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible"))) }, _ => None, } }, ExprKind::Path(QPath::TypeRelative(ty, seg)) => { - matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) + matches!(ty.basic_res(), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) }, _ => None, } @@ -435,7 +435,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt let first = BinaryOp::new(first)?; let second = BinaryOp::new(second)?; if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind - && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding) + && peel_blocks_with_stmt(last_arm.body).res_local_id() == Some(*binding) && last_arm.guard.is_none() { // Proceed as normal @@ -516,7 +516,7 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> }, span: first_expr.span.to(second_expr.span), make_assignment: Some(maybe_input_first_path), - hir_with_ignore_attr: Some(first_expr.hir_id()), + hir_with_ignore_attr: Some(first_expr.hir_id), }) } else { None @@ -655,8 +655,8 @@ fn is_clamp_meta_pattern<'tcx>( let (min, max) = (second_expr, first_expr); let refers_to_input = match input_hir_ids { Some((first_hir_id, second_hir_id)) => { - path_to_local_id(peel_blocks(first_bin.left), first_hir_id) - && path_to_local_id(peel_blocks(second_bin.left), second_hir_id) + peel_blocks(first_bin.left).res_local_id() == Some(first_hir_id) + && peel_blocks(second_bin.left).res_local_id() == Some(second_hir_id) }, None => eq_expr_value(cx, first_bin.left, second_bin.left), }; diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index ed0cce754b954..ee531741a5153 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -1,7 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -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}; @@ -165,6 +164,7 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_paren(); + let rhs_ty = cx.typeck_results().expr_ty(rhs); let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() && matches!( lhs.kind, @@ -182,7 +182,7 @@ fn build_suggestion( } ) ) { - format!("_{}", cx.typeck_results().expr_ty(rhs)) + format!("_{rhs_ty}") } else { String::new() }; @@ -199,9 +199,12 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); + // Dereference the RHS if it is a reference type + let divisor_snippet = match Sugg::hir_with_context(cx, rhs, expr.span.ctxt(), "_", applicability) { + sugg if rhs_ty.is_ref() => sugg.deref(), + sugg => sugg, + }; span_lint_and_sugg( cx, @@ -209,7 +212,7 @@ fn build_suggestion( expr.span, "manually reimplementing `div_ceil`", "consider using `.div_ceil()`", - sugg, + format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"), *applicability, ); } diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 60782f445ab91..a81c4dc6a7931 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, path_to_local}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // Checking all possible scenarios using a function would be a hopeless task, as we have // 16 possible alignments of constants/operands. For now, let's use `partition`. && let mut exprs = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] - && exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2 + && exprs.iter_mut().partition_in_place(|i| i.res_local_id().is_some()) == 2 && !expr.span.in_external_macro(cx.sess().source_map()) && ( is_not_const(cx.tcx, cx.tcx.hir_enclosing_body_owner(expr.hir_id).into()) @@ -149,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { && let ctxt = expr.span.ctxt() && let Some(const_1) = ecx.eval_local(const_1, ctxt) && let Some(const_2) = ecx.eval_local(const_2, ctxt) - && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) + && first.res_local_id().is_some_and(|f| second.res_local_id().is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index b3ee45cc02098..ccb8d4272bfac 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; -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}; @@ -81,8 +82,8 @@ impl LateLintPass<'_> for ManualHashOne { && !hash_expr.span.from_expansion() && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind && seg.ident.name == sym::hash - && is_trait_method(cx, hash_expr, sym::Hash) - && path_to_local_id(ref_to_hasher.peel_borrows(), hasher) + && cx.ty_based_def(hash_expr).opt_parent(cx).is_diag_item(cx, sym::Hash) + && ref_to_hasher.peel_borrows().res_local_id() == Some(hasher) && let maybe_finish_stmt = stmts.next() // There should be no more statements referencing `hasher` diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index f7d9ec1fae8e4..25057b4aeaa27 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,8 +1,8 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; 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; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -58,7 +58,7 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op if needs_ref_to_cmp(cx, ty) || ty.is_str() || ty.is_slice() - || matches!(get_type_diagnostic_name(cx, ty), Some(sym::OsStr | sym::OsString)) + || matches!(ty.opt_diag_name(cx), Some(sym::OsStr | sym::OsString)) { return Some((expr.span, ToAscii(is_lower, ty_raw))); } @@ -72,8 +72,8 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { ty.is_char() || *ty.kind() == ty::Uint(UintTy::U8) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::String) + || ty.is_diag_item(cx, sym::Vec) + || ty.is_lang_item(cx, LangItem::String) } impl LateLintPass<'_> for ManualIgnoreCaseCmp { diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 2eebb2430fd94..8c6abbeac4489 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; 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::res::MaybeResPath; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; +use clippy_utils::{higher, is_in_const_context, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { } fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { - let local_hid = path_to_local(arg)?; + let local_hid = arg.res_local_id()?; if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 2705ef20b795d..298bf10754897 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -2,16 +2,14 @@ use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; -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 clippy_utils::{is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -131,39 +129,25 @@ fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> b /// 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)); + let path = match pat.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::Expr(e) if let Some((qpath, id)) = e.opt_qpath() => (qpath, id), + _ => return false, + }; + let res = path.res(cx); matches!( res, Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) @@ -384,7 +368,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. - if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { + if !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) { has_disallowed = true; } }); diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs index f54ccf2c87b0f..e78f6affda2a3 100644 --- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs +++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs @@ -1,7 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{is_trait_method, peel_hir_expr_refs}; +use clippy_utils::peel_hir_expr_refs; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Mutability, QPath}; @@ -52,7 +53,7 @@ impl LateLintPass<'_> for ManualMainSeparatorStr { && path.ident.name == sym::to_string && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind && let Res::Def(DefKind::Const, receiver_def_id) = path.res - && is_trait_method(cx, target, sym::ToString) + && cx.ty_based_def(target).opt_parent(cx).is_diag_item(cx, sym::ToString) && cx.tcx.is_diagnostic_item(sym::path_main_separator, receiver_def_id) && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && ty.is_str() diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index b036e78cdedc5..63f6d89f2ad74 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -189,7 +190,7 @@ fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> b fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn), + ExprKind::Path(_) => expr.res(cx).is_diag_item(cx, sym::default_fn), ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir_body(cl.body).value), _ => false, } @@ -214,11 +215,11 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => false, }, ExprKind::Array([]) => true, - ExprKind::Call(def, []) => clippy_utils::is_path_diagnostic_item(cx, def, sym::default_fn), + ExprKind::Call(def, []) => def.res(cx).is_diag_item(cx, sym::default_fn), _ => false, } } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_path_diagnostic_item(cx, expr, sym::slice_from_ref) + expr.basic_res().is_diag_item(cx, sym::slice_from_ref) } diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index 1e91a429fe45c..d993ed48eac4a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_in_const_context, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -64,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 - && let Some(hir_id) = path_to_local(rem2_lhs) + && let Some(hir_id) = rem2_lhs.res_local_id() && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 7fb88763e640f..674f0da818f5f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -2,8 +2,8 @@ use clippy_config::Conf; use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind::Assign; @@ -189,7 +189,7 @@ fn check_to_owned( && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_chars, chars_expr_def_id) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() - && is_type_lang_item(cx, ty, hir::LangItem::String) + && ty.is_lang_item(cx, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind @@ -250,7 +250,7 @@ fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - let required = match get_type_diagnostic_name(cx, ty) { + let required = match ty.opt_diag_name(cx) { Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN, Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN, Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN, @@ -264,7 +264,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap)) + matches!(ty.opt_diag_name(cx), Some(sym::BTreeMap | sym::HashMap)) } fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) { diff --git a/src/tools/clippy/clippy_lints/src/manual_rotate.rs b/src/tools/clippy/clippy_lints/src/manual_rotate.rs index 22e3407303f07..e8db44698d9ce 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rotate.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rotate.rs @@ -56,20 +56,14 @@ impl Display for ShiftDirection { } } -fn parse_shift<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, -) -> Option<(ShiftDirection, u128, &'tcx Expr<'tcx>)> { +fn parse_shift<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(ShiftDirection, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let dir = match op.node { BinOpKind::Shl => ShiftDirection::Left, BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; - if let Constant::Int(shift) = const_expr { - return Some((dir, shift, l)); - } + return Some((dir, l, r)); } None } @@ -78,40 +72,62 @@ impl LateLintPass<'_> for ManualRotate { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Binary(op, l, r) = expr.kind && let BinOpKind::Add | BinOpKind::BitOr = op.node - && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l) - && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r) - { - if l_shift_dir == r_shift_dir { - return; - } - if !clippy_utils::eq_expr_value(cx, l_expr, r_expr) { - return; - } - let Some(bit_width) = (match cx.typeck_results().expr_ty(expr).kind() { + && let Some((l_shift_dir, l_expr, l_amount)) = parse_shift(l) + && let Some((r_shift_dir, r_expr, r_amount)) = parse_shift(r) + && l_shift_dir != r_shift_dir + && clippy_utils::eq_expr_value(cx, l_expr, r_expr) + && let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() { ty::Int(itype) => itype.bit_width(), ty::Uint(itype) => itype.bit_width(), _ => return, - }) else { - return; - }; - if l_amount + r_amount == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { + } + { + let const_eval = ConstEvalCtxt::new(cx); + + let ctxt = expr.span.ctxt(); + let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) = + const_eval.eval_local(l_amount, ctxt) + && let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt) + && l_amount_val + r_amount_val == u128::from(bit_width) + { + if l_amount_val < r_amount_val { (l_shift_dir, l_amount) } else { (r_shift_dir, r_amount) + } + } else { + let (amount1, binop, minuend, amount2, shift_direction) = match (l_amount.kind, r_amount.kind) { + (_, ExprKind::Binary(binop, minuend, other)) => (l_amount, binop, minuend, other, l_shift_dir), + (ExprKind::Binary(binop, minuend, other), _) => (r_amount, binop, minuend, other, r_shift_dir), + _ => return, }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); - } + + if let Some(Constant::Int(minuend)) = const_eval.eval_local(minuend, ctxt) + && clippy_utils::eq_expr_value(cx, amount1, amount2) + // (x << s) | (x >> bit_width - s) + && ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend) + // (x << s) | (x >> (bit_width - 1) ^ s) + || (binop.node == BinOpKind::BitXor && u128::from(bit_width).checked_sub(minuend) == Some(1))) + { + // NOTE: we take these from the side that _doesn't_ have the binop, since it's probably simpler + (shift_direction, amount1) + } else { + return; + } + }; + + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 39e5289c62ae4..681dc2ab5bc0c 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -205,9 +205,9 @@ fn lint_map_unit_fn( ) { let var_arg = &map_args.0; - let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) { + let (map_type, variant, lint) = if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Option) { ("Option", "Some", OPTION_MAP_UNIT_FN) - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) { + } else if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Result) { ("Result", "Ok", RESULT_MAP_UNIT_FN) } else { return; diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs index e0cb5d14d3c93..fb83f7cf65ddb 100644 --- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{higher, is_res_lang_ctor, sym}; +use clippy_utils::{higher, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,8 +57,8 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result.ok(, _) && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt && let_pat.span.ctxt() == ctxt diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index aaf559fc4439e..79f737f07eb1e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -1,18 +1,17 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; -use clippy_utils::{ - SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, - peel_ref_operators, -}; +use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; +use rustc_span::symbol::Ident; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; @@ -35,7 +34,7 @@ pub(super) fn check_if_let<'tcx>( check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, @@ -50,26 +49,28 @@ fn check_arm<'tcx>( if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) && let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { IfLetOrMatch::IfLet(scrutinee, pat, _, els, _) => Some((scrutinee, pat, els)), - IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) - // if there are more than two arms, collapsing would be non-trivial - // one of the arms must be "wild-like" - && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) - { - let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); - Some((scrutinee, then.pat, Some(els.body))) - } else { - None + IfLetOrMatch::Match(scrutinee, arms, ..) => { + if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) + // if there are more than two arms, collapsing would be non-trivial + // one of the arms must be "wild-like" + && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) + { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None + } }, } && outer_pat.span.eq_ctxt(inner_scrutinee.span) // match expression must be a local binding // match { .. } - && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) + && let Some(binding_id) = peel_ref_operators(cx, inner_scrutinee).res_local_id() && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } - && let (Some(binding_span), is_innermost_parent_pat_struct) - = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) + && let (Some((binding_ident, binding_span)), is_innermost_parent_pat_struct) = + find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal && match (outer_else_body, inner_else_body) { (None, None) => true, @@ -77,9 +78,7 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), } // the binding must not be used in the if guard - && outer_guard.is_none_or( - |e| !is_local_used(cx, e, binding_id) - ) + && outer_guard.is_none_or(|e| !is_local_used(cx, e, binding_id)) // ...or anywhere in the inner expression && match inner { IfLetOrMatch::IfLet(_, _, body, els, _) => { @@ -103,7 +102,7 @@ fn check_arm<'tcx>( // collapsing patterns need an explicit field name in struct pattern matching // ex: Struct {x: Some(1)} let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name")) + format!(", prefixed by `{binding_ident}: `") } else { String::new() }; @@ -135,21 +134,24 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), _ => false, } } -fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { - let mut span = None; +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option<(Ident, Span)>, bool) { + let mut binding = None; let mut is_innermost_parent_pat_struct = false; - pat.walk_short(|p| match &p.kind { + pat.walk_short(|p| match p.kind { // ignore OR patterns PatKind::Or(_) => false, - PatKind::Binding(_bm, _, _ident, _) => { + PatKind::Binding(_bm, _, ident, _) => { let found = p.hir_id == hir_id; if found { - span = Some(p.span); + binding = Some((ident, p.span)); } !found }, @@ -158,7 +160,7 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi true }, }); - (span, is_innermost_parent_pat_struct) + (binding, is_innermost_parent_pat_struct) } /// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`, diff --git a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs index 93d7683d2af81..be4b4f346dbcf 100644 --- a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { && args.len() == 1 && let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind && let body = peel_blocks(arms[0].body) - && path_to_local_id(body, arg) + && body.res_local_id() == Some(arg) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs index abf723fa6f4ca..d7224052ebc58 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; @@ -66,15 +65,15 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && let ExprKind::Call(callee, [arg]) = inner_expr.kind { return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) - && path_to_local_id(arg, target); + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + && arg.res_local_id() == Some(target); } false } fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { - return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + return inner_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone); } false } @@ -98,7 +97,7 @@ pub(super) fn check_match<'tcx>( expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, ty, sym::Option) + if ty.is_diag_item(cx, sym::Option) && let [first_arm, second_arm] = arms && first_arm.guard.is_none() && second_arm.guard.is_none() diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs index de57d1eee9249..f111da60bbd5b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs @@ -2,8 +2,7 @@ use super::MANUAL_MAP; use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_res_lang_ctor, path_res}; - +use clippy_utils::res::{MaybeDef, MaybeQPath}; use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; @@ -91,7 +90,7 @@ fn get_some_expr<'tcx>( // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + if ctxt == expr.span.ctxt() && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => { Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index a8490d6aa7d82..c9293412fba82 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; @@ -72,7 +73,10 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool true }, PatKind::TupleStruct(qpath, ..) => { - is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err + cx.qpath_res(&qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) + == must_match_err }, PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) @@ -103,7 +107,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &' /// Check if `expr` contains `Some(ident)`, possibly as a block fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind - && is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome) + && body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && cx.typeck_results().expr_ty(body_arg) == ty && let ExprKind::Path(QPath::Resolved( _, @@ -120,7 +124,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t /// Check if `expr` is `None`, possibly as a block fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } /// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index ac9e51890362b..e00d0c7f3d6c1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,4 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -10,8 +11,8 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, get_type_diagnostic_name, implements_trait}; -use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; +use clippy_utils::{is_default_equivalent, is_lint_allowed, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -31,7 +32,7 @@ fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { } } -fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { +fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>, allow_wildcard: bool) -> Option<&'tcx Expr<'tcx>> { if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.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 @@ -48,7 +49,9 @@ fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'t && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) { Some(arm.body) - } else if let PatKind::Wild = arm.pat.kind { + } else if let PatKind::Wild = arm.pat.kind + && allow_wildcard + { // We consider that the `Some` check will filter it out if it's not right. Some(arm.body) } else { @@ -62,11 +65,11 @@ fn get_some_and_none_bodies<'tcx>( arm2: &'tcx Arm<'tcx>, ) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { if let Some(binding_id) = get_some(cx, arm1.pat) - && let Some(body_none) = get_none(cx, arm2) + && let Some(body_none) = get_none(cx, arm2, true) { Some(((arm1.body, binding_id), body_none)) - } else if let Some(binding_id) = get_some(cx, arm2.pat) - && let Some(body_none) = get_none(cx, arm1) + } else if let Some(body_none) = get_none(cx, arm1, false) + && let Some(binding_id) = get_some(cx, arm2.pat) { Some(((arm2.body, binding_id), body_none)) } else { @@ -114,7 +117,8 @@ fn handle( && is_default_equivalent(cx, peel_blocks(body_none)) { // We now check if the condition is a None variant, in which case we need to specify the type - if path_res(cx, condition) + if condition + .res(cx) .opt_def_id() .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) { @@ -174,7 +178,7 @@ fn handle( } fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static str> { - match get_type_diagnostic_name(cx, ty)? { + match ty.opt_diag_name(cx)? { sym::Option => Some("Option"), sym::Result => Some("Result"), _ => None, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index d4bfdb7e440d8..235cb9e4ecce8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -1,11 +1,12 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; +use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, - path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks, + peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -33,8 +34,7 @@ where let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + if !(scrutinee_ty.is_diag_item(cx, sym::Option) && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Option)) { return None; } @@ -138,7 +138,7 @@ where { snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() } else { - if path_to_local_id(some_expr.expr, id) + if some_expr.expr.res_local_id() == Some(id) && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) && binding_ref.is_some() { @@ -190,7 +190,7 @@ pub struct SuggInfo<'a> { fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) + if arg.res_local_id() == Some(binding) && cx.typeck_results().expr_adjustments(arg).is_empty() && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { @@ -259,9 +259,19 @@ pub(super) fn try_parse_pattern<'tcx>( kind: PatExprKind::Path(qpath), hir_id, .. - }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None), + }) if cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + Some(OptionPat::None) + }, PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + if cx + .qpath_res(qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -273,5 +283,5 @@ pub(super) fn try_parse_pattern<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index 1cb4b512a30e5..156118be347f9 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_none_arm, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_none_arm, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -58,10 +59,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) + && cx + .qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionSome) && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind - && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) + && e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 5816da5695eb6..b5f631e8fea33 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -1,17 +1,18 @@ +//! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` + use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Attribute, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; use super::MATCH_LIKE_MATCHES_MACRO; -/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` pub(crate) fn check_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -20,16 +21,42 @@ pub(crate) fn check_if_let<'tcx>( then_expr: &'tcx Expr<'_>, else_expr: &'tcx Expr<'_>, ) { - find_matches_sugg( - cx, - let_expr, - IntoIterator::into_iter([ - (&[][..], Some(let_pat), then_expr, None), - (&[][..], None, else_expr, None), - ]), - expr, - true, - ); + if !span_contains_comment(cx.sess().source_map(), expr.span) + && cx.typeck_results().expr_ty(expr).is_bool() + && let Some(b0) = find_bool_lit(then_expr) + && let Some(b1) = find_bool_lit(else_expr) + && b0 != b1 + { + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, let_pat.hir_id) && is_some_wild(let_pat.kind) { + return; + } + + // The suggestion may be incorrect, because some arms can have `cfg` attributes + // evaluated into `false` and so such arms will be stripped before. + let mut applicability = Applicability::MaybeIncorrect; + let pat = snippet_with_applicability(cx, let_pat.span, "..", &mut applicability); + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = let_expr; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = let_expr.kind + && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() + { + ex_new = ex_inner; + } + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + "if let .. else expression looks like `matches!` macro", + "try", + format!( + "{}matches!({}, {pat})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + } } pub(super) fn check_match<'tcx>( @@ -38,55 +65,80 @@ pub(super) fn check_match<'tcx>( scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'tcx>], ) -> bool { - find_matches_sugg( - cx, - scrutinee, - arms.iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), - e, - false, - ) -} - -/// Lint a `match` or `if let` for replacement by `matches!` -fn find_matches_sugg<'a, 'b, I>( - cx: &LateContext<'_>, - ex: &Expr<'_>, - mut iter: I, - expr: &Expr<'_>, - is_if_let: bool, -) -> bool -where - 'b: 'a, - I: Clone - + DoubleEndedIterator - + ExactSizeIterator - + Iterator>, &'a Expr<'b>, Option<&'a Expr<'b>>)>, -{ - if !span_contains_comment(cx.sess().source_map(), expr.span) - && iter.len() >= 2 - && cx.typeck_results().expr_ty(expr).is_bool() - && let Some((_, last_pat_opt, last_expr, _)) = iter.next_back() - && let iter_without_last = iter.clone() - && let Some((first_attrs, _, first_expr, first_guard)) = iter.next() - && let Some(b0) = find_bool_lit(&first_expr.kind) - && let Some(b1) = find_bool_lit(&last_expr.kind) + if let Some((last_arm, arms_without_last)) = arms.split_last() + && let Some((first_arm, middle_arms)) = arms_without_last.split_first() + && !span_contains_comment(cx.sess().source_map(), e.span) + && cx.typeck_results().expr_ty(e).is_bool() + && let Some(b0) = find_bool_lit(first_arm.body) + && let Some(b1) = find_bool_lit(last_arm.body) && b0 != b1 - && (first_guard.is_none() || iter.len() == 0) - && first_attrs.is_empty() - && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty()) + // We handle two cases: + && ( + // - There are no middle arms, i.e., 2 arms in total + // + // In that case, the first arm may or may not have a guard, because this: + // ```rs + // match e { + // Either::Left $(if $guard)|+ => true, // or `false`, but then we'll need `!matches!(..)` + // _ => false, + // } + // ``` + // can always become this: + // ```rs + // matches!(e, Either::Left $(if $guard)|+) + // ``` + middle_arms.is_empty() + + // - (added in #6216) There are middle arms + // + // In that case, neither they nor the first arm may have guards + // -- otherwise, they couldn't be combined into an or-pattern in `matches!` + // + // This: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => true, + // _ /* matches `Either3::Third` */ => false, + // } + // ``` + // can become this: + // ```rs + // matches!(e, Either3::First | Either3::Second) + // ``` + // + // But this: + // ```rs + // match e { + // Either3::First if X => true, + // Either3::Second => true, + // _ => false, + // } + // ``` + // cannot be transformed. + // + // We set an additional constraint of all of them needing to return the same bool, + // so we don't lint things like: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => false, + // _ => false, + // } + // ``` + // This is not *strictly* necessary, but it simplifies the logic a bit + || arms_without_last.iter().all(|arm| { + cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) + }) + ) { - if let Some(last_pat) = last_pat_opt - && !is_wild(last_pat) - { + if !is_wild(last_arm.pat) { return false; } - for arm in iter_without_last.clone() { - if let Some(pat) = arm.1 - && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some(pat.kind) - { + for arm in arms_without_last { + let pat = arm.pat; + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some_wild(pat.kind) { return false; } } @@ -96,14 +148,12 @@ where let mut applicability = Applicability::MaybeIncorrect; let pat = { use itertools::Itertools as _; - iter_without_last - .filter_map(|arm| { - let pat_span = arm.1?.span; - Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) - }) + arms_without_last + .iter() + .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) .join(" | ") }; - let pat_and_guard = if let Some(g) = first_guard { + let pat_and_guard = if let Some(g) = first_arm.guard { format!( "{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability) @@ -113,8 +163,8 @@ where }; // strip potential borrows (#6503), but only if the type is a reference - let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind + let mut ex_new = scrutinee; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = scrutinee.kind && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; @@ -122,11 +172,8 @@ where span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, - expr.span, - format!( - "{} expression looks like `matches!` macro", - if is_if_let { "if let .. else" } else { "match" } - ), + e.span, + "match expression looks like `matches!` macro", "try", format!( "{}matches!({}, {pat_and_guard})", @@ -142,11 +189,11 @@ where } /// Extract a `bool` or `{ bool }` -fn find_bool_lit(ex: &ExprKind<'_>) -> Option { - match ex { +fn find_bool_lit(ex: &Expr<'_>) -> Option { + match ex.kind { ExprKind::Lit(Spanned { node: LitKind::Bool(b), .. - }) => Some(*b), + }) => Some(b), ExprKind::Block( rustc_hir::Block { stmts: [], @@ -168,8 +215,9 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option { } } -fn is_some(path_kind: PatKind<'_>) -> bool { - match path_kind { +/// Checks whether a pattern is `Some(_)` +fn is_some_wild(pat_kind: PatKind<'_>) -> bool { + match pat_kind { PatKind::TupleStruct(QPath::Resolved(_, path), [first, ..], _) if is_wild(first) => { let name = path.segments[0].ident; name.name == rustc_span::sym::Some diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 818e504245549..be914429edb44 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -61,8 +62,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| { let mut local_map: HirIdMap = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - if let Some(a_id) = path_to_local(a) - && let Some(b_id) = path_to_local(b) + if let Some(a_id) = a.res_local_id() + && let Some(b_id) = b.res_local_id() && let entry = match local_map.entry(a_id) { HirIdMapEntry::Vacant(entry) => entry, // check if using the same bindings as before diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index eb8b16e1561bd..3f8f2dc0e132d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -58,7 +58,7 @@ impl MatchExprVisitor<'_, '_> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); - if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { + if ty.is_lang_item(self.cx, LangItem::String) || ty.kind() == &ty::Str { return ControlFlow::Break(case_method); } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 70a03ff937625..ac5da320bdffb 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -16,8 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { ty::Adt(adt_def, _) - if adt_def.is_enum() - && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) => + if adt_def.is_enum() && !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) => { adt_def }, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index 8ce8453360f78..e38ba801c0bf7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::is_local_used; use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' } let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); - if is_type_diagnostic_item(cx, ex_ty, sym::Result) { + if ex_ty.is_diag_item(cx, sym::Result) { for arm in arms { if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::qpath_to_string(&cx.tcx, path); diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 3a2097c3df261..c9b6821ad98fd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -1,10 +1,10 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; +use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ - SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, - peel_blocks_with_stmt, + SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, over, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; @@ -104,8 +104,8 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool return false; } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); - if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { - return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) + if let_expr_ty.is_diag_item(cx, sym::Option) { + return else_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } return eq_expr_value(cx, if_let.let_expr, else_expr); diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 7c6d45e424006..d39e315cae1f2 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{is_in_const_context, path_to_local, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -164,7 +165,7 @@ fn get_pat_binding<'tcx>( guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>, ) -> Option { - if let Some(local) = path_to_local(guard_expr) + if let Some(local) = guard_expr.res_local_id() && !is_local_used(cx, outer_arm.body, local) { let mut span = None; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 9d0115791838c..2ca76a1fe6945 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,10 +1,11 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; 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::ty::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, sym}; +use clippy_utils::{higher, is_expn_of, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; @@ -216,7 +217,7 @@ fn find_method_sugg_for_if_let<'tcx>( if keyword == "while" && let ExprKind::MethodCall(method_path, _, [], _) = let_expr.kind && method_path.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } @@ -460,7 +461,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte Item::Diag(expected_ty, expected_variant) => { let ty = cx.typeck_results().pat_ty(pat); - if is_type_diagnostic_item(cx, ty, expected_ty) { + if ty.is_diag_item(cx, expected_ty) { let variant = ty .ty_adt_def() .expect("struct pattern type is not an ADT") diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 02f87512966ba..44c4d7a31ff31 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -224,7 +224,7 @@ enum PatState<'a> { /// A std enum we know won't be extended. Tracks the states of each variant separately. /// /// This is not used for `Option` since it uses the current pattern to track its state. - StdEnum(&'a mut [PatState<'a>]), + StdEnum(&'a mut [Self]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs index af90cb5e67334..7358628f0f7e6 100644 --- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_expr; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && let ExprKind::Path(ref match_fun_path) = match_fun.kind && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind - && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) + && err_fun.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 95ecef5987007..ac3cbaec55f30 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -1,12 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{ - is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core, -}; +use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -129,7 +128,7 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -163,7 +162,7 @@ fn check_replace_option_with_some( msrv: Msrv, ) -> bool { if let ExprKind::Call(src_func, [src_arg]) = src.kind - && is_res_lang_ctor(cx, path_res(cx, src_func), OptionSome) + && src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && msrv.meets(cx, msrvs::OPTION_REPLACE) { // We do not have to check for a `const` context here, because `core::mem::replace()` and diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs index 0498f317442ac..77ac181ee633a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; +use clippy_utils::{peel_blocks, peel_ref_operators, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -23,10 +23,14 @@ pub(super) fn check<'tcx>( && let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind && let ExprKind::Binary(ref op, l, r) = body.value.kind && op.node == BinOpKind::Eq - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv).peel_refs(), sym::SliceIter) + && cx + .typeck_results() + .expr_ty(filter_recv) + .peel_refs() + .is_diag_item(cx, sym::SliceIter) && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); - path_to_local_id(expr, arg_id) + expr.res_local_id() == Some(arg_id) }) && let needle = if operand_is_arg(l) { r diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs index b8cc5ddd845c9..baea49296cd7a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( && let Some(impl_id) = cx.tcx.impl_of_assoc(bytes_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_str() && let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, hir::LangItem::String)) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index 02fc09170e59c..40d521d61c118 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if ty.is_str() { "str" - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { "String" } else { return; diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 6f9702f6c6c33..b4b10e972f6d4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; 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; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>( || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit())) && !ext_str.chars().skip(1).all(|c| c.is_ascii_digit()) && let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs() - && (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String)) + && (recv_ty.is_str() || recv_ty.is_lang_item(cx, LangItem::String)) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs index de27a45ba4d92..4b0fc7eba8e34 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{method_chain_args, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; @@ -17,7 +18,7 @@ pub(super) fn check( ) -> bool { if let Some(args) = method_chain_args(info.chain, chain_methods) && let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind - && let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = fun.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs index 6e24cabca8bba..67def8767bb0a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_range_full; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; @@ -29,9 +29,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span fn match_acceptable_type(cx: &LateContext<'_>, expr: &Expr<'_>, types: &[rustc_span::Symbol]) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); - types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty)) + types.iter().any(|&ty| expr_ty.is_diag_item(cx, ty)) // String type is a lang item but not a diagnostic item for now so we need a separate check - || is_type_lang_item(cx, expr_ty, LangItem::String) + || expr_ty.is_lang_item(cx, LangItem::String) } fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index 0a456d1057ad9..2a0ae14a4b088 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -4,27 +4,14 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::{self}; -use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -#[allow(clippy::too_many_lines)] -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - method_name: Symbol, - receiver: &Expr<'_>, - args: &[Expr<'_>], -) { - let arg = if method_name == sym::clone && args.is_empty() { - receiver - } else { - return; - }; +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { if cx .typeck_results() .type_dependent_def_id(expr.hir_id) @@ -34,10 +21,10 @@ pub(super) fn check( { return; } - let arg_adjustments = cx.typeck_results().expr_adjustments(arg); + let arg_adjustments = cx.typeck_results().expr_adjustments(receiver); let arg_ty = arg_adjustments .last() - .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); + .map_or_else(|| cx.typeck_results().expr_ty(receiver), |a| a.target); let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() @@ -76,7 +63,7 @@ pub(super) fn check( }; let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; + let snip = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "_", &mut app).0; let deref_count = arg_adjustments .iter() diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs index 65583c6a9811e..238e1fe988b39 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -3,21 +3,12 @@ use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; -use rustc_span::symbol::{Symbol, sym}; +use rustc_middle::ty::{self, IsSuggestable}; +use rustc_span::symbol::sym; use super::CLONE_ON_REF_PTR; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], -) { - if !(args.is_empty() && method_name == sym::clone) { - return; - } +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if let ty::Adt(adt, subst) = obj_ty.kind() @@ -39,12 +30,22 @@ pub(super) fn check( // Sometimes unnecessary ::<_> after Rc/Arc/Weak let mut app = Applicability::Unspecified; let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; - diag.span_suggestion( - expr.span, - "try", - format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), - app, - ); + let generic = subst.type_at(0); + if generic.is_suggestable(cx.tcx, true) { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::<{generic}>::clone(&{snippet})"), + app, + ); + } else { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::::clone(&{snippet})"), + Applicability::HasPlaceholders, + ); + } }, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs index f50fb627b89a0..d80d6f7810f53 100644 --- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -19,7 +19,9 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(cx, msrvs::ITERATOR_COPIED) => { + _ if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && msrv.meets(cx, msrvs::ITERATOR_COPIED) => + { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs index 578865c329180..8ba8264b713c4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; -use clippy_utils::{is_mutable, is_trait_method, path_to_local_with_projections, sym}; +use clippy_utils::{is_mutable, path_to_local_with_projections, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp let typeck = cx.typeck_results(); // if the "last" method is that of Iterator - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // if self implements DoubleEndedIterator && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) && let self_type = cx.typeck_results().expr_ty(self_expr) diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index cbf713a3b17c4..9b0c290577405 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -1,7 +1,7 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_range_full, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; @@ -35,8 +35,8 @@ fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_> /// Checks `std::string::String` fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - is_type_lang_item(cx, expr, LangItem::String) - && is_type_lang_item(cx, recv, LangItem::String) + expr.is_lang_item(cx, LangItem::String) + && recv.is_lang_item(cx, LangItem::String) && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) } diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs index 91ddaca07d8bb..6e9aebcf18ae4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -1,7 +1,8 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -16,7 +17,7 @@ pub(super) fn check( err_span: Span, msrv: Msrv, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // Grabs the `Result` type && let result_type = cx.typeck_results().expr_ty(recv) // Tests if the T type in a `Result` is not None @@ -40,7 +41,7 @@ pub(super) fn check( /// Given a `Result` type, return its data (`T`). fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().next(), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 818e26f8aa1df..288f966991acb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::Span; use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -20,20 +20,15 @@ pub(super) fn check<'tcx>( format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, - name: Symbol, receiver: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], + arg: &'tcx hir::Expr<'tcx>, ) { - if name == sym::expect - && let [arg] = args - && let arg_root = get_arg_root(cx, arg) - && contains_call(cx, arg_root) - && !contains_return(arg_root) - { + let arg_root = get_arg_root(cx, arg); + if contains_call(cx, arg_root) && !contains_return(arg_root) { let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::Option) { + let closure_args = if receiver_type.is_diag_item(cx, sym::Option) { "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym::Result) { + } else if receiver_type.is_diag_item(cx, sym::Result) { "|_|" } else { return; @@ -54,7 +49,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, @@ -69,7 +64,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!(\"{{}}\", {arg_root_snippet}))"), applicability, @@ -88,7 +83,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && { let arg_type = cx.typeck_results().expr_ty(receiver); let base_type = arg_type.peel_refs(); - base_type.is_str() || is_type_lang_item(cx, base_type, hir::LangItem::String) + base_type.is_str() || base_type.is_lang_item(cx, hir::LangItem::String) } { receiver } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs index db60061904f6f..829c1226a859e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::EXTEND_WITH_DRAIN; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) //check source object && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind && src_method.ident.name == sym::drain @@ -18,10 +18,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: //check if actual src type is mutable for code suggestion && let immutable = src_ty.is_mutable_ptr() && let src_ty = src_ty.peel_refs() - && is_type_diagnostic_item(cx, src_ty, sym::Vec) + && src_ty.is_diag_item(cx, sym::Vec) //check drain range && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() - && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) + && src_ty_range.is_lang_item(cx, LangItem::RangeFull) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs index 35008c39c084d..a964cbf657b4d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::FILETYPE_IS_FILE; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if !is_type_diagnostic_item(cx, ty, sym::FileType) { + if !ty.is_diag_item(cx, sym::FileType) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 2da0f8341b17b..26b19848fe1b6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -1,8 +1,8 @@ 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::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; 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, sym}; +use clippy_utils::{SpanlessEq, higher, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -134,16 +134,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => map_arg, } // .map(|y| y[.acceptable_method()].unwrap()) - && let simple_equal = (path_to_local_id(receiver, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id)) + && let simple_equal = (receiver.res_local_id() == Some(filter_param_id) + && map_arg_peeled.res_local_id() == Some(map_param_id)) && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { // in `filter(|x| ..)`, replace `*x` with `x` let a_path = if !is_filter_param_ref && let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind { expr_path } else { a }; // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) + a_path.res_local_id() == Some(filter_param_id) + && b.res_local_id() == Some(map_param_id) && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) }) && (simple_equal @@ -166,7 +166,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { let expr_uses_local = |pat: &Pat<'_>, expr: &Expr<'_>| { if let PatKind::TupleStruct(QPath::Resolved(_, path), [subpat], _) = pat.kind && let PatKind::Binding(_, local_id, ident, _) = subpat.kind - && path_to_local_id(expr.peel_blocks(), local_id) + && expr.peel_blocks().res_local_id() == Some(local_id) && let Some(local_variant_def_id) = path.res.opt_def_id() && local_variant_def_id == variant_def_id { @@ -204,7 +204,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => return None, }; - if path_to_local_id(scrutinee, map_param_id) + if scrutinee.res_local_id() == Some(map_param_id) // else branch should be a `panic!` or `unreachable!` macro call && let Some(mac) = root_macro_call(else_.peel_blocks().span) && (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable)) @@ -247,7 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind - && path_to_local_id(scrutinee, filter_param_id) + && scrutinee.res_local_id() == Some(filter_param_id) && let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind && let Some(variant_def_id) = path.res.opt_def_id() { @@ -266,8 +266,8 @@ fn is_filter_some_map_unwrap( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> bool { - let iterator = is_trait_method(cx, expr, sym::Iterator); - let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); + let option = cx.typeck_results().expr_ty(filter_recv).is_diag_item(cx, sym::Option); (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) } @@ -275,12 +275,12 @@ fn is_filter_some_map_unwrap( /// is `filter(|x| x.is_ok()).map(|x| x.unwrap())` fn is_filter_ok_map_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { // result has no filter, so we only check for iterators - let iterator = is_trait_method(cx, expr, sym::Iterator); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); iterator && is_ok_filter_map(cx, filter_arg, map_arg) } /// lint use of `filter().map()` or `find().map()` for `Iterators` -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, @@ -398,7 +398,7 @@ fn is_find_or_filter<'a>( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(Ident, CheckResult<'a>)> { - if is_trait_method(cx, map_recv, sym::Iterator) + if cx.ty_based_def(map_recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) // filter(|x| ...is_some())... && let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind && let filter_body = cx.tcx.hir_body(filter_body_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index 94944bd9445b8..b803d1a3309da 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,10 +1,9 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::ty::is_copy; -use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, -}; +use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,7 +15,7 @@ use rustc_span::{Span, sym}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { if !expr.span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Closure(closure) = arg.kind && let body = cx.tcx.hir_body(closure.body) && let value = peel_blocks(body.value) diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs index b04d761d4860e..c6e30710eef90 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind; @@ -19,7 +20,7 @@ fn is_identity(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(applicability) = is_identity(cx, filter_map_arg) { // check if the iterator is from an empty array, see issue #12653 diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs index 9f3c346042ff9..698025d58e543 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if !msrv.meets(cx, msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs index 0c2ecfbc8ffdf..043adb8434a0d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::is_expr_untyped_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +15,9 @@ pub(super) fn check<'tcx>( flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { - if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, flat_map_arg) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && is_expr_untyped_identity_function(cx, flat_map_arg) + { span_lint_and_sugg( cx, FLAT_MAP_IDENTITY, diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs index 3242dcadb7013..fb0e8f9d91b5a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs +++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,10 +7,9 @@ use rustc_middle::ty; use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; -use clippy_utils::ty::is_type_diagnostic_item; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); @@ -19,7 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), _ => return, }; - if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::Option) { + if !sig.output().skip_binder().is_diag_item(cx, sym::Option) { return; } span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/format_collect.rs b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs index 1b28596d50da6..7b2fe00d32188 100644 --- a/src/tools/clippy/clippy_lints/src/methods/format_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs @@ -1,7 +1,7 @@ use super::FORMAT_COLLECT; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_format_macro, root_macro_call_first_node}; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -17,7 +17,7 @@ fn peel_non_expn_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx> } pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) { - if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + if cx.typeck_results().expr_ty(expr).is_lang_item(cx, LangItem::String) && let ExprKind::Closure(closure) = map_arg.kind && let body = cx.tcx.hir_body(closure.body) && let Some(value) = peel_non_expn_blocks(body.value) diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs index d664eaaac704f..11671f377e668 100644 --- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,9 +1,10 @@ use std::fmt::Write as _; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_ast::join_path_idents; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -15,7 +16,7 @@ use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>], func: &Expr<'_>) { - if is_path_diagnostic_item(cx, func, sym::from_iter_fn) + if func.res(cx).is_diag_item(cx, sym::from_iter_fn) && 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, &[]) diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs index 2e1d71ce284d6..88e9f2fdaee3e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_first.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( format!("{slice_name}.first()"), app, ); - } else if is_type_diagnostic_item(cx, identity, sym::VecDeque) { + } else if identity.is_diag_item(cx, sym::VecDeque) { let mut app = Applicability::MachineApplicable; let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs index 9daad1a8a949e..d273558a7006e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs @@ -2,7 +2,6 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -22,16 +21,17 @@ pub(super) fn check<'tcx>( let expr_ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) { - "Vec" - } else if is_type_diagnostic_item(cx, expr_ty, sym::VecDeque) { - "VecDeque" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::HashMap) { - "HashMap" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::BTreeMap) { - "BTreeMap" } else { - return; // caller is not a type that we want to lint + match expr_ty + .ty_adt_def() + .and_then(|def| cx.tcx.get_diagnostic_name(def.did())) + { + Some(sym::Vec) => "Vec", + Some(sym::VecDeque) => "VecDeque", + Some(sym::HashMap) if !is_mut => "HashMap", + Some(sym::BTreeMap) if !is_mut => "BTreeMap", + _ => return, // caller is not a type that we want to lint + } }; let mut span = expr.span; diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 0ba84919395cc..57c0ba25ebbf0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,12 +11,12 @@ use rustc_span::Symbol; use super::IMPLICIT_CLONE; pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_clone_like(cx, method_name, method_def_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) + && is_clone_like(cx, method_name, method_parent_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) - && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) + && !(ref_count > 0 && method_parent_id.is_diag_item(cx, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() @@ -40,19 +41,15 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re } /// Returns true if the named method can be used to clone the receiver. -pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { +pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: hir::def_id::DefId) -> bool { match method_name { - sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), - sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), - sym::to_string => is_diag_trait_item(cx, method_def_id, sym::ToString), - sym::to_vec => cx - .tcx - .impl_of_assoc(method_def_id) - .filter(|&impl_did| { - cx.tcx.type_of(impl_did).instantiate_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() - }) - .is_some(), + sym::to_os_string => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::OsStr), + sym::to_owned => method_parent_id.is_diag_item(cx, sym::ToOwned), + sym::to_path_buf => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Path), + sym::to_string => method_parent_id.is_diag_item(cx, sym::ToString), + sym::to_vec => method_parent_id + .opt_impl_ty(cx) + .is_some_and(|ty| ty.instantiate_identity().is_slice()), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs index ab21515f47f78..7a49a5f31f035 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,27 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::sym; use super::INEFFICIENT_TO_STRING; -/// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], - msrv: Msrv, -) { - if args.is_empty() - && method_name == sym::to_string - && let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, msrv: Msrv) { + if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did) && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) @@ -61,7 +52,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_lang_item(cx, ty, hir::LangItem::String) { + if ty.is_lang_item(cx, hir::LangItem::String) { return true; } diff --git a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs index 3706f3b670ba1..194ddf45e6f11 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -8,7 +8,7 @@ use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs index bedeb63367d06..c4b116af48713 100644 --- a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::has_iter_method; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,17 +10,10 @@ use rustc_span::symbol::{Symbol, sym}; use super::INTO_ITER_ON_REF; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_span: Span, - method_name: Symbol, - receiver: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, receiver: &hir::Expr<'_>) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); if let ty::Ref(..) = self_ty.kind() - && method_name == sym::into_iter - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs index 9276261606e1e..b081e804859a5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs +++ b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{expr_or_init, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args && !expr.span.from_expansion() && !error_kind.span.from_expansion() && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind - && is_path_diagnostic_item(cx, path, sym::io_error_new) + && path.ty_rel_def(cx).is_diag_item(cx, sym::io_error_new) && let ExprKind::Path(QPath::Resolved(_, init_path)) = &expr_or_init(cx, error_kind).kind && let [.., error_kind_ty, error_kind_variant] = init_path.segments && cx.tcx.is_diagnostic_item(sym::io_errorkind, error_kind_ty.res.def_id()) diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index 545bef1a4c5bc..add01b6a08376 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{is_assert_macro, root_macro_call}; -use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context}; use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_span::sym; @@ -45,7 +46,8 @@ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization /// value depends on a `#[cfg(…)]` directive. fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { - while let Some(init) = path_to_local(expr) + while let Some(init) = expr + .res_local_id() .and_then(|id| find_binding_init(cx, id)) .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) .filter(|init| !is_under_cfg(cx, init.hir_id)) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs index b4ab313fe98d1..b1a0b658a84ce 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,6 +1,7 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::{get_iterator_item_ty, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::get_iterator_item_ty; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, ) { let expr_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, expr_ty, sym::Vec) + if expr_ty.is_diag_item(cx, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) && let ty::Adt(_, args) = expr_ty.kind() && let Some(iter_item_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs index 6b64cc8b50ae7..ea2508cd7f382 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -13,21 +13,21 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, ty, sym::Vec) { + } else if ty.is_diag_item(cx, sym::Vec) { "Vec" - } else if is_type_diagnostic_item(cx, ty, sym::VecDeque) { + } else if ty.is_diag_item(cx, sym::VecDeque) { "VecDeque" - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) { + } else if ty.is_diag_item(cx, sym::HashSet) { "HashSet" - } else if is_type_diagnostic_item(cx, ty, sym::HashMap) { + } else if ty.is_diag_item(cx, sym::HashMap) { "HashMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + } else if ty.is_diag_item(cx, sym::BTreeMap) { "BTreeMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeSet) { + } else if ty.is_diag_item(cx, sym::BTreeSet) { "BTreeSet" - } else if is_type_diagnostic_item(cx, ty, sym::LinkedList) { + } else if ty.is_diag_item(cx, sym::LinkedList) { "LinkedList" - } else if is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { + } else if ty.is_diag_item(cx, sym::BinaryHeap) { "BinaryHeap" } else { return; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index adeff375c8aad..8d95b70c6a4bc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::get_iterator_item_ty; use hir::ExprKind; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ 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, sym}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; @@ -107,7 +108,7 @@ fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if let Some(expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(path, _, [_], _) = expr.kind && path.ident.name == sym::map - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return true; } @@ -141,7 +142,7 @@ fn expression_type( filter_arg: &hir::Expr<'_>, filter_span: Span, ) -> Option { - if !is_trait_method(cx, expr, sym::Iterator) + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) || parent_is_map(cx, expr) || span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index cbb1b450e60fc..2d6bc36dc5359 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -1,8 +1,8 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( _ => return, } && 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)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs index fd4650e1e45f6..01f35ff02d44a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, higher}; use rustc_ast::ast; use rustc_errors::Applicability; @@ -75,6 +75,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal } fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) + cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs index 1fdbd81bf240e..6d1ee32e50269 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( iter_span: Span, nth_span: Span, ) -> bool { - let caller_type = match get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(iter_recv).peel_refs()) { + let caller_type = match cx.typeck_results().expr_ty(iter_recv).peel_refs().opt_diag_name(cx) { Some(sym::Vec) => "`Vec`", Some(sym::VecDeque) => "`VecDeque`", _ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice", diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs index 0f8abd0172423..e0107b75e4142 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_item_or_ctor; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; use hir::{LangItem, OwnerNode}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -13,7 +14,7 @@ use super::ITER_NTH_ZERO; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 83e565562af08..8183c30f8c56b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,9 +1,10 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -67,8 +68,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), - ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, - ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg), + ExprKind::Path(ref p) + if cx + .qpath_res(p, recv.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + None + }, + ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs index fa8f9d640ee6e..8cff3bd815a1b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::expr_or_init; use clippy_utils::higher::VecArgs; -use clippy_utils::{expr_or_init, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -58,7 +59,7 @@ fn check<'tcx>( message: &'static str, note: &'static str, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(len) = get_iterator_length(cx, recv) && let Some(skipped) = expr_as_u128(cx, arg) && skipped > len diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs index fedb7c22eded6..661188c90ed61 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, path_to_local}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BindingMode, Node, PatKind}; @@ -11,20 +11,20 @@ use super::ITER_SKIP_NEXT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let mut application = Applicability::MachineApplicable; + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_then( cx, ITER_SKIP_NEXT, expr.span.trim_start(recv.span).unwrap(), "called `skip(..).next()` on an iterator", |diag| { - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, _, _, _) = pat.kind && ann != BindingMode::MUT { - application = Applicability::Unspecified; + applicability = Applicability::Unspecified; diag.span_help( pat.span, format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), @@ -35,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "use `nth` instead", format!(".nth({})", snippet(cx, arg.span, "..")), - application, + applicability, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs index 663e34437a307..cae31e7c9606e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_from_proc_macro, is_trait_method}; +use clippy_utils::is_from_proc_macro; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(arg) = ConstEvalCtxt::new(cx) .eval_local(arg_expr, expr.span.ctxt()) .and_then(|constant| { diff --git a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs index 90d5d9df55eed..11dde2429adfa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +8,7 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs index 2ad070793cbb2..e84b7452c7584 100644 --- a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::expr_or_init; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use super::JOIN_ABSOLUTE_PATHS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, expr_span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if (is_type_diagnostic_item(cx, ty, sym::Path) || is_type_diagnostic_item(cx, ty, sym::PathBuf)) + if (ty.is_diag_item(cx, sym::Path) || ty.is_diag_item(cx, sym::PathBuf)) && let ExprKind::Lit(spanned) = expr_or_init(cx, join_arg).kind && let LitKind::Str(symbol, _) = spanned.node && let sym_str = symbol.as_str() diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index bc96815944d5f..1a5b180b0c86f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -18,9 +19,10 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = arg.kind && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() - && let Some(fn_id) = typeck.type_dependent_def_id(expr.hir_id) - && (is_diag_trait_item(cx, fn_id, sym::Iterator) - || ((is_diag_item_method(cx, fn_id, sym::Option) || is_diag_item_method(cx, fn_id, sym::Result)) + && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) + && (fn_def.opt_parent(cx).is_diag_item(cx, sym::Iterator) + || ((fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) + || fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params @@ -29,7 +31,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && let ExprKind::Block(block, _) = body.value.kind && let Some(final_expr) = block.expr && !block.stmts.is_empty() - && path_to_local_id(final_expr, arg_id) + && final_expr.res_local_id() == Some(arg_id) && typeck.expr_adjustments(final_expr).is_empty() { let mut requires_copy = false; @@ -46,7 +48,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { - if path_to_local_id(e, arg_id) { + if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (_, false) | (UseKind::Deref | UseKind::Return(..), true) => { @@ -64,7 +66,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: }); } else if matches!(e.kind, ExprKind::Ret(_)) { ret_count += 1; - } else if path_to_local_id(e, arg_id) { + } else if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (UseKind::Return(..), false) => { diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs index 93325ca488e4e..8f65858987b9c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -30,8 +30,8 @@ pub(super) fn check( } // 2. the caller of `map()` is neither `Option` nor `Result` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Result); if !is_option && !is_result { return; } @@ -208,7 +208,7 @@ pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did()) && args.type_at(0).is_bool() && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol()) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, flavor.symbol()) && let Ok(map_func) = MapFunc::try_from(map_expr) { return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv); diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs b/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs index 9a03559b22377..b064f978588c4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( .tcx .get_diagnostic_item(sym::DoubleEndedIterator) .is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])) - && is_trait_method(cx, rev_call, sym::Iterator) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(rev_call).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index 077957fa44dc8..a8e30e44488cc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -19,9 +18,13 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind - && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) + && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) && let Some(recv_snippet) = recv.span.get_source_text(cx) && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) @@ -42,14 +45,21 @@ pub(super) fn check<'tcx>( fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { match map_expr.kind { - ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true, + ExprKind::Path(ref qpath) + if cx + .qpath_res(qpath, map_expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) => + { + true + }, ExprKind::Closure(closure) => { let body = cx.tcx.hir_body(closure.body); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind && let ExprKind::Call(callee, [ok_arg]) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, callee), ResultOk) + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, ResultOk) { - path_to_local_id(ok_arg, param_id) + ok_arg.res_local_id() == Some(param_id) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs b/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs index 83b57cca17bf1..1a7628ed43a4f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; -use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core}; +use clippy_utils::{expr_use_ctxt, fn_def_id, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind && let Some(def_id) = fn_def_id(cx, repeat_expr) && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs index c785b23bc9c4a..2196ce92b0ab5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_res, sym}; +use clippy_utils::sym; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -83,7 +84,7 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { // `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) + && matches!(base.basic_res(), Res::PrimTy(_)) { match seg.ident.name { sym::MAX => return Some(MinMax::Max), diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs index a811dd1cee18f..4fe14f8053c94 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -35,14 +34,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } } else { let ty = cx.typeck_results().expr_ty(e); - if is_type_lang_item(cx, ty, LangItem::String) - || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) - || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) + if ty.is_lang_item(cx, LangItem::String) + || (ty.is_lang_item(cx, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) + || (ty.is_diag_item(cx, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String) + (ty.is_str() || ty.is_lang_item(cx, LangItem::String)).then_some(RepeatKind::String) } } } @@ -55,8 +54,11 @@ pub(super) fn check( take_arg: &Expr<'_>, ) { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind - && is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat) - && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) + && repeat_fn.basic_res().is_diag_item(cx, sym::iter_repeat) + && cx + .typeck_results() + .expr_ty(collect_expr) + .is_lang_item(cx, LangItem::String) && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && cx.tcx.trait_of_assoc(take_id) == Some(iter_trait_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs index 23dba47f60f40..f2e127bedde56 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; @@ -20,7 +21,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !fold_span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let init_ty = cx.typeck_results().expr_ty(init) && let Some(try_trait) = cx.tcx.lang_items().try_trait() && implements_trait(cx, init_ty, try_trait, &[]) diff --git a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs index ac11baa2d54ca..ad950f75f8130 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_expr_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use rustc_span::{Span, sym}; use super::MAP_ALL_ANY_IDENTITY; -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, @@ -19,8 +20,8 @@ pub(super) fn check( any_arg: &Expr<'_>, method: &str, ) { - if is_trait_method(cx, expr, sym::Iterator) - && is_trait_method(cx, recv, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) && let Some(map_arg) = map_arg.span.get_source_text(cx) diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index 748be9bfcc626..1bc29c9c1dd1c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function}; -use clippy_utils::{is_diag_trait_item, peel_blocks}; +use clippy_utils::ty::{is_copy, should_call_clone_as_function}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; @@ -18,14 +19,13 @@ use super::MAP_CLONE; // If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't // run this lint because it would overlap with `useless_asref` which provides a better suggestion // in this case. -fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool { - if is_diag_trait_item(cx, method_id, sym::Iterator) { +fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_parent_id: DefId) -> bool { + if method_parent_id.is_diag_item(cx, sym::Iterator) { return true; } // We check if it's an `Option` or a `Result`. - if let Some(id) = cx.tcx.impl_of_assoc(method_id) { - let identity = cx.tcx.type_of(id).instantiate_identity(); - if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) { + if let Some(ty) = method_parent_id.opt_impl_ty(cx) { + if !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result) { return false; } } else { @@ -42,8 +42,8 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> } pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: Msrv) { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && should_run_lint(cx, e, method_id) + if let Some(parent_id) = cx.typeck_results().type_dependent_def_id(e.hir_id).opt_parent(cx) + && should_run_lint(cx, e, parent_id) { match arg.kind { hir::ExprKind::Closure(&hir::Closure { body, .. }) => { diff --git a/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs index e944eac91e7ae..1112fbc2a1c74 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,7 +12,7 @@ use super::MAP_COLLECT_RESULT_UNIT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result) + if collect_ret_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = collect_ret_ty.kind() && let Some(result_t) = args.types().next() && result_t.is_unit() diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs index 41beda9c5cb4a..f7da24bed2b80 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,11 @@ use super::MAP_ERR_IGNORE; pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Result) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { capture_clause: CaptureBy::Ref, body, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index 750f933330a25..e4ae14b6cf59c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_trait_method, span_contains_comment}; +use clippy_utils::span_contains_comment; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ fn try_get_caller_ty_name_and_method_name( caller_expr: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(&'static str, &'static str)> { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if is_map_to_option(cx, map_arg) { // `(...).map(...)` has type `impl Iterator> Some(("Iterator", "filter_map")) @@ -69,7 +69,7 @@ fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { _ => map_closure_ty.fn_sig(cx.tcx), }; let map_closure_return_ty = cx.tcx.instantiate_bound_regions_with_erased(map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) + map_closure_return_ty.is_diag_item(cx, sym::Option) }, _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 6190c43578e9b..fa394526bdf1e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; +use clippy_utils::ty::is_copy; +use clippy_utils::{is_expr_untyped_identity_function, is_mutable, path_to_local_with_projections}; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind, Node, PatKind}; use rustc_lint::{LateContext, LintContext}; @@ -21,9 +22,9 @@ pub(super) fn check( ) { let caller_ty = cx.typeck_results().expr_ty(caller); - if (is_trait_method(cx, expr, sym::Iterator) - || is_type_diagnostic_item(cx, caller_ty, sym::Result) - || is_type_diagnostic_item(cx, caller_ty, sym::Option)) + if (cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + || caller_ty.is_diag_item(cx, sym::Result) + || caller_ty.is_diag_item(cx, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs index df5a0de3392b0..62bdc4a3e4111 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` or a `Result`. - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) { return false; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index b0b4e5eedb084..c9066be51c44b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -133,6 +133,7 @@ mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; mod unnecessary_map_or; mod unnecessary_min_or_max; +mod unnecessary_option_map_or_else; mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; @@ -151,7 +152,8 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{contains_return, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; @@ -4639,6 +4641,30 @@ declare_clippy_lint! { "detects redundant calls to `Iterator::cloned`" } +declare_clippy_lint! { + /// Checks for usage of `.map_or_else()` "map closure" for `Option` type. + /// + /// ### Why is this bad? + /// This can be written more concisely by using `unwrap_or_else()`. + /// + /// ### Example + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.map_or_else(|| 2 * k, |n| n); + /// ``` + /// Use instead: + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.unwrap_or_else(|| 2 * k); + /// ``` + #[clippy::version = "1.88.0"] + pub UNNECESSARY_OPTION_MAP_OR_ELSE, + suspicious, + "making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4820,6 +4846,7 @@ impl_lint_pass!(Methods => [ SWAP_WITH_TEMPORARY, IP_CONSTANT, REDUNDANT_ITER_CLONED, + UNNECESSARY_OPTION_MAP_OR_ELSE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4842,8 +4869,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - self.check_methods(cx, expr); - match expr.kind { ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, func); @@ -4854,24 +4879,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { swap_with_temporary::check(cx, expr, func, args); ip_constant::check(cx, expr, func, args); }, - ExprKind::MethodCall(method_call, receiver, args, _) => { - let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); - expect_fun_call::check( - cx, - &self.format_args, - expr, - method_span, - method_call.ident.name, - receiver, - args, - ); - clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); - clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); - single_char_add_str::check(cx, expr, receiver, args); - into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + ExprKind::MethodCall(..) => { + self.check_methods(cx, expr); }, ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -4886,7 +4895,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; @@ -4958,7 +4966,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } impl Methods { - #[allow(clippy::too_many_lines)] + #[expect(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) { @@ -5044,7 +5052,7 @@ impl Methods { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); option_as_ref_cloned::check(cx, recv, span); }, - (sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => { + (sym::collect, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => { @@ -5065,17 +5073,26 @@ impl Methods { _ => {}, } }, - (sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some((sym::cloned, recv2, [], _, _)) => { - iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false); - }, - Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { - iter_count::check(cx, expr, recv2, name2); - }, - Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), - Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), - Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), - _ => {}, + (sym::count, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { + match method_call(recv) { + Some((sym::cloned, recv2, [], _, _)) => { + iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::RmCloned, + false, + ); + }, + Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { + iter_count::check(cx, expr, recv2, name2); + }, + Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + _ => {}, + } }, (sym::min | sym::max, [arg]) => { unnecessary_min_or_max::check(cx, expr, name, recv, arg); @@ -5294,6 +5311,7 @@ impl Methods { }, (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); + unnecessary_option_map_or_else::check(cx, expr, recv, def, map); unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, (sym::next, []) => { @@ -5457,7 +5475,7 @@ impl Methods { } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - (sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => { + (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, (sym::to_owned, []) => { @@ -5502,7 +5520,14 @@ impl Methods { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name); + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + then_method, + obfuscated_if_else::Unwrap::Or(u_arg), + ); }, _ => {}, } @@ -5519,9 +5544,8 @@ impl Methods { expr, t_recv, t_arg, - None, then_method, - sym::unwrap_or_default, + obfuscated_if_else::Unwrap::OrDefault, ); }, _ => {}, @@ -5538,9 +5562,8 @@ impl Methods { expr, t_recv, t_arg, - Some(u_arg), then_method, - sym::unwrap_or_else, + obfuscated_if_else::Unwrap::OrElse(u_arg), ); }, _ => { @@ -5567,8 +5590,18 @@ impl Methods { } // Handle method calls whose receiver and arguments may come from expansion if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + let method_span = path.ident.span; + + // Those methods do their own method name checking as they deal with multiple methods. + or_fun_call::check(cx, expr, method_span, path.ident.name, recv, args, self.msrv); + unnecessary_to_owned::check(cx, expr, path.ident.name, recv, args, self.msrv); + match (path.ident.name, args) { - (sym::expect, [_]) => { + (sym::clone, []) => { + clone_on_ref_ptr::check(cx, expr, recv); + clone_on_copy::check(cx, expr, recv); + }, + (sym::expect, [arg]) => { unwrap_expect_used::check( cx, expr, @@ -5578,6 +5611,7 @@ impl Methods { self.allow_expect_in_tests, unwrap_expect_used::Variant::Expect, ); + expect_fun_call::check(cx, &self.format_args, expr, method_span, recv, arg); }, (sym::expect_err, [_]) => { unwrap_expect_used::check( @@ -5590,6 +5624,15 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, + (sym::insert_str | sym::push_str, _) => { + single_char_add_str::check(cx, expr, recv, args); + }, + (sym::into_iter, []) => { + into_iter_on_ref::check(cx, expr, method_span, recv); + }, + (sym::to_string, []) => { + inefficient_to_string::check(cx, expr, recv, self.msrv); + }, (sym::unwrap, []) => { unwrap_expect_used::check( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs index 9d2c5e6232d65..c9264e747b56d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &' && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) + && cx.tcx.type_of(impl_id).is_diag_item(cx, sym::Mutex) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs index 635d06330e058..22baad40b4496 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::NEEDLESS_AS_BYTES; pub fn check(cx: &LateContext<'_>, prev_method: Symbol, method: Symbol, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); - if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { + if ty1.is_lang_item(cx, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 71c1576cd57d1..948ed8a25746e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ 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::source::SpanRangeExt; -use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{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 +33,7 @@ fn handle_expr( // `is_ascii`, then only `.all()` should warn. if revert != is_all && method.ident.name == sym::is_ascii - && path_to_local_id(receiver, first_param) + && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char && let Some(snippet) = before_chars.get_source_text(cx) @@ -75,8 +76,8 @@ 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 - && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) - && path_to_local_id(peels_expr_ref(arg), first_param) + && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) + && peels_expr_ref(arg).res_local_id() == Some(first_param) && let Some(snippet) = before_chars.get_source_text(cx) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 0075bf166cc18..4f005103d23f8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -2,15 +2,11 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -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, sym, -}; +use clippy_utils::ty::{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, sym}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -98,7 +94,7 @@ pub(super) fn check<'tcx>( if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind && let ty = cx.typeck_results().expr_ty(collect_expr) && matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList) ) && let iter_ty = cx.typeck_results().expr_ty(iter_expr) @@ -339,14 +335,18 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() && method_name.ident.name == sym::collect - && is_trait_method(self.cx, expr, sym::Iterator) + && self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); self.visit_expr(recv); return; } - if path_to_local_id(recv, self.target) { + if recv.res_local_id() == Some(self.target) { if self .illegal_mutable_capture_ids .intersection(&self.current_mutably_captured_ids) @@ -384,7 +384,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if let Some(hir_id) = path_to_local(recv) + if let Some(hir_id) = recv.res_local_id() && let Some(index) = self.hir_id_uses_map.remove(&hir_id) { if self @@ -402,7 +402,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { } } // Check if the collection is used for anything else - if path_to_local_id(expr, self.target) { + if expr.res_local_id() == Some(self.target) { self.seen_other = true; } else { walk_expr(self, expr); @@ -464,7 +464,7 @@ impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if path_to_local_id(expr, self.id) { + if expr.res_local_id() == Some(self.id) { self.count += 1; } else { walk_expr(self, expr); @@ -549,13 +549,17 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { && (recv.hir_id == self.hir_id_of_expr || self .hir_id_of_let_binding - .is_some_and(|hid| path_to_local_id(recv, hid))) - && !is_trait_method(self.cx, expr, sym::Iterator) + .is_some_and(|hid| recv.res_local_id() == Some(hid))) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } else if let ExprKind::Assign(place, value, _span) = &expr.kind && value.hir_id == self.hir_id_of_expr - && let Some(id) = path_to_local(place) + && let Some(id) = place.res_local_id() { // our iterator was directly assigned to a variable self.hir_id_of_let_binding = Some(id); diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index d77d044340dca..06e6a3c70b87d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_hir::def::Res; @@ -15,9 +15,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name let typeck = cx.typeck_results(); let outer_ty = typeck.expr_ty(expr); - if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { + if outer_ty.is_diag_item(cx, sym::Option) && outer_ty == typeck.expr_ty(recv) { if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { - let Res::Local(binding_id) = path_res(cx, recv) else { + let Res::Local(binding_id) = *recv.basic_res() else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs index 1544a12e6ba83..1622fdb88bd53 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_type = cx.typeck_results().expr_ty(expr); - is_type_diagnostic_item(cx, expr_type, sym::Option) + expr_type.is_diag_item(cx, sym::Option) } /// Returns the string of the function call that creates the temporary. diff --git a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs index 32f32f1b21673..9fa51f78c99d7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( arg2: &'tcx rustc_hir::Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { + if !(ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs index 604b48656aeac..b2466bbd982d9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs @@ -5,25 +5,24 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, sym}; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::ExprKind; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Symbol; +#[expect(clippy::needless_pass_by_value)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - then_recv: &'tcx hir::Expr<'_>, - then_arg: &'tcx hir::Expr<'_>, - unwrap_arg: Option<&'tcx hir::Expr<'_>>, + expr: &'tcx Expr<'_>, + then_recv: &'tcx Expr<'_>, + then_arg: &'tcx Expr<'_>, then_method_name: Symbol, - unwrap_method_name: Symbol, + unwrap: Unwrap<'tcx>, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { let then_eager = switch_to_eager_eval(cx, then_arg); - let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg)); + let unwrap_eager = unwrap.arg().is_none_or(|arg| switch_to_eager_eval(cx, arg)); let mut applicability = if then_eager && unwrap_eager { Applicability::MachineApplicable @@ -40,18 +39,17 @@ pub(super) fn check<'tcx>( _ => return, }; - // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol - let els = match unwrap_method_name { - sym::unwrap_or => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), - sym::unwrap_or_else if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { - let body = cx.tcx.hir_body(closure.body); - snippet_with_applicability(cx, body.value.span, "..", &mut applicability) - }, - sym::unwrap_or_else if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { - snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" + let els = match unwrap { + Unwrap::Or(arg) => snippet_with_applicability(cx, arg.span, "..", &mut applicability), + Unwrap::OrElse(arg) => match arg.kind { + ExprKind::Closure(closure) => { + let body = cx.tcx.hir_body(closure.body); + snippet_with_applicability(cx, body.value.span, "..", &mut applicability) + }, + ExprKind::Path(_) => snippet_with_applicability(cx, arg.span, "_", &mut applicability) + "()", + _ => return, }, - sym::unwrap_or_default => "Default::default()".into(), - _ => return, + Unwrap::OrDefault => "Default::default()".into(), }; let sugg = format!( @@ -83,3 +81,18 @@ pub(super) fn check<'tcx>( ); } } + +pub(super) enum Unwrap<'tcx> { + Or(&'tcx Expr<'tcx>), + OrElse(&'tcx Expr<'tcx>), + OrDefault, +} + +impl<'tcx> Unwrap<'tcx> { + fn arg(&self) -> Option<&'tcx Expr<'tcx>> { + match self { + Self::Or(a) | Self::OrElse(a) => Some(a), + Self::OrDefault => None, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs index e10bc0216e5fd..c9c1f4865b813 100644 --- a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -9,7 +10,7 @@ use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // lint if the caller of `ok()` is a `Result` && let result_type = cx.typeck_results().expr_ty(recv) && let Some(error_type) = get_error_type(cx, result_type) @@ -29,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().nth(1), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index 37a8e25bef96a..1b520a9edb566 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -1,7 +1,7 @@ +use clippy_utils::res::MaybeDef; use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{paths, sym}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use rustc_span::source_map::Spanned; 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) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) + ty.is_diag_item(cx, 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<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs index 3c38deca6cd1f..591f6aacaef87 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -11,7 +11,11 @@ use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) + && cx + .typeck_results() + .expr_ty(as_ref_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 906ead16fd0d1..3d489075ce8ac 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_to_local_id, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -23,7 +23,7 @@ pub(super) fn check( let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(as_ref_recv); - if !is_type_diagnostic_item(cx, option_ty, sym::Option) { + if !option_ty.is_diag_item(cx, sym::Option) { return; } @@ -51,7 +51,7 @@ pub(super) fn check( match &closure_expr.kind { hir::ExprKind::MethodCall(_, receiver, [], _) => { - if path_to_local_id(receiver, closure_body.params[0].pat.hir_id) + if receiver.res_local_id() == Some(closure_body.params[0].pat.hir_id) && let adj = cx .typeck_results() .expr_adjustments(receiver) @@ -72,7 +72,7 @@ pub(super) fn check( if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind && let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + inner2.res_local_id() == Some(closure_body.params[0].pat.hir_id) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs index 1a273f77fb7d0..342ffea51d656 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -37,8 +36,8 @@ pub(super) fn check<'tcx>( def_arg: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); // There are two variants of this `map_or` lint: // (1) using `map_or` as an adapter from `Result` to `Option` @@ -49,12 +48,12 @@ pub(super) fn check<'tcx>( return; } - if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) { + if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // nothing to lint! return; } - let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome); + let f_arg_is_some = map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); @@ -62,7 +61,7 @@ pub(super) fn check<'tcx>( && let arg_snippet = snippet(cx, fn_decl_span, "..") && let body = cx.tcx.hir_body(body) && let Some((func, [arg_char])) = reduce_unit_expression(body.value) - && let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = func.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let func_snippet = snippet(cx, arg_char.span, ".."); diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index 4ba8e01090427..32a9b4fe7c58b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::is_copy; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) { // Replacing `.map().unwrap_or()` with `.map_or(, )` can sometimes lead to // borrowck errors, see #10579 for one such instance. diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index 04e4503e4097f..aed4a0075c2fd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -4,8 +4,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, sym, @@ -113,7 +114,7 @@ fn check_unwrap_or_default( let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` - if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + if receiver_ty.is_diag_item(cx, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { return false; } @@ -239,7 +240,7 @@ fn check_or_fn_call<'tcx>( && let self_ty = cx.typeck_results().expr_ty(self_expr) && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + .find(|&&i| self_ty.is_diag_item(cx, i.0) && i.2.contains(&name)) { let ctxt = span.ctxt(); let mut app = Applicability::HasPlaceholders; diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs index 1a760ea733d7f..07199b84f39ec 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind}; @@ -21,14 +20,14 @@ pub(super) fn check<'tcx>( let title; let or_arg_content: Span; - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { title = "found `.or(Some(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { or_arg_content = content; } else { return; } - } else if is_type_diagnostic_item(cx, ty, sym::Result) { + } else if ty.is_diag_item(cx, sym::Result) { title = "found `.or(Ok(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { or_arg_content = content; @@ -60,7 +59,7 @@ pub(super) fn check<'tcx>( fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { if let ExprKind::Call(some_expr, [arg]) = expr.kind - && is_res_lang_ctor(cx, path_res(cx, some_expr), item) + && some_expr.res(cx).ctor_parent(cx).is_lang_item(cx, item) { Some(arg.span.source_callsite()) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs index 32752ef7435fb..18046ff24f9d2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -12,7 +12,11 @@ use super::PATH_BUF_PUSH_OVERWRITE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::PathBuf) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Str(ref path_lit, _) = lit.node && let pushed_path = Path::new(path_lit.as_str()) diff --git a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs index d3f513e7abd27..87d1aa62b2c22 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs @@ -1,8 +1,8 @@ use super::PATH_ENDS_WITH_EXT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -23,7 +23,11 @@ pub(super) fn check( msrv: Msrv, allowed_dotfiles: &FxHashSet<&'static str>, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) + if cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::Path) && !path.span.from_expansion() && let ExprKind::Lit(lit) = path.kind && let LitKind::Str(path, StrStyle::Cooked) = lit.node diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index e13df18333e46..de4207c2abf45 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; -use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use rustc_lint::LateContext; use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) && is_integer_const(cx, start, 0) @@ -82,7 +83,7 @@ fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Optio /// them to a closure, return the pattern of the closure. fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { if let Some(parent_expr) = get_parent_expr(cx, expr) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind && recv.hir_id == expr.hir_id && matches!( diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index 6738bbf0a12b9..a6dfbada53483 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; 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; @@ -32,7 +32,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, recv_ty, sym::Stdin) + if recv_ty.is_diag_item(cx, sym::Stdin) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { @@ -45,7 +45,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if args.is_empty() && 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) + && parse_result_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() && let Some(ok_ty) = substs[0].as_type() && parse_fails_on_trailing_newline(ok_ty) diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs index 40b6becd45321..a98a807d1a3ce 100644 --- a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs @@ -1,8 +1,8 @@ use super::READONLY_WRITE_LOCK; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::mir::{enclosing_mir, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; @@ -13,14 +13,17 @@ fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind && path.ident.name == sym::unwrap { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result) + cx.typeck_results() + .expr_ty(receiver) + .peel_refs() + .is_diag_item(cx, sym::Result) } else { false } } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock) + if cx.typeck_results().expr_ty(receiver).peel_refs().is_diag_item(cx, sym::RwLock) && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs index 9111604ef53b7..57a7254605f93 100644 --- a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs +++ b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs index af619c9e3bb17..e2946c22a46b5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -19,13 +19,13 @@ pub(super) fn check<'tcx>( map_arg: &'tcx hir::Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. - && is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome) + && map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind && let body = cx.tcx.hir_body(body) // And finally we check that we return a `None` in the "else case". - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(body.value)), OptionNone) + && peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { let msg = "called `map_or_else(|_| None, Some)` on a `Result` value"; let self_snippet = snippet(cx, recv.span, ".."); diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs index 54f38a322b8d9..8f47306c89479 100644 --- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{self as hir, Node}; use rustc_lint::LateContext; @@ -7,7 +8,6 @@ use std::ops::ControlFlow; 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::{peel_blocks, potential_return_of_enclosing_body}; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( } let recv_type = cx.typeck_results().expr_ty(recv); - if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) { + if !matches!(recv_type.opt_diag_name(cx), Some(sym::Option | sym::Result)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index 855babb797a21..c9c75f3f38e2d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; +use clippy_utils::{is_receiver_of_method_call, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -14,7 +14,7 @@ use super::SEARCH_IS_SOME; /// lint searching an Iterator followed by `is_some()` /// or calling `find()` on a string followed by `is_some()` or `is_none()` -#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, @@ -27,7 +27,11 @@ pub(super) fn check<'tcx>( ) { let option_check_method = if is_some { "is_some" } else { "is_none" }; // lint if caller of search is an Iterator - if is_trait_method(cx, is_some_recv, sym::Iterator) { + if cx + .ty_based_def(is_some_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) + { let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); if search_snippet.lines().count() <= 1 { @@ -109,7 +113,7 @@ pub(super) fn check<'tcx>( else if search_method == sym::find { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + if self_ty.is_lang_item(cx, hir::LangItem::String) { true } else { self_ty.is_str() diff --git a/src/tools/clippy/clippy_lints/src/methods/skip_while_next.rs b/src/tools/clippy/clippy_lints/src/methods/skip_while_next.rs index 9f0b6c34ea2ef..2452c499b9cee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/skip_while_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/skip_while_next.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,7 @@ use super::SKIP_WHILE_NEXT; /// lint use of `skip_while().next()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.skip_while().next()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_help( cx, SKIP_WHILE_NEXT, diff --git a/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs index 6d4cfdb34f319..4aff194923a6c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; use rustc_lint::LateContext; @@ -11,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { if let ExprKind::Index(indexed, index, _) = recv.kind && is_range_literal(index) && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { let mut applicability = Applicability::MaybeIncorrect; let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 8daa5db887acf..eee7fb0c5a813 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -1,10 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; 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, path_to_local_id, paths, sym}; +use clippy_utils::{paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -214,7 +215,7 @@ fn indirect_usage<'tcx>( { let mut path_to_binding = None; let _: Option = for_each_expr(cx, init_expr, |e| { - if path_to_local_id(e, binding) { + if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } ControlFlow::Continue(Descend::from(path_to_binding.is_none())) @@ -271,7 +272,6 @@ struct IterUsage { span: Span, } -#[allow(clippy::too_many_lines)] fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, @@ -351,7 +351,9 @@ fn parse_iter_usage<'tcx>( && cx .typeck_results() .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) => + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Option) => { (Some(UnwrapKind::Unwrap), e.span) }, diff --git a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs index f11a41f90f1ab..1fc0633f4f959 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{method_chain_args, sym}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,7 +10,7 @@ use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { + if !obj_ty.is_lang_item(cx, hir::LangItem::String) { return; } if let Some(arglists) = method_chain_args(arg, &[sym::chars]) { @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr } else { "" } - } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + } else if self_ty.is_lang_item(cx, hir::LangItem::String) { "&" } else { return; diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs index f0f9d30d3000a..48e89c2998efa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -19,14 +20,14 @@ pub(super) fn check<'tcx>( body: &Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let PatKind::Binding(_, arg, _, _) = param.pat.kind && let ExprKind::Lit(lit_kind) = recv.kind && let LitKind::Str(val, _) = lit_kind.node && let ExprKind::Binary(kind, lhs, rhs) = body.kind && let BinOpKind::Eq = kind.node - && let Some(lhs_path) = path_to_local(lhs) - && let Some(rhs_path) = path_to_local(rhs) + && let Some(lhs_path) = lhs.res_local_id() + && let Some(rhs_path) = rhs.res_local_id() && let scrutinee = match (lhs_path == arg, rhs_path == arg) { (true, false) => rhs, (false, true) => lhs, diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs index c60a49067ec09..3e9c677fe34a2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::SUSPICIOUS_COMMAND_ARG_SPACE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Command) + if ty.is_diag_item(cx, sym::Command) && let hir::ExprKind::Lit(lit) = &arg.kind && let ast::LitKind::Str(s, _) = &lit.node && let Some((arg1, arg2)) = s.as_str().split_once(' ') diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs index 788014d9bb632..ece97c1f758c0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::expr_or_init; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::usage::mutated_variables; -use clippy_utils::{expr_or_init, is_trait_method}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +9,10 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { - if is_trait_method(cx, count_recv, sym::Iterator) + if cx + .ty_based_def(count_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) && let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind && let closure_body = cx.tcx.hir_body(closure.body) && !cx.typeck_results().expr_ty(closure_body.value).is_unit() diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs index ffc237e3c24cc..bcd1f11931fc6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_diag_trait_item; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -11,10 +10,13 @@ use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToOwned) + if cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, input_type, sym::Cow) + && input_type.is_diag_item(cx, sym::Cow) { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/src/tools/clippy/clippy_lints/src/methods/unbuffered_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/unbuffered_bytes.rs index dd5566f8c8baf..fdddd5e2771db 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unbuffered_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unbuffered_bytes.rs @@ -1,6 +1,6 @@ use super::UNBUFFERED_BYTES; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_hir as hir; use rustc_lint::LateContext; @@ -8,7 +8,7 @@ use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { // Lint if the `.bytes()` call is from the `Read` trait and the implementor is not buffered. - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && let Some(buf_read) = cx.tcx.get_diagnostic_item(sym::IoBufRead) && let ty = cx.typeck_results().expr_ty_adjusted(recv) && !implements_trait(cx, ty, buf_read, &[]) diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs index 6371fe6442826..5e247a50358e6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::is_uninit_value_valid_for_ty; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let hir::ExprKind::Call(callee, []) = recv.kind - && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit) + && callee.ty_rel_def(cx).is_diag_item(cx, sym::maybe_uninit_uninit) && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)) { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs b/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs index 3c7955bc46981..9defd5626eb47 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -9,7 +9,7 @@ use rustc_span::sym; use super::UNIT_HASH; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { span_lint_and_then( cx, UNIT_HASH, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index d260e0ef6e197..d9d642015063e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,9 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; 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, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -19,7 +20,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'tcx>, name: Symbol, ) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } @@ -46,7 +47,7 @@ pub(super) fn check<'tcx>( // Check if the closure is .filter_map(|x| Some(x)) if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) + && expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { span_lint( @@ -95,8 +96,8 @@ pub(super) fn check<'tcx>( fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { hir::ExprKind::Call(func, args) => { - if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) { - if path_to_local_id(&args[0], arg_id) { + if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { + if args[0].res_local_id() == Some(arg_id) { return (false, false); } return (true, false); @@ -106,7 +107,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc hir::ExprKind::MethodCall(segment, recv, [arg], _) => { if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() - && path_to_local_id(arg, arg_id) + && arg.res_local_id() == Some(arg_id) { (false, true) } else { @@ -133,7 +134,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => { + hir::ExprKind::Path(ref path) + if cx + .qpath_res(path, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { (false, true) }, _ => (true, true), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index 8e3cc9abe8328..bd471e0b18e3d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_ast::ast; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -74,8 +75,8 @@ fn check_fold_with_op( && let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind && let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind - && path_to_local_id(left_expr, first_arg_id) - && (replacement.has_args || path_to_local_id(right_expr, second_arg_id)) + && left_expr.res_local_id() == Some(first_arg_id) + && (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id)) { let mut applicability = Applicability::MachineApplicable; @@ -115,7 +116,7 @@ pub(super) fn check( fold_span: Span, ) { // Check that this is a call to Iterator::fold rather than just some function called fold - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs index 39fce2c40c91d..10ea0c0c3e23e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -11,11 +11,11 @@ use rustc_span::{Span, sym}; use super::UNNECESSARY_GET_THEN_CHECK; fn is_a_std_set_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashSet) || is_type_diagnostic_item(cx, ty, sym::BTreeSet) + ty.is_diag_item(cx, sym::HashSet) || ty.is_diag_item(cx, sym::BTreeSet) } fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) + ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) } pub(super) fn check( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 20cf35363d13f..4142f9f75773e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -1,10 +1,11 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; use core::ops::ControlFlow; use itertools::Itertools; use rustc_errors::Applicability; @@ -50,7 +51,7 @@ pub fn check_for_loop_iter( // check whether `expr` is mutable fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs index efd1a718504ce..3290bdd778243 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); if let ty::Ref(_, ref_type, _) = collect_output_adjusted_type.kind() // the turbofish for collect is ::> - && let ty::Slice(slice) = ref_type.kind() - && is_type_lang_item(cx, *slice, LangItem::String) + && let ty::Slice(slice) = *ref_type.kind() + && slice.is_lang_item(cx, LangItem::String) // the argument for join is "" && let ExprKind::Lit(spanned) = &join_arg.kind && let LitKind::Str(symbol, _) = spanned.node diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 71e606add526e..2869547650f31 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -19,8 +19,8 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) -> bool { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); if (is_option || is_result || is_bool) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index cc4448192d3e7..410e973f855b6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -37,21 +38,23 @@ pub(super) fn check( } let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind { - let Some(qpath) = call.qpath_opt() else { return }; + let Some((qpath, hir_id)) = call.opt_qpath() else { + return; + }; let args = last_path_segment(qpath).args.map(|args| args.args); - let res = cx.qpath_res(qpath, call.hir_id()); + let res = cx.qpath_res(qpath, hir_id); - if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) { (sym::Some, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { (sym::Ok, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) { (sym::Err, call_args, get_ty_from_args(args, 1)) } else { return; } - } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) { + } else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; (sym::None, call_args, None) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index 1f5e3de6e7a2d..0c01be4b18756 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -3,10 +3,11 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::{Sugg, make_binop}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -54,7 +55,7 @@ pub(super) fn check<'a>( return; }; - let variant = match get_type_diagnostic_name(cx, recv_ty) { + let variant = match recv_ty.opt_diag_name(cx) { Some(sym::Option) => Variant::Some, Some(sym::Result) => Variant::Ok, Some(_) | None => return, @@ -75,11 +76,11 @@ pub(super) fn check<'a>( // .map_or(true, |x| x != y) // .map_or(true, |x| y != x) - swapped comparison && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) - && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } + && let non_binding_location = if l.res_local_id() == Some(hir_id) { r } else { l } && switch_to_eager_eval(cx, non_binding_location) - // xor, because if its both then that's a strange edge case and + // if its both then that's a strange edge case and // we can just ignore it, since by default clippy will error on this - && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) + && (l.res_local_id() == Some(hir_id)) != (r.res_local_id() == Some(hir_id)) && !is_local_used(cx, non_binding_location, hir_id) && let typeck_results = cx.typeck_results() && let l_ty = typeck_results.expr_ty(l) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..265619e263765 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -0,0 +1,111 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{expr_or_init, find_binding_init, peel_blocks}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Body, BodyId, Closure, Expr, ExprKind, HirId, QPath}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::UNNECESSARY_OPTION_MAP_OR_ELSE; +use super::utils::get_last_chain_binding_hir_id; + +fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { + let msg = "unused \"map closure\" when calling `Option::map_or_else` value"; + let mut applicability = Applicability::MachineApplicable; + let self_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + let err_snippet = snippet_with_applicability(cx, def_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + UNNECESSARY_OPTION_MAP_OR_ELSE, + expr.span, + msg, + "consider using `unwrap_or_else`", + format!("{self_snippet}.unwrap_or_else({err_snippet})"), + Applicability::MachineApplicable, + ); +} + +fn handle_qpath( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + def_arg: &Expr<'_>, + expected_hir_id: HirId, + qpath: QPath<'_>, +) { + if let QPath::Resolved(_, path) = qpath + && let Res::Local(hir_id) = path.res + && expected_hir_id == hir_id + { + emit_lint(cx, expr, recv, def_arg); + } +} + +fn handle_closure(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body_id: BodyId) { + let body = cx.tcx.hir_body(body_id); + handle_fn_body(cx, expr, recv, def_arg, body); +} + +fn handle_fn_body(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body: &Body<'_>) { + if let Some(first_param) = body.params.first() { + let body_expr = peel_blocks(body.value); + match body_expr.kind { + ExprKind::Path(qpath) => { + handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); + }, + // If this is a block (that wasn't peeled off), then it means there are statements. + ExprKind::Block(block, _) => { + if let Some(block_expr) = block.expr + // First we ensure that this is a "binding chain" (each statement is a binding + // of the previous one) and that it is a binding of the closure argument. + && let Some(last_chain_binding_id) = + get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts) + && let ExprKind::Path(qpath) = block_expr.kind + { + handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath); + } + }, + _ => {}, + } + } +} + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { + // lint if the caller of `map_or_else()` is an `Option` + if !cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { + return; + } + match map_arg.kind { + // If the second argument is a closure, we can check its body. + ExprKind::Closure(&Closure { body, .. }) => { + handle_closure(cx, expr, recv, def_arg, body); + }, + ExprKind::Path(qpath) => { + let res = cx.qpath_res(&qpath, map_arg.hir_id); + match res { + // Case 1: Local variable (could be a closure) + Res::Local(hir_id) => { + if let Some(init_expr) = find_binding_init(cx, hir_id) { + let origin = expr_or_init(cx, init_expr); + if let ExprKind::Closure(&Closure { body, .. }) = origin.kind { + handle_closure(cx, expr, recv, def_arg, body); + } + } + }, + // Case 2: Function definition + Res::Def(DefKind::Fn, def_id) => { + if let Some(local_def_id) = def_id.as_local() + && let Some(body) = cx.tcx.hir_maybe_body_owned_by(local_def_id) + { + handle_fn_body(cx, expr, recv, def_arg, body); + } + }, + _ => (), + } + }, + _ => (), + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index f84d0d6dff0af..1f6bb60414ae1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( map_arg: &'tcx Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index fa9d5332ff4f1..7cc8c79481a0f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_trait_method, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; @@ -138,7 +139,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp ] = &closure_body.params && let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind && method_path.ident.name == sym::cmp - && is_trait_method(cx, closure_body.value, sym::Ord) + && cx + .ty_based_def(closure_body.value) + .opt_parent(cx) + .is_diag_item(cx, sym::Ord) { let (closure_body, closure_arg, reverse) = if mirrored_exprs(left_expr, left_ident, right_expr, right_ident) { ( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 640931a828998..a6a39cb6ab30e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,14 +2,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, -}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; 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, return_ty, sym, -}; +use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -35,12 +32,12 @@ pub fn check<'tcx>( args: &'tcx [Expr<'_>], msrv: Msrv, ) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) && args.is_empty() { - if is_cloned_or_copied(cx, method_name, method_def_id) { + if is_cloned_or_copied(cx, method_name, method_parent_id) { unnecessary_iter_cloned::check(cx, expr, method_name, receiver); - } else if is_to_owned_like(cx, expr, method_name, method_def_id) { + } else if is_to_owned_like(cx, expr, method_name, method_parent_id) { if check_split_call_arg(cx, expr, method_name, receiver) { return; } @@ -48,7 +45,7 @@ pub fn check<'tcx>( // `check_addr_of_expr` and `check_into_iter_call_arg` determine whether the call is unnecessary // based on its context, that is, whether it is a referent in an `AddrOf` expression, an // argument in a `into_iter` call, or an argument in the call of some other function. - if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { + if check_addr_of_expr(cx, expr, method_name, method_parent_id, receiver) { return; } if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { @@ -66,12 +63,12 @@ pub fn check<'tcx>( /// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_addr_of_expr( cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, receiver: &Expr<'_>, ) -> bool { if let Some(parent) = get_parent_expr(cx, expr) @@ -133,7 +130,7 @@ fn check_addr_of_expr( // `redundant_clone`, but copyable arrays are not. && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { @@ -159,7 +156,7 @@ fn check_addr_of_expr( // *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) && (cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) { if n_receiver_refs > 0 { span_lint_and_sugg( @@ -220,7 +217,7 @@ fn check_into_iter_call_arg( && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. - && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) + && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 && !is_expr_temporary_value(cx, receiver) { @@ -320,7 +317,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to // add `.as_ref()` to the suggestion. - let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + let as_ref = if cx.typeck_results().expr_ty(expr).is_lang_item(cx, 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, sym::Target) != Some(cx.tcx.types.str_) @@ -615,21 +612,26 @@ 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 { - matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + matches!(method_name, sym::cloned | sym::copied) && method_parent_id.is_diag_item(cx, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. -fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_cow_into_owned(cx, method_name, method_def_id) - || (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id)) - || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) +fn is_to_owned_like<'a>( + cx: &LateContext<'a>, + call_expr: &Expr<'a>, + method_name: Symbol, + method_parent_id: DefId, +) -> bool { + is_cow_into_owned(cx, method_name, method_parent_id) + || (method_name != sym::to_string && is_clone_like(cx, method_name, method_parent_id)) + || is_to_string_on_string_like(cx, call_expr, method_name, method_parent_id) } /// 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 == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + method_name == sym::into_owned && method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that @@ -638,9 +640,9 @@ fn is_to_string_on_string_like<'a>( cx: &LateContext<'_>, call_expr: &'a Expr<'a>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, ) -> bool { - if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) { + if method_name != sym::to_string || !method_parent_id.is_diag_item(cx, sym::ToString) { return false; } @@ -673,12 +675,12 @@ fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { } fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { - original_arg_ty.is_str() && is_type_lang_item(cx, arg_ty, LangItem::String) + original_arg_ty.is_str() && arg_ty.is_lang_item(cx, LangItem::String) } fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { (original_arg_ty.is_slice() || original_arg_ty.is_array() || original_arg_ty.is_array_slice()) - && is_type_diagnostic_item(cx, arg_ty, sym::Vec) + && arg_ty.is_diag_item(cx, sym::Vec) } // This function will check the following: diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs index af4ade3cc0f7d..a7d9b2e0fab01 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; +use clippy_utils::{expr_or_init, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; @@ -40,9 +40,9 @@ use crate::loops::UNUSED_ENUMERATE_INDEX; pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); // If we call a method on a `std::iter::Enumerate` instance - if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate) + if recv_ty.is_diag_item(cx, sym::Enumerate) // If we are calling a method of the `Iterator` trait - && is_trait_method(cx, call_expr, sym::Iterator) + && cx.ty_based_def(call_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // And the map argument is a closure && let ExprKind::Closure(closure) = closure_arg.kind && let closure_body = cx.tcx.hir_body(closure.body) diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs index 027215e3b4d7b..73a407be4f210 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{is_never_like, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_never_like; use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed}; use rustc_hir::Expr; use rustc_lint::{LateContext, Lint}; @@ -45,9 +46,9 @@ pub(super) fn check( ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err { + let (kind, none_value, none_prefix) = if ty.is_diag_item(cx, sym::Option) && !is_err { ("an `Option`", "None", "") - } else if is_type_diagnostic_item(cx, ty, sym::Result) + } else if ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = ty.kind() && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type() { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index e56f4b80d0172..972304d79e751 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; -use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{get_parent_expr, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -42,11 +43,11 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option> { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbol, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + let Some(def) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { return; }; - if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) { + if def.opt_parent(cx).is_diag_item(cx, sym::AsRef) || def.opt_parent(cx).is_diag_item(cx, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -79,10 +80,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo applicability, ); } - } else if let Some(impl_id) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) - { + } else if matches!( + def.opt_parent(cx).opt_impl_ty(cx).opt_diag_name(cx), + Some(sym::Option | sym::Result) + ) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); @@ -137,7 +138,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) - && path_to_local_id(obj, local_id) + && obj.res_local_id() == Some(local_id) { true } else { @@ -146,7 +147,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { }, hir::ExprKind::Call(call, [recv]) => { if let hir::ExprKind::Path(qpath) = call.kind - && path_to_local_id(recv, local_id) + && recv.res_local_id() == Some(local_id) { check_qpath(cx, qpath, call.hir_id) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs index 22df1f3f485e1..c6f54159c7a78 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_inside_always_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; use rustc_lint::LateContext; @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<' && segment.ident.name == sym::new_unchecked && let [init_arg] = args && is_inside_always_const_context(cx.tcx, expr.hir_id) - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) + && cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::NonZero) && msrv.meets(cx, msrvs::CONST_UNWRAP) { let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index b0cc7a785bc31..1e1b124b44866 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -1,5 +1,5 @@ -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, path_to_local_id, usage}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_expr, usage}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -20,7 +20,7 @@ pub(super) fn derefs_to_slice<'tcx>( match ty.kind() { ty::Slice(_) => true, ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec), + ty::Adt(..) => ty.is_diag_item(cx, sym::Vec), ty::Array(_, size) => size.try_to_target_usize(cx.tcx).is_some(), ty::Ref(_, inner, _) => may_slice(cx, *inner), _ => false, @@ -130,7 +130,7 @@ impl<'tcx> CloneOrCopyVisitor<'_, 'tcx> { fn is_binding(&self, expr: &Expr<'tcx>) -> bool { self.binding_hir_ids .iter() - .any(|hir_id| path_to_local_id(expr, *hir_id)) + .any(|&hir_id| expr.res_local_id() == Some(hir_id)) } } diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs index bfb481f4fc092..5debaab2067bd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -19,7 +19,11 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Vec) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), .. diff --git a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs index 8ed61637eca21..5727843302d6a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs +++ b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -19,9 +18,13 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, (msg, help): (&'static str, &'static str), ) { - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .peel_refs() + .is_diag_item(cx, sym::File) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, VERBOSE_FILE_READS, expr.span, msg, |diag| { diff --git a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs index b5f34a9be2e23..3081d7f91f067 100644 --- a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs +++ b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' if let Some(did) = ty.ty_adt_def() && cx.tcx.is_diagnostic_item(sym::Waker, did.did()) && let ExprKind::MethodCall(_, waker_ref, &[], _) = recv.kind - && is_trait_method(cx, recv, sym::Clone) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Clone) { let mut applicability = Applicability::MachineApplicable; let snippet = snippet_with_applicability(cx, waker_ref.span.source_callsite(), "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 74b297c13621e..12a6f345168fe 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -81,7 +81,6 @@ impl fmt::Display for Convention { } } -#[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: Symbol, diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index f9a7c562c7a59..ba62853c74571 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_trait_method, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -78,7 +79,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { - if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { + if cx.typeck_results().expr_ty(receiver).is_floating_point() + || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) + { match path.ident.name { sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 8822b32b1c3d6..15b773c2c64f4 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sym; 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}; @@ -112,11 +112,9 @@ fn should_lint<'tcx>( if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); - if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { + if path.ident.name == sym::debug_struct && recv_ty.is_diag_item(cx, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name == sym::finish_non_exhaustive - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) - { + } else if path.ident.name == sym::finish_non_exhaustive && recv_ty.is_diag_item(cx, sym::DebugStruct) { has_finish_non_exhaustive = true; } } @@ -137,7 +135,7 @@ fn as_field_call<'tcx>( ) -> Option { if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind && let recv_ty = typeck_results.expr_ty(recv).peel_refs() - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) + && recv_ty.is_diag_item(cx, sym::DebugStruct) && path.ident.name == sym::field && let ExprKind::Lit(lit) = &debug_field.kind && let LitKind::Str(sym, ..) = lit.node @@ -180,7 +178,9 @@ fn check_struct<'tcx>( .fields() .iter() .filter_map(|field| { - if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) { + if field_accesses.contains(&field.ident.name) + || field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + { None } else { Some((field.span, "this field is unused")) diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 3b44d4b60d320..ddd4271960e13 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,7 @@ 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, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_parent_expr, 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}; @@ -84,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. let var = if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind - && let Some(var) = path_to_local(lhs) + && let Some(var) = lhs.res_local_id() && expr.span.desugaring_kind().is_none() { var @@ -325,7 +326,7 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { return; } - if path_to_local_id(expr, self.var) + if expr.res_local_id() == Some(self.var) // Check that this is a read, not a write. && !is_in_assignment_position(self.cx, expr) { diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index fe2157ca533a6..2fef8404f824d 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -1,7 +1,12 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; +use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::ty_from_hir_ty; +use rustc_errors::{Applicability, Diag}; +use rustc_hir::{self as hir, Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::mir::Mutability; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -88,24 +93,83 @@ declare_clippy_lint! { declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); +// NOTE: we don't use `check_expr` because that would make us lint every _use_ of such mutexes, not +// just their definitions impl<'tcx> LateLintPass<'tcx> for Mutex { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !item.span.from_expansion() + && let ItemKind::Static(_, _, ty, body_id) = item.kind { - let mutex_param = subst.type_at(0); - if let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`" - ); - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), + let body = cx.tcx.hir_body(body_id); + let mid_ty = ty_from_hir_ty(cx, ty); + check_expr(cx, body.value.peel_blocks(), &TypeAscriptionKind::Required(ty), mid_ty); + } + } + fn check_local(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx LetStmt<'_>) { + if !stmt.span.from_expansion() + && let Some(init) = stmt.init + { + let mid_ty = cx.typeck_results().expr_ty(init); + check_expr(cx, init.peel_blocks(), &TypeAscriptionKind::Optional(stmt.ty), mid_ty); + } + } +} + +/// Whether the type ascription `: Mutex` (which we'll suggest replacing with `AtomicX`) is +/// required +enum TypeAscriptionKind<'tcx> { + /// Yes; for us, this is the case for statics + Required(&'tcx hir::Ty<'tcx>), + /// No; the ascription might've been necessary in an expression like: + /// ```ignore + /// let mutex: Mutex = Mutex::new(0); + /// ``` + /// to specify the type of `0`, but since `AtomicX` already refers to a concrete type, we won't + /// need this ascription anymore. + Optional(Option<&'tcx hir::Ty<'tcx>>), +} + +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty_ascription: &TypeAscriptionKind<'tcx>, ty: Ty<'tcx>) { + if let ty::Adt(_, subst) = ty.kind() + && ty.is_diag_item(cx, sym::Mutex) + && let mutex_param = subst.type_at(0) + && let Some(atomic_name) = get_atomic_name(mutex_param) + { + let msg = "using a `Mutex` where an atomic would do"; + let diag = |diag: &mut Diag<'_, _>| { + // if `expr = Mutex::new(arg)`, we can try emitting a suggestion + if let ExprKind::Call(qpath, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(_mutex, new)) = qpath.kind + && new.ident.name == sym::new + { + let mut applicability = Applicability::MaybeIncorrect; + let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability); + let mut suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + match ty_ascription { + TypeAscriptionKind::Required(ty_ascription) => { + suggs.push((ty_ascription.span, format!("std::sync::atomic::{atomic_name}"))); + }, + TypeAscriptionKind::Optional(Some(ty_ascription)) => { + // See https://github.com/rust-lang/rust-clippy/pull/15386 for why this is + // required + let colon_ascription = (cx.sess().source_map()) + .span_extend_to_prev_char_before(ty_ascription.span, ':', true) + .with_leading_whitespace(cx) + .into_span(); + suggs.push((colon_ascription, String::new())); + }, + TypeAscriptionKind::Optional(None) => {}, // nothing to remove/replace } + diag.multipart_suggestion("try", suggs, applicability); + } else { + diag.help(format!("consider using an `{atomic_name}` instead")); } + diag.help("if you just want the locking behavior and not the internal type, consider using `Mutex<()>`"); + }; + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), } } } @@ -135,7 +199,8 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { IntTy::I128 => None, } }, - ty::RawPtr(_, _) => Some("AtomicPtr"), + // `AtomicPtr` only accepts `*mut T` + ty::RawPtr(_, Mutability::Mut) => Some("AtomicPtr"), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index b8601f77e2492..55208ae708b93 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -1,9 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::{Block, Label, ast}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use clippy_utils::higher; +use clippy_utils::source::{indent_of, snippet_block, snippet_with_context}; +use rustc_ast::Label; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_span::{ExpnKind, Span}; declare_clippy_lint! { /// ### What it does @@ -127,9 +130,11 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); -impl EarlyLintPass for NeedlessContinue { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if !expr.span.from_expansion() { +impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + // We cannot use `from_expansion` because for loops, while loops and while let loops are desugared + // into `loop` expressions. + if !matches!(expr.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(..)) { check_and_warn(cx, expr); } } @@ -188,19 +193,19 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { +fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> bool { match else_expr.kind { - ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), - ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), + ExprKind::Block(else_block, _) => is_first_block_stmt_continue(else_block, label), + ExprKind::Continue(l) => compare_labels(label, l.label.as_ref()), _ => false, } } -fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block<'_>, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::Continue(ref l) = e.kind { - compare_labels(label, l.as_ref()) + StmtKind::Semi(e) | StmtKind::Expr(e) => { + if let ExprKind::Continue(l) = e.kind { + compare_labels(label, l.label.as_ref()) } else { false } @@ -222,20 +227,34 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> } /// If `expr` is a loop expression (while/while let/for/loop), calls `func` with -/// the AST object representing the loop block of `expr`. -fn with_loop_block(expr: &ast::Expr, mut func: F) +/// the HIR object representing the loop block of `expr`. +fn with_loop_block(expr: &Expr<'_>, mut func: F) where - F: FnMut(&Block, Option<&Label>), + F: FnMut(&Block<'_>, Option<&Label>), { - if let ast::ExprKind::While(_, loop_block, label) - | ast::ExprKind::ForLoop { - body: loop_block, - label, - .. + if let Some(higher::ForLoop { body, label, .. }) = higher::ForLoop::hir(expr) + && let ExprKind::Block(block, _) = &body.kind + { + func(block, label.as_ref()); + return; } - | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind + + if let Some(higher::While { body, label, .. }) = higher::While::hir(expr) + && let ExprKind::Block(block, _) = &body.kind { - func(loop_block, label.as_ref()); + func(block, label.as_ref()); + return; + } + + if let Some(higher::WhileLet { if_then, label, .. }) = higher::WhileLet::hir(expr) + && let ExprKind::Block(block, _) = &if_then.kind + { + func(block, label.as_ref()); + return; + } + + if let ExprKind::Loop(block, label, LoopSource::Loop, ..) = expr.kind { + func(block, label.as_ref()); } } @@ -247,17 +266,18 @@ where /// - The `if` condition expression, /// - The `then` block, and /// - The `else` expression. -fn with_if_expr(stmt: &ast::Stmt, mut func: F) +fn with_if_expr(expr: &Expr<'_>, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), + F: FnMut(&Expr<'_>, &Expr<'_>, &Block<'_>, &Expr<'_>), { - match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind { - func(e, cond, if_block, else_expr); - } - }, - _ => {}, + if let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) + && let ExprKind::Block(then, _) = then.kind + { + func(expr, cond, then, r#else); } } @@ -269,20 +289,21 @@ enum LintType { } /// Data we pass around for construction of help messages. -struct LintData<'a> { +#[derive(Debug)] +struct LintData<'hir> { /// The `if` expression encountered in the above loop. - if_expr: &'a ast::Expr, + if_expr: &'hir Expr<'hir>, /// The condition expression for the above `if`. - if_cond: &'a ast::Expr, + if_cond: &'hir Expr<'hir>, /// The `then` block of the `if` statement. - if_block: &'a Block, + if_block: &'hir Block<'hir>, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. - else_expr: &'a ast::Expr, + else_expr: &'hir Expr<'hir>, /// The 0-based index of the `if` statement in the containing loop block. - stmt_idx: usize, + stmt_idx: Option, /// The statements of the loop block. - loop_block: &'a Block, + loop_block: &'hir Block<'hir>, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -299,7 +320,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; -fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { +fn emit_warning(cx: &LateContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { // snip is the whole *help* message that appears after the warning. // message is the warning message. // expr is the expression which the lint warning message refers to. @@ -325,8 +346,15 @@ fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: L ); } -fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); +fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintData<'_>) -> String { + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); @@ -339,8 +367,15 @@ fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintD ) } -fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); +fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &LintData<'_>) -> String { + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); // Region B let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span))); @@ -352,18 +387,32 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin let indent = span_of_first_expr_in_block(data.if_block) .and_then(|span| indent_of(cx, span)) .unwrap_or(0); - let to_annex = data.loop_block.stmts[data.stmt_idx + 1..] - .iter() - .map(|stmt| { - let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let to_annex = if let Some(stmt_idx) = data.stmt_idx { + let mut lines = data.loop_block.stmts[stmt_idx + 1..] + .iter() + .map(|stmt| { + let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let snip = snippet_block(cx, span, "..", None); + snip.lines() + .map(|line| format!("{}{line}", " ".repeat(indent))) + .collect::>() + .join("\n") + }) + .collect::>(); + if let Some(expr) = data.loop_block.expr { + let span = expr.span; let snip = snippet_block(cx, span, "..", None); - snip.lines() + let expr_lines = snip + .lines() .map(|line| format!("{}{line}", " ".repeat(indent))) .collect::>() - .join("\n") - }) - .collect::>() - .join("\n"); + .join("\n"); + lines.push(expr_lines); + } + lines.join("\n") + } else { + String::new() + }; let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( @@ -373,46 +422,53 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_last_stmt_in_expr(inner_expr: &ast::Expr, func: &F) +fn check_last_stmt_in_expr(cx: &LateContext<'_>, inner_expr: &Expr<'_>, func: &F) where F: Fn(Option<&Label>, Span), { - match &inner_expr.kind { - ast::ExprKind::Continue(continue_label) => { - func(continue_label.as_ref(), inner_expr.span); + match inner_expr.kind { + ExprKind::Continue(continue_label) => { + func(continue_label.label.as_ref(), inner_expr.span); }, - ast::ExprKind::If(_, then_block, else_block) => { - check_last_stmt_in_block(then_block, func); + ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => { + check_last_stmt_in_block(cx, then_block, func); if let Some(else_block) = else_block { - check_last_stmt_in_expr(else_block, func); + check_last_stmt_in_expr(cx, else_block, func); } }, - ast::ExprKind::Match(_, arms, _) => { + ExprKind::Match(_, arms, _) => { + let match_ty = cx.typeck_results().expr_ty(inner_expr); + if !match_ty.is_unit() && !match_ty.is_never() { + return; + } for arm in arms { - if let Some(expr) = &arm.body { - check_last_stmt_in_expr(expr, func); - } + check_last_stmt_in_expr(cx, arm.body, func); } }, - ast::ExprKind::Block(b, _) => { - check_last_stmt_in_block(b, func); + ExprKind::Block(b, _) => { + check_last_stmt_in_block(cx, b, func); }, _ => {}, } } -fn check_last_stmt_in_block(b: &Block, func: &F) +fn check_last_stmt_in_block(cx: &LateContext<'_>, b: &Block<'_>, func: &F) where F: Fn(Option<&Label>, Span), { + if let Some(expr) = b.expr { + check_last_stmt_in_expr(cx, expr, func); + return; + } + if let Some(last_stmt) = b.stmts.last() - && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + && let StmtKind::Expr(inner_expr) | StmtKind::Semi(inner_expr) = last_stmt.kind { - check_last_stmt_in_expr(inner_expr, func); + check_last_stmt_in_expr(cx, inner_expr, func); } } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { +fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { with_loop_block(expr, |loop_block, label| { let p = |continue_label: Option<&Label>, span: Span| { if compare_labels(label, continue_label) { @@ -427,16 +483,51 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }; - let stmts = &loop_block.stmts; + let stmts = loop_block.stmts; for (i, stmt) in stmts.iter().enumerate() { let mut maybe_emitted_in_if = false; - with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind { + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { + let data = &LintData { + if_expr, + if_cond: cond, + if_block: then_block, + else_expr, + stmt_idx: Some(i), + loop_block, + }; + + maybe_emitted_in_if = true; + if needless_continue_in_else(else_expr, label) { + emit_warning( + cx, + data, + DROP_ELSE_BLOCK_AND_MERGE_MSG, + LintType::ContinueInsideElseBlock, + ); + } else if is_first_block_stmt_continue(then_block, label) { + emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; + } + }); + } + + if i == stmts.len() - 1 && loop_block.expr.is_none() && !maybe_emitted_in_if { + check_last_stmt_in_block(cx, loop_block, &p); + } + } + + if let Some(expr) = loop_block.expr { + let mut maybe_emitted_in_if = false; + + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { let data = &LintData { if_expr, if_cond: cond, if_block: then_block, else_expr, - stmt_idx: i, + stmt_idx: None, loop_block, }; @@ -455,8 +546,8 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }); - if i == stmts.len() - 1 && !maybe_emitted_in_if { - check_last_stmt_in_block(loop_block, &p); + if !maybe_emitted_in_if { + check_last_stmt_in_block(cx, loop_block, &p); } } }); @@ -491,8 +582,12 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &Block) -> Option { - block.stmts.first().map(|stmt| stmt.span) +fn span_of_first_expr_in_block(block: &Block<'_>) -> Option { + block + .stmts + .first() + .map(|stmt| stmt.span) + .or(block.expr.map(|expr| expr.span)) } #[cfg(test)] diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 3a6ccc2bca998..d03188f1d39b8 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind}; @@ -7,8 +8,8 @@ use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sym; use clippy_utils::ty::has_iter_method; -use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -65,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) && method_name.ident.name == sym::for_each - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, 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() // Skip the lint if the body is not block because this is simpler than `for` loop. diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index a914267cf5002..464a91959a8eb 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; @@ -116,7 +116,7 @@ impl LocalAssign { } Some(Self { - lhs_id: path_to_local(lhs)?, + lhs_id: lhs.res_local_id()?, rhs_span: rhs.span.source_callsite(), span, }) diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs index ad6313e391bd9..4bcd26c74f57f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { } declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Bound<'tcx> { /// The [`DefId`] of the type parameter the bound refers to param: DefId, diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 7052e1d0fbe5d..3d2285efbe185 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -364,7 +364,6 @@ impl MutablyUsedVariablesCtxt<'_> { } impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { - #[allow(clippy::if_same_then_else)] fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { if let euv::Place { base: @@ -398,7 +397,6 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - #[allow(clippy::if_same_then_else)] fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 455d1426aa8cb..fb5f21acf2af2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, -}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; +use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; @@ -219,7 +218,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { diag.span_help(span, "or consider marking this type as `Copy`"); } - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path @@ -262,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - if is_type_lang_item(cx, ty, LangItem::String) + if ty.is_lang_item(cx, LangItem::String) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { @@ -362,7 +361,7 @@ fn extract_clone_suggestions<'tcx>( let mut spans = Vec::new(); for_each_expr_without_closures(body, |e| { if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) + && recv.res_local_id() == Some(id) { if seg.ident.name == sym::capacity { return ControlFlow::Break(()); diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index 2a2160c3be2d1..986a827c59c55 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_res; +use clippy_utils::res::MaybeQPath; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -94,7 +94,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark { 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 Res::Def(DefKind::Ctor(..), ctor_id) = path.res(cx) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some" diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 0d6666eed455c..701923cf6efc4 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; -use clippy_utils::{ - in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, -}; +use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ @@ -109,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { self.underscore_bindings.swap_remove(&def_id); } } diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index e531f797272d2..3285023b34aa8 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core, -}; +use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, LangItem, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TraitRef}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::{EarlyBinder, TyCtxt, TypeckResults}; +use rustc_session::impl_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -109,33 +108,96 @@ declare_clippy_lint! { suspicious, "non-canonical implementation of `PartialOrd` on an `Ord` type" } -declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); +impl_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); + +#[expect( + clippy::struct_field_names, + reason = "`_trait` suffix is meaningful on its own, \ + and creating an inner `StoredTraits` struct would just add a level of indirection" +)] +pub(crate) struct NonCanonicalImpls { + partial_ord_trait: Option, + ord_trait: Option, + clone_trait: Option, + copy_trait: Option, +} + +impl NonCanonicalImpls { + pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { + let lang_items = tcx.lang_items(); + Self { + partial_ord_trait: lang_items.partial_ord_trait(), + ord_trait: tcx.get_diagnostic_item(sym::Ord), + clone_trait: lang_items.clone_trait(), + copy_trait: lang_items.copy_trait(), + } + } +} + +/// The traits that this lint looks at +enum Trait { + Clone, + PartialOrd, +} impl LateLintPass<'_> for NonCanonicalImpls { - fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind - && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) - && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) - && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) - // NOTE: check this early to avoid expensive checks that come after this one - && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if let ItemKind::Impl(impl_) = item.kind + // Both `PartialOrd` and `Clone` have one required method, and `PartialOrd` can have 5 methods in total + && (1..=5).contains(&impl_.items.len()) + && let Some(of_trait) = impl_.of_trait + && let Some(trait_did) = of_trait.trait_ref.trait_def_id() + // Check this early to hopefully bail out as soon as possible + && let trait_ = if Some(trait_did) == self.clone_trait { + Trait::Clone + } else if Some(trait_did) == self.partial_ord_trait { + Trait::PartialOrd + } else { + return; + } && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) - && let body = cx.tcx.hir_body(impl_item_id) - && let ExprKind::Block(block, ..) = body.value.kind - && !block.span.in_external_macro(cx.sess().source_map()) - && !is_from_proc_macro(cx, impl_item) { - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) - { - check_clone_on_copy(cx, impl_item, block); - } else if trait_name == Some(sym::PartialOrd) - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); + let mut assoc_fns = impl_ + .items + .iter() + .map(|id| cx.tcx.hir_impl_item(*id)) + .filter_map(|assoc| { + if let ImplItemKind::Fn(_, body_id) = assoc.kind + && let body = cx.tcx.hir_body(body_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + { + Some((assoc, body, block)) + } else { + None + } + }); + + match trait_ { + Trait::Clone => { + if let Some(copy_trait) = self.copy_trait + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && implements_trait(cx, trait_impl.self_ty(), copy_trait, &[]) + { + for (assoc, _, block) in assoc_fns { + check_clone_on_copy(cx, assoc, block); + } + } + }, + Trait::PartialOrd => { + if let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, + // since it doesn't have an `Rhs` + && let [lhs, rhs] = trait_impl.args.as_slice() + && lhs == rhs + && let Some(ord_trait) = self.ord_trait + && implements_trait(cx, trait_impl.self_ty(), ord_trait, &[]) + && let Some((assoc, body, block)) = + assoc_fns.find(|(assoc, _, _)| assoc.ident.name == sym::partial_cmp) + { + check_partial_ord_on_ord(cx, assoc, item, body, block); + } + }, } } } @@ -153,6 +215,10 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B return; } + if is_from_proc_macro(cx, impl_item) { + return; + } + span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -164,7 +230,7 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B ); } - if impl_item.ident.name == sym::clone_from { + if impl_item.ident.name == sym::clone_from && !is_from_proc_macro(cx, impl_item) { span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -181,7 +247,6 @@ fn check_partial_ord_on_ord<'tcx>( cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>, item: &Item<'_>, - trait_impl: &TraitRef<'_>, body: &Body<'_>, block: &Block<'tcx>, ) { @@ -206,12 +271,7 @@ fn check_partial_ord_on_ord<'tcx>( && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) { return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { + } else if is_from_proc_macro(cx, impl_item) { return; } @@ -261,7 +321,7 @@ fn expr_is_cmp<'tcx>( impl_item: &ImplItem<'_>, needs_fully_qualified: &mut bool, ) -> bool { - let impl_item_did = impl_item.owner_id.def_id; + let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); match expr.kind { ExprKind::Call( Expr { @@ -271,16 +331,16 @@ fn expr_is_cmp<'tcx>( }, [cmp_expr], ) => { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) - // Fix #11178, allow `Self::cmp(self, ..)` too - && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) + typeck.qpath_res(some_path, *some_hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + // Fix #11178, allow `Self::cmp(self, ..)` + && self_cmp_call(cx, typeck, cmp_expr, needs_fully_qualified) }, ExprKind::MethodCall(_, recv, [], _) => { - cx.tcx - .typeck(impl_item_did) - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + typeck + .type_dependent_def(expr.hir_id) + .assoc_parent(cx) + .is_diag_item(cx, sym::Into) + && self_cmp_call(cx, typeck, recv, needs_fully_qualified) }, _ => false, } @@ -289,12 +349,13 @@ fn expr_is_cmp<'tcx>( /// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. fn self_cmp_call<'tcx>( cx: &LateContext<'tcx>, + typeck: &TypeckResults<'tcx>, cmp_expr: &'tcx Expr<'tcx>, - def_id: LocalDefId, needs_fully_qualified: &mut bool, ) -> bool { match cmp_expr.kind { - ExprKind::Call(path, [_, _]) => path_res(cx, path) + ExprKind::Call(path, [_, _]) => path + .res(typeck) .opt_def_id() .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)), ExprKind::MethodCall(_, recv, [_], ..) => { @@ -309,11 +370,7 @@ fn self_cmp_call<'tcx>( // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp` *needs_fully_qualified = true; - // It's a bit annoying but `typeck_results` only gives us the CURRENT body, which we - // have none, not of any `LocalDefId` we want, so we must call the query itself to avoid - // an immediate ICE - cx.tcx - .typeck(def_id) + typeck .type_dependent_def_id(cmp_expr.hir_id) .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)) }, diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index 7ecde40aee017..61e4d419fdf06 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; 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::res::MaybeResPath; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; +use clippy_utils::{fn_def_id, is_no_std_crate, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -188,7 +189,7 @@ impl LazyInfo { fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option { // Check if item is a `once_cell:sync::Lazy` static. if let ItemKind::Static(_, _, ty, body_id) = item.kind - && let Some(path_def_id) = path_def_id(cx, ty) + && let Some(path_def_id) = ty.basic_res().opt_def_id() && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { @@ -219,7 +220,7 @@ impl LazyInfo { fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap>) { // Applicability might get adjusted to `Unspecified` later if any calls // in `calls_span_and_id` are not replaceable judging by the `sugg_map`. - let mut appl = Applicability::MachineApplicable; + let mut app = Applicability::MachineApplicable; let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())]; for (span, def_id) in &self.calls_span_and_id { @@ -228,7 +229,7 @@ impl LazyInfo { suggs.push((*span, sugg)); } else { // If NO suggested replacement, not machine applicable - appl = Applicability::Unspecified; + app = Applicability::Unspecified; } } @@ -239,7 +240,7 @@ impl LazyInfo { self.ty_span_no_args, "this type has been superseded by `LazyLock` in the standard library", |diag| { - diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); + diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, app); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index ec8c2299d8cbb..784ea34bac58f 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{get_expr_use_or_unification_node, path_def_id, path_to_local, path_to_local_id}; +use clippy_utils::get_expr_use_or_unification_node; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -212,7 +213,7 @@ impl Usage { /// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the /// `DefId` of the function paired with the parameter's index. #[derive(Default)] -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Params { params: Vec, by_id: HirIdMap, @@ -358,7 +359,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && let Some(param) = self.params.get_by_id_mut(id) { let typeck = cx.typeck_results(); @@ -370,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { Some((Node::Expr(parent), child_id)) => match parent.kind { // Recursive call. Track which index the parameter is used in. ExprKind::Call(callee, args) - if path_def_id(cx, callee).is_some_and(|id| { + if callee.res(cx).opt_def_id().is_some_and(|id| { id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id)) }) => { @@ -395,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { }, // Parameter update e.g. `x = x + 1` ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) - if rhs.hir_id == child_id && path_to_local_id(lhs, id) => + if rhs.hir_id == child_id && lhs.res_local_id() == Some(id) => { return; }, diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index e062e55dad894..0a6499e095832 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -2,7 +2,7 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; @@ -108,9 +108,7 @@ impl ArithmeticSideEffects { rhs_ty: Ty<'tcx>, ) -> bool { let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem); - let is_sat_or_wrap = |ty: Ty<'_>| { - is_type_diagnostic_item(cx, ty, sym::Saturating) || is_type_diagnostic_item(cx, ty, sym::Wrapping) - }; + let is_sat_or_wrap = |ty: Ty<'_>| ty.is_diag_item(cx, sym::Saturating) || ty.is_diag_item(cx, sym::Wrapping); // If the RHS is `NonZero`, then division or module by zero will never occur. if Self::is_non_zero_u(cx, rhs_ty) && is_div_or_rem { diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs index e87cfd103c301..d6af0234f010b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs @@ -47,7 +47,6 @@ fn check_compare<'a>(cx: &LateContext<'a>, bit_op: &Expr<'a>, cmp_op: BinOpKind, } } -#[allow(clippy::too_many_lines)] fn check_bit_mask( cx: &LateContext<'_>, bit_op: BinOpKind, diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index 604f8f5da0b86..05358de5b3482 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; @@ -47,11 +47,14 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { - Some(sym::from_str_method) => true, - Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path + .res(cx) + .opt_def_id() + .is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { + Some(sym::from_str_method) => true, + Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, + }) => { (arg, arg.span) }, diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index d897b0e8dd918..4a1da7e07a888 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -1,8 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -18,7 +18,11 @@ pub(crate) fn check<'tcx>( ) { if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) + && cx + .typeck_results() + .expr_ty(self_arg) + .peel_refs() + .is_diag_item(cx, sym::Duration) && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { diff --git a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs index 7b98afa9b40bd..1620312474e99 100644 --- a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs +++ b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>( if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() && let right_ty = cx.typeck_results().expr_ty(right) - && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) + && (right_ty.is_integral() || right_ty.is_diag_item(cx, 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/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 3483f3081a58c..c32c74a8fe608 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, - is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, + is_in_const_context, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -314,9 +315,9 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); - if is_res_lang_ctor(cx, res, OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, OptionSome) { return Some((inner_pat, false)); - } else if is_res_lang_ctor(cx, res, ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) { return Some((inner_pat, true)); } } @@ -379,9 +380,14 @@ fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { - is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + cx.qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) && matches!(first_pat.kind, PatKind::Wild) }, PatKind::Wild => true, diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index ee1d59490ce9f..57127e9d22985 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; use core::ops::ControlFlow; @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { return; } let owner = cx.tcx.local_def_id_to_hir_id(def_id).expect_owner(); - if is_type_diagnostic_item(cx, return_ty(cx, owner), sym::Result) { + if return_ty(cx, owner).is_diag_item(cx, sym::Result) { lint_impl_body(cx, span, body); } } diff --git a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs index 9b9024c810575..44217ac2c4fcb 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,13 +47,21 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { } // If the expression is of type `Option` - let is_ty_option = - |expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option); + let is_ty_option = |expr: &Expr<'_>| { + cx.typeck_results() + .expr_ty(expr) + .peel_refs() + .is_diag_item(cx, sym::Option) + }; // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone) + && peel_hir_expr_refs(expr) + .0 + .res(cx) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index 4ce6827cac9bb..a5e57d97301e3 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_to_local_id, sym}; +use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let PatKind::Binding(BindingMode::MUT, id, name, None) = local.pat.kind && !local.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().pat_ty(local.pat) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let Res::Local(id) = path.res && !expr.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().expr_ty(left) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, @@ -176,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { if let Some(mut searcher) = self.searcher.take() && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { searcher.err_span = searcher.err_span.to(stmt.span); diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs index da56a785007c4..68a34d459e0df 100644 --- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs +++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; 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}; @@ -34,7 +34,10 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node && path.ident.name == sym::set_readonly - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) + && cx + .typeck_results() + .expr_ty(receiver) + .is_diag_item(cx, sym::FsPermissions) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 9eed46460a613..8446b6fbbea57 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; 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, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, std_or_core, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; @@ -561,7 +562,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ } // Check if this is local we care about - let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else { + let Some(&args_idx) = e.res_local_id().and_then(|id| self.bindings.get(&id)) else { return walk_expr(self, e); }; let args = &self.args[args_idx]; @@ -617,7 +618,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type // doesn't coerce to a slice and our adjusted type check below isn't enough, // but it would still be valid to call with a slice - if is_allowed_vec_method(self.cx, use_expr) { + if is_allowed_vec_method(use_expr) { return; } } @@ -742,8 +743,10 @@ fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, expr: &Expr<'_>) -> bool { if let ExprKind::Call(pathexp, []) = expr.kind { - path_def_id(cx, pathexp) - .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))) + matches!( + pathexp.basic_res().opt_diag_name(cx), + Some(sym::ptr_null | sym::ptr_null_mut) + ) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs index 66c59cb70d361..694d44d708568 100644 --- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::is_path_lang_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { // We ignore fields that have `#[doc(hidden)]`. && !is_doc_hidden(cx.tcx.hir_attrs(field.hir_id)) // We ignore fields that are `PhantomData`. - && !is_path_lang_item(cx, field.ty, LangItem::PhantomData) + && !field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) { span_lint_hir_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index d3a5a5dddfbeb..e67ea1f5e370d 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -4,14 +4,15 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, 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, sym, + pat_and_expr_can_be_question_mark, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, + sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -205,7 +206,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ IfBlockType::IfIs(caller, caller_ty, call_sym, if_then) => { // If the block could be identified as `if x.is_none()/is_err()`, // we then only need to check the if_then return to see if it is none/err. - is_type_diagnostic_item(cx, caller_ty, smbl) + caller_ty.is_diag_item(cx, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { sym::Option => call_sym == sym::is_none, @@ -214,20 +215,20 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ } }, IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { - is_type_diagnostic_item(cx, let_expr_ty, smbl) + let_expr_ty.is_diag_item(cx, smbl) && match smbl { sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, // because the later one will be suggested as `if option.is_none()` thus causing conflict. - is_res_lang_ctor(cx, res, OptionSome) + res.ctor_parent(cx).is_lang_item(cx, OptionSome) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) }, sym::Result => { - (is_res_lang_ctor(cx, res, ResultOk) + (res.ctor_parent(cx).is_lang_item(cx, ResultOk) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) - || is_res_lang_ctor(cx, res, ResultErr) + || res.ctor_parent(cx).is_lang_item(cx, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) && if_else.is_none() }, @@ -247,8 +248,11 @@ fn expr_return_none_or_err( match peel_blocks_with_stmt(expr).kind { ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym), ExprKind::Path(ref qpath) => match smbl { - sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone), - sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), + sym::Option => cx + .qpath_res(qpath, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), + sym::Result => expr.res_local_id().is_some() && expr.res_local_id() == cond_expr.res_local_id(), _ => false, }, ExprKind::Call(call_expr, [arg]) => { @@ -342,7 +346,10 @@ fn extract_ctor_call<'a, 'tcx>( pat: &'a Pat<'tcx>, ) -> Option<&'a Pat<'tcx>> { if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor) + && cx + .qpath_res(variant_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, expected_ctor) { Some(val_binding) } else { @@ -371,7 +378,7 @@ fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Ar // Extract out `val` && let Some(binding) = extract_binding_pat(val_binding) // Check body is just `=> val` - && path_to_local_id(peel_blocks(arm.body), binding) + && peel_blocks(arm.body).res_local_id() == Some(binding) { true } else { @@ -393,7 +400,7 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A // check `=> return Err(...)` && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) + && ok_ctor.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // check if `...` is `val` from binding or `val.into()` && is_local_or_local_into(cx, ret_expr, ok_val) { @@ -404,10 +411,10 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A }, TryMode::Option => { // Check the pat is `None` - if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone) + if arm.pat.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) // Check `=> return None` && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind - && is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone) + && ret_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !ret_expr.span.from_expansion() { true @@ -424,8 +431,10 @@ fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); match expr.kind { - ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), - _ => path_to_local_id(expr, val), + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => { + is_into_call && recv.res_local_id() == Some(val) + }, + _ => expr.res_local_id() == Some(val), } } @@ -477,7 +486,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_then, if_else, ) - && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) + && ((is_early_return(sym::Option, cx, &if_block) && peel_blocks(if_then).res_local_id() == Some(bind_id)) || is_early_return(sym::Result, cx, &if_block)) && if_else .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) @@ -485,7 +494,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .is_none() { if !is_copy(cx, caller_ty) - && let Some(hir_id) = path_to_local(let_expr) + && let Some(hir_id) = let_expr.res_local_id() && local_used_after_expr(cx, hir_id, expr) { return; @@ -521,11 +530,11 @@ impl QuestionMark { } } -fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { +fn is_try_block(bl: &Block<'_>) -> bool { if let Some(expr) = bl.expr && let ExprKind::Call(callee, [_]) = expr.kind { - is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput) + callee.opt_lang_path() == Some(LangItem::TryTraitFromOutput) } else { false } @@ -581,8 +590,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() @@ -598,8 +607,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { self.try_block_depth_stack.pop(); } - fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 0b2313cb7eeb9..e4c91b7efd2bd 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -2,13 +2,11 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, is_path_lang_item, - path_to_local, -}; +use clippy_utils::{expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const}; use rustc_ast::Mutability; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -321,7 +319,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option (true, Ordering::Less), _ => return None, }; - if let Some(id) = path_to_local(l) { + if let Some(id) = l.res_local_id() { if let Some(c) = ConstEvalCtxt::new(cx).eval(r) { return Some(RangeBounds { val: c, @@ -333,7 +331,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option( // Check if `expr` is the argument of a compiler-generated `IntoIter::into_iter(expr)` if let ExprKind::Call(func, [arg]) = parent_expr.kind && arg.hir_id == use_ctxt.child_id - && is_path_lang_item(cx, func, LangItem::IntoIterIntoIter) + && func.opt_lang_path() == Some(LangItem::IntoIterIntoIter) { return true; } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index de6766cbe94aa..13c1b10bf2e8c 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -100,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) || fn_name == Some(sym::to_owned_method) - || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String)); + || (fn_name == Some(sym::to_string_method) && arg_ty.is_lang_item(cx, LangItem::String)); let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string)); diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index a358eff2ce554..f2cf809d6012a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -80,7 +81,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind && addressee.span.ctxt() == ctxt && let ExprKind::Index(indexed, range, _) = addressee.kind - && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) + && cx + .typeck_results() + .expr_ty_adjusted(range) + .is_lang_item(cx, LangItem::RangeFull) { let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 89d945161f629..d1fc228f4b352 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -2,9 +2,10 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths; use clippy_utils::paths::PathLookup; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::SpanRangeExt; -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}; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Call(fun, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, fun) + && let Some(def_id) = fun.res(cx).opt_def_id() && let Some(regex_kind) = self.definitions.get(&def_id) { if let Some(&(loop_item_id, loop_span)) = self.loop_stack.last() diff --git a/src/tools/clippy/clippy_lints/src/replace_box.rs b/src/tools/clippy/clippy_lints/src/replace_box.rs new file mode 100644 index 0000000000000..4bbd1803a78d2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/replace_box.rs @@ -0,0 +1,111 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_default_equivalent_call, local_is_initialized}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects assignments of `Default::default()` or `Box::new(value)` + /// to a place of type `Box`. + /// + /// ### Why is this bad? + /// This incurs an extra heap allocation compared to assigning the boxed + /// storage. + /// + /// ### Example + /// ```no_run + /// let mut b = Box::new(1u32); + /// b = Default::default(); + /// ``` + /// Use instead: + /// ```no_run + /// let mut b = Box::new(1u32); + /// *b = Default::default(); + /// ``` + #[clippy::version = "1.92.0"] + pub REPLACE_BOX, + perf, + "assigning a newly created box to `Box` is inefficient" +} +declare_lint_pass!(ReplaceBox => [REPLACE_BOX]); + +impl LateLintPass<'_> for ReplaceBox { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + && let lhs_ty = cx.typeck_results().expr_ty(lhs) + // No diagnostic for late-initialized locals + && lhs.res_local_id().is_none_or(|local| local_is_initialized(cx, local)) + && let Some(inner_ty) = lhs_ty.boxed_ty() + { + if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, inner_ty, default_trait_id, &[]) + && is_default_call(cx, rhs) + { + span_lint_and_then( + cx, + REPLACE_BOX, + expr.span, + "creating a new box with default content", + |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = Default::default()", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref() + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with default instead", + suggestion, + app, + ); + }, + ); + } + + if inner_ty.is_sized(cx.tcx, cx.typing_env()) + && let Some(rhs_inner) = get_box_new_payload(cx, rhs) + { + span_lint_and_then(cx, REPLACE_BOX, expr.span, "creating a new box", |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = {}", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref(), + Sugg::hir_with_context(cx, rhs_inner, expr.span.ctxt(), "_", &mut app), + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with inner value instead", + suggestion, + app, + ); + }); + } + } + } +} + +fn is_default_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Call(func, _args) if is_default_equivalent_call(cx, func, Some(expr))) +} + +fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call(box_new, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind + && seg.ident.name == sym::new + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) + { + Some(arg) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs index 51adbbcd58bdb..ce86a9caac754 100644 --- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; -use clippy_utils::{is_from_proc_macro, path_to_local_id, sym}; +use clippy_utils::{is_from_proc_macro, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind}; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::reserve && !is_from_proc_macro(cx, expr) { diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs index e2002fb36e5a6..f54a26a77620a 100644 --- a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_cfg}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, PatKind, StmtKind}; @@ -21,7 +22,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) && cx.tcx.hir_attrs(local.hir_id).is_empty() && let Some(initexpr) = &local.init && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) + && retexpr.res_local_id() == Some(local_id) && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) && !initexpr.span.in_external_macro(cx.sess().source_map()) && !retexpr.span.in_external_macro(cx.sess().source_map()) diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs index c05038cd1e50e..c47a3ef21e869 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{is_from_proc_macro, is_inside_let_else}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; @@ -19,7 +20,7 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + && maybe_constr.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index ff6e6ef214b5b..688da33a17778 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr; use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -103,7 +103,7 @@ fn try_parse_op_call<'tcx>( let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if value.span.eq_ctxt(expr.span) && path.ident.name == symbol { for sym in &[sym::HashSet, sym::BTreeSet] { - if is_type_diagnostic_item(cx, receiver_ty, *sym) { + if receiver_ty.is_diag_item(cx, *sym) { return Some((OpExpr { receiver, value, span }, *sym)); } } diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 14399867f3181..7fdea6bec5100 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use rustc_data_structures::fx::FxHashMap; @@ -202,7 +202,7 @@ pub fn is_local_used_except<'tcx>( for_each_expr(cx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) - } else if path_to_local_id(e, id) { + } else if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 9110f684bd10c..c4604fb1558df 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary, sym}; +use clippy_utils::{expr_or_init, get_attr, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -276,7 +277,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) && { - if let Some(local_hir_id) = path_to_local(expr) { + if let Some(local_hir_id) = expr.res_local_id() { local_hir_id == hir_id } else { true @@ -301,7 +302,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { modify_apa_params(&mut apa); let _ = self.ap.apas.insert(hir_id, apa); } else { - let Some(hir_id) = path_to_local(expr) else { + let Some(hir_id) = expr.res_local_id() else { return; }; let Some(apa) = self.ap.apas.get_mut(&hir_id) else { @@ -319,7 +320,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } }, hir::StmtKind::Semi(semi_expr) => { - if has_drop(semi_expr, apa.first_bind_ident, self.cx) { + if has_drop(self.cx, semi_expr, apa.first_bind_ident) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; @@ -416,11 +417,11 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: Option, lcx: &LateContext<'_>) -> bool { +fn has_drop(cx: &LateContext<'_>, expr: &hir::Expr<'_>, first_bind_ident: Option) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res - && lcx.tcx.is_diagnostic_item(sym::mem_drop, did) + && cx.tcx.is_diagnostic_item(sym::mem_drop, did) { let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs index 8c34da0d14a4d..595c75acf031f 100644 --- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -40,8 +40,8 @@ declare_clippy_lint! { declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); impl EarlyLintPass for SingleCharLifetimeNames { - fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { - if param.ident.span.in_external_macro(ctx.sess().source_map()) { + fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &GenericParam) { + if param.ident.span.in_external_macro(cx.sess().source_map()) { return; } @@ -51,7 +51,7 @@ impl EarlyLintPass for SingleCharLifetimeNames { { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( - ctx, + cx, SINGLE_CHAR_LIFETIME_NAMES, param.ident.span, "single-character lifetime names are likely uninformative", diff --git a/src/tools/clippy/clippy_lints/src/single_option_map.rs b/src/tools/clippy/clippy_lints/src/single_option_map.rs index cc497c97a4725..4556d287711fe 100644 --- a/src/tools/clippy/clippy_lints/src/single_option_map.rs +++ b/src/tools/clippy/clippy_lints/src/single_option_map.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_res, peel_blocks}; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; @@ -55,10 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { if let ExprKind::MethodCall(method_name, callee, args, _span) = func_body.kind && method_name.ident.name == sym::map && let callee_type = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_type, sym::Option) + && callee_type.is_diag_item(cx, sym::Option) && let ExprKind::Path(_path) = callee.kind - && let Res::Local(_id) = path_res(cx, callee) - && matches!(path_res(cx, callee), Res::Local(_id)) + && matches!(callee.basic_res(), Res::Local(_)) && !matches!(args[0].kind, ExprKind::Path(_)) { if let ExprKind::Closure(closure) = args[0].kind { @@ -71,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { } else if let ExprKind::MethodCall(_segment, receiver, method_args, _span) = value.kind && matches!(receiver.kind, ExprKind::Path(_)) && method_args.iter().all(|arg| matches!(arg.kind, ExprKind::Path(_))) - && method_args.iter().all(|arg| matches!(path_res(cx, arg), Res::Local(_))) + && method_args.iter().all(|arg| matches!(arg.basic_res(), Res::Local(_))) { return; } diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs index 606e852aae9e3..bf304ebcfdc05 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,8 +57,7 @@ declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); impl LateLintPass<'_> for SizeOfRef { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, path) - && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && path.basic_res().is_diag_item(cx, sym::mem_size_of_val) && let arg_ty = cx.typeck_results().expr_ty(arg) && peel_and_count_ty_refs(arg_ty).1 > 1 { diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index f497d0700b8eb..b25fa0905feb8 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -1,10 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; 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, sym, -}; +use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, 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}; @@ -102,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)` if let ExprKind::Assign(left, right, _) = expr.kind - && let Some(local_id) = path_to_local(left) + && let Some(local_id) = left.res_local_id() && let Some(size_expr) = Self::as_vec_initializer(cx, right) { let vi = VecAllocation { @@ -149,10 +147,10 @@ impl SlowVectorInit { } if let ExprKind::Call(func, [len_expr]) = expr.kind - && is_path_diagnostic_item(cx, func, sym::vec_with_capacity) + && func.ty_rel_def(cx).is_diag_item(cx, sym::vec_with_capacity) { Some(InitializedSize::Initialized(len_expr)) - } else if matches!(expr.kind, ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::vec_new)) { + } else if matches!(expr.kind, ExprKind::Call(func, []) if func.ty_rel_def(cx).is_diag_item(cx, sym::vec_new)) { Some(InitializedSize::Uninitialized) } else { None @@ -246,7 +244,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { 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) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { @@ -258,7 +256,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'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) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) @@ -301,7 +299,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind - && is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat) + && fn_expr.basic_res().is_diag_item(self.cx, sym::iter_repeat) && is_integer_literal(repeat_arg, 0) { true diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index f63e6b3087b9e..e5347bf3e8f07 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -5,9 +5,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, str_literal_to_char_literal}; +use clippy_utils::sym; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{path_to_local_id, sym}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -146,12 +147,12 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { - if path_to_local_id(left, binding) + if left.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, right) { set_char_spans.push(span); ControlFlow::Continue(Descend::No) - } else if path_to_local_id(right, binding) + } else if right.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, left) { set_char_spans.push(span); @@ -164,7 +165,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< ExprKind::Match(match_value, [arm, _], _) => { if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none() || arm.guard.is_some() - || !path_to_local_id(match_value, binding) + || match_value.res_local_id() != Some(binding) { return ControlFlow::Break(()); } diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 57d5900b045ea..47306949a6990 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; 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, sym, + SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -188,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx, _) => { let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs(); - if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) { + if e_ty.is_str() || e_ty.is_lang_item(cx, LangItem::String) { span_lint( cx, STRING_SLICE, @@ -203,7 +202,10 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { @@ -253,7 +255,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let ExprKind::Call(fun, [bytes_arg]) = e.kind // Find `std::str::converts::from_utf8` or `std::primitive::str::from_utf8` && let Some(sym::str_from_utf8 | sym::str_inherent_from_utf8) = - path_def_id(cx, fun).and_then(|id| cx.tcx.get_diagnostic_name(id)) + fun.res(cx).opt_diag_name(cx) // Find string::as_bytes && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index 33856c750d7e9..58d692db5029d 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; @@ -61,9 +61,9 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); let mut app = Applicability::MachineApplicable; let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; - let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { + let method_name = if ty.is_diag_item(cx, sym::cstring_type) { "as_bytes" - } else if is_type_lang_item(cx, ty, LangItem::CStr) { + } else if ty.is_lang_item(cx, LangItem::CStr) { "to_bytes" } else { return; diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 76ab3cdae22ef..c3cb2c09752fe 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for Swap { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn generate_swap_warning<'tcx>( block: &'tcx Block<'tcx>, cx: &LateContext<'tcx>, @@ -110,8 +110,8 @@ fn generate_swap_warning<'tcx>( if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_diagnostic_item(cx, ty, sym::VecDeque) + || ty.is_diag_item(cx, sym::Vec) + || ty.is_diag_item(cx, sym::VecDeque) { let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); @@ -361,7 +361,8 @@ impl<'tcx> IndexBinding<'_, 'tcx> { // - Variable declaration is outside the suggestion span // - Variable is not used as an index or elsewhere later if !self.suggest_span.contains(init.span) - || path_to_local(expr) + || expr + .res_local_id() .is_some_and(|hir_id| !self.suggest_span.contains(self.cx.tcx.hir_span(hir_id))) || !self.is_used_other_than_swapping(first_segment.ident) { diff --git a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs index ff196355a2e37..339c97d575aee 100644 --- a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; @@ -41,8 +41,7 @@ declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]); impl LateLintPass<'_> for SwapPtrToRef { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind - && let Some(fn_id) = path_def_id(cx, fn_expr) - && cx.tcx.is_diagnostic_item(sym::mem_swap, fn_id) + && fn_expr.basic_res().is_diag_item(cx, sym::mem_swap) && let ctxt = e.span.ctxt() && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt) && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt) diff --git a/src/tools/clippy/clippy_lints/src/time_subtraction.rs b/src/tools/clippy/clippy_lints/src/time_subtraction.rs index fde8c3d9a9a76..dbd4ec77fd5f9 100644 --- a/src/tools/clippy/clippy_lints/src/time_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/time_subtraction.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_path_diagnostic_item, ty}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -96,16 +96,16 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { let lhs_ty = typeck.expr_ty(lhs); let rhs_ty = typeck.expr_ty(rhs); - if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + if lhs_ty.is_diag_item(cx, sym::Instant) { // Instant::now() - instant if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && rhs_ty.is_diag_item(cx, sym::Instant) && let Some(sugg) = Sugg::hir_opt(cx, rhs) { print_manual_instant_elapsed_sugg(cx, expr, sugg); } // instant - duration - else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + else if rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -122,8 +122,8 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } - } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + } else if lhs_ty.is_diag_item(cx, sym::Duration) + && rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -146,7 +146,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind - && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) + && cx.ty_based_def(fn_expr).is_diag_item(cx, sym::instant_now) { true } else { @@ -170,7 +170,7 @@ fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { /// Returns true if the type is Duration or Instant fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) + ty.is_diag_item(cx, sym::Duration) || ty.is_diag_item(cx, sym::Instant) } fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { @@ -194,7 +194,7 @@ fn print_unchecked_duration_subtraction_sugg( let typeck = cx.typeck_results(); let left_ty = typeck.expr_ty(left_expr); - let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + let lint_msg = if left_ty.is_diag_item(cx, sym::Instant) { "unchecked subtraction of a 'Duration' from an 'Instant'" } else { "unchecked subtraction between 'Duration' values" diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index 3e847543e1c16..71c4172c98528 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_in_const_context, is_path_diagnostic_item, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if is_path_diagnostic_item(cx, to_digits_call, sym::char_to_digit) { + if to_digits_call.res(cx).is_diag_item(cx, sym::char_to_digit) { Some((false, char_arg, radix_arg)) } else { None diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 9182a55081f40..352b8526b0217 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -238,7 +238,7 @@ impl TraitBounds { } } - #[allow(clippy::mutable_key_type)] + #[expect(clippy::mutable_key_type)] fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) { struct SpanlessTy<'cx, 'tcx> { ty: &'tcx Ty<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs index 7acf3be51fb7e..e109b1c50e227 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -40,7 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t }, // Catching: // `std::mem::transmute(std::ptr::null::())` - ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { + ExprKind::Call(func1, []) if func1.basic_res().is_diag_item(cx, sym::ptr_null) => { lint_expr(cx, expr); true }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs index 544014bd32b30..1a6262f2ff767 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -35,7 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // `std::mem::transmute(std::ptr::null::())` if let ExprKind::Call(func1, []) = arg.kind - && is_path_diagnostic_item(cx, func1, sym::ptr_null) + && func1.basic_res().is_diag_item(cx, sym::ptr_null) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 3c21d194b81d5..5d0945bece553 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{is_from_proc_macro, path_to_local}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind, Node, PatKind}; @@ -152,7 +153,7 @@ fn all_bindings_are_for_conv<'tcx>( locals: &[&Expr<'_>], kind: ToType, ) -> bool { - let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::>>() else { + let Some(locals) = locals.iter().map(|e| e.res_local_id()).collect::>>() else { return false; }; let local_parents = locals.iter().map(|l| cx.tcx.parent_hir_node(*l)).collect::>(); diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs index 24fe4e08a5b44..985057cd6734b 100644 --- a/src/tools/clippy/clippy_lints/src/types/box_collection.rs +++ b/src/tools/clippy/clippy_lints/src/types/box_collection.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -33,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let param = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, param)?; + let id = param.basic_res().opt_def_id()?; cx.tcx .get_diagnostic_name(id) .filter(|&name| { diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 515be5adeed0b..ccb027f77bf5f 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -693,7 +693,7 @@ impl Types { } } -#[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] +#[expect(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, diff --git a/src/tools/clippy/clippy_lints/src/types/option_option.rs b/src/tools/clippy/clippy_lints/src/types/option_option.rs index d12d14f2b1413..10df007f2a13d 100644 --- a/src/tools/clippy/clippy_lints/src/types/option_option.rs +++ b/src/tools/clippy/clippy_lints/src/types/option_option.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Option, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && path_def_id(cx, arg) == Some(def_id) + && arg.basic_res().opt_def_id() == Some(def_id) { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/types/owned_cow.rs b/src/tools/clippy/clippy_lints/src/types/owned_cow.rs index 8933994d18558..0eef373be04ea 100644 --- a/src/tools/clippy/clippy_lints/src/types/owned_cow.rs +++ b/src/tools/clippy/clippy_lints/src/types/owned_cow.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -30,10 +31,10 @@ pub(super) fn check(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, def_id: DefId) } fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String)> { - if clippy_utils::is_path_lang_item(cx, cty, hir::LangItem::String) { + if cty.basic_res().is_lang_item(cx, hir::LangItem::String) { return Some((cty.span, "str".into())); } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::Vec) { + if cty.basic_res().is_diag_item(cx, sym::Vec) { return if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = cty.kind && let [.., last_seg] = path.segments && let Some(args) = last_seg.args @@ -45,7 +46,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) None }; } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::cstring_type) { + if cty.basic_res().is_diag_item(cx, sym::cstring_type) { return Some(( cty.span, (if clippy_utils::is_no_std_crate(cx) { @@ -58,7 +59,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) } // Neither OsString nor PathBuf are available outside std for (diag, repl) in [(sym::OsString, "std::ffi::OsStr"), (sym::PathBuf, "std::path::Path")] { - if clippy_utils::is_path_diagnostic_item(cx, cty, diag) { + if cty.basic_res().is_diag_item(cx, diag) { return Some((cty.span, repl.into())); } } diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs index c4fd0fbf87a90..46d9febb187fc 100644 --- a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs +++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -28,8 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -70,8 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -106,7 +105,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { let ty = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, ty)?; + let id = ty.basic_res().opt_def_id()?; let path = match cx.tcx.get_diagnostic_name(id) { Some(sym::OsString) => "std::ffi::OsStr", Some(sym::PathBuf) => "std::path::Path", diff --git a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs index 7b13debc01ef8..e3d536822d5f8 100644 --- a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,8 +11,7 @@ use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && let Some(id) = path_def_id(cx, arg) - && cx.tcx.is_diagnostic_item(sym::Mutex, id) + && arg.basic_res().is_diag_item(cx, sym::Mutex) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, RC_MUTEX, hir_ty.span, "usage of `Rc>`", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs index 0ba51daf027d1..dbae2cc335165 100644 --- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs +++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -40,7 +41,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; + let Some(id) = ty.basic_res().opt_def_id() else { + return false; + }; let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) { Some(sym::Arc) => ("Arc", ty), Some(sym::Rc) => ("Rc", ty), diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index e843e169113e7..e6663182fec53 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{expr_or_init, fn_def_id_with_node_args}; use rustc_ast::BinOpKind; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -317,7 +318,7 @@ where if let ExprKind::Call(f, _) = expr.kind && let ExprKind::Path(qpath) = f.kind && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) - && let Some(method_def_id) = path_def_id(self.cx, f) + && let Some(method_def_id) = f.res(self.cx).opt_def_id() && let Some(trait_def_id) = self.cx.tcx.trait_of_assoc(method_def_id) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 751e9b0034277..9afa9d65c2613 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -7,8 +7,8 @@ use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{Descend, for_each_expr}; use hir::HirId; -use rustc_hir as hir; -use rustc_hir::{Block, BlockCheckMode, Impl, ItemKind, Node, UnsafeSource}; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, Block, BlockCheckMode, FnSig, Impl, ItemKind, Node, UnsafeSource}; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { && !block.span.in_external_macro(cx.tcx.sess.source_map()) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) && !is_unsafe_from_proc_macro(cx, block.span) - && !block_has_safety_comment(cx, block.span) + && !block_has_safety_comment(cx, block.span, self.accept_comment_above_attributes) && !block_parents_have_safety_comment( self.accept_comment_above_statement, self.accept_comment_above_attributes, @@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if let Some(tail) = block.expr && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) && !tail.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = + && let HasSafetyComment::Yes(pos, _) = stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { @@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = + && let HasSafetyComment::Yes(pos, _) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { @@ -191,8 +191,12 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { let mk_spans = |pos: BytePos| { let source_map = cx.tcx.sess.source_map(); - let span = Span::new(pos, pos, SyntaxContext::root(), None); - let help_span = source_map.span_extend_to_next_char(span, '\n', true); + let help_span = Span::new( + pos, + pos + BytePos(u32::try_from("SAFETY:".len()).unwrap()), + SyntaxContext::root(), + None, + ); let span = if source_map.is_multiline(item.span) { source_map.span_until_char(item.span, '\n') } else { @@ -201,16 +205,16 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { (span, help_span) }; - let item_has_safety_comment = item_has_safety_comment(cx, item); + let item_has_safety_comment = item_has_safety_comment(cx, item, self.accept_comment_above_attributes); match item_has_safety_comment { - HasSafetyComment::Yes(pos) => check_has_safety_comment(cx, item, mk_spans(pos)), + HasSafetyComment::Yes(pos, is_doc) => check_has_safety_comment(cx, item, mk_spans(pos), is_doc), HasSafetyComment::No => check_has_no_safety_comment(cx, item), HasSafetyComment::Maybe => {}, } } } -fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span)) { +fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span), is_doc: bool) { match &item.kind { ItemKind::Impl(Impl { of_trait: Some(of_trait), @@ -252,6 +256,40 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h } } }, + // Unsafe functions with a SAFETY comment are suggested to change it to a `# Safety` comment + ItemKind::Fn { + sig: FnSig { header, .. }, + .. + } if header.is_unsafe() => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + span_lint_and_then( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + format!( + "{} has unnecessary safety comment", + cx.tcx.def_descr(item.owner_id.to_def_id()), + ), + |diag| { + if is_doc { + // If it's already within a doc comment, we try to suggest the change + + diag.span_suggestion( + help_span, + "consider changing it to a `# Safety` section", + "# Safety", + Applicability::MachineApplicable, + ); + } else { + diag.span_help( + help_span, + "consider changing the `safety` comment for a `# Safety` doc comment", + ); + } + }, + ); + } + }, // Aside from unsafe impls and consts/statics with an unsafe block, items in general // do not have safety invariants that need to be documented, so lint those. _ => { @@ -272,6 +310,7 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h }, } } + fn check_has_no_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(of_trait), @@ -407,21 +446,21 @@ fn block_parents_have_safety_comment( cx: &LateContext<'_>, id: HirId, ) -> bool { - let (span, hir_id) = match cx.tcx.parent_hir_node(id) { - Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner, + let span = match cx.tcx.parent_hir_node(id) { + Node::Expr(expr) if let Some((span, _)) = find_unsafe_block_parent_in_expr(cx, expr) => span, Node::Stmt(hir::Stmt { kind: - hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) - | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) - | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), + hir::StmtKind::Let(hir::LetStmt { span, .. }) + | hir::StmtKind::Expr(hir::Expr { span, .. }) + | hir::StmtKind::Semi(hir::Expr { span, .. }), .. }) - | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), + | Node::LetStmt(hir::LetStmt { span, .. }) => *span, - node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node) && is_const_or_static(&node) => { - (span, hir_id) + span }, _ => return false, @@ -429,24 +468,7 @@ fn block_parents_have_safety_comment( // if unsafe block is part of a let/const/static statement, // and accept_comment_above_statement is set to true // we accept the safety comment in the line the precedes this statement. - accept_comment_above_statement - && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) -} - -/// Extends `span` to also include its attributes, then checks if that span has a safety comment. -fn span_with_attrs_has_safety_comment( - cx: &LateContext<'_>, - span: Span, - hir_id: HirId, - accept_comment_above_attributes: bool, -) -> bool { - let span = if accept_comment_above_attributes { - include_attrs_in_span(cx, hir_id, span) - } else { - span - }; - - span_has_safety_comment(cx, span) + accept_comment_above_statement && span_has_safety_comment(cx, span, accept_comment_above_attributes) } /// Checks if an expression is "branchy", e.g. loop, match/if/etc. @@ -458,7 +480,7 @@ fn is_branchy(expr: &hir::Expr<'_>) -> bool { } /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -468,30 +490,25 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // attributes and doc comments. matches!( - span_from_macro_expansion_has_safety_comment(cx, span), - HasSafetyComment::Yes(_) - ) || span_has_safety_comment(cx, span) -} - -fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { - span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { - if attr.is_doc_comment().is_some() { - return acc; - } - acc.to(attr.span()) - })) + span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes), + HasSafetyComment::Yes(_, _) + ) || span_has_safety_comment(cx, span, accept_comment_above_attributes) } +#[derive(Debug)] enum HasSafetyComment { - Yes(BytePos), + Yes(BytePos, bool), No, Maybe, } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { - match span_from_macro_expansion_has_safety_comment(cx, item.span) { +fn item_has_safety_comment( + cx: &LateContext<'_>, + item: &hir::Item<'_>, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, item.span, accept_comment_above_attributes) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, } @@ -536,29 +553,26 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf return if comment_start_line.line >= unsafe_line.line { HasSafetyComment::No } else { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines() [(comment_start_line.line + usize::from(!include_first_line_of_file))..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) }; } HasSafetyComment::Maybe } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] fn stmt_has_safety_comment( cx: &LateContext<'_>, span: Span, hir_id: HirId, accept_comment_above_attributes: bool, ) -> HasSafetyComment { - match span_from_macro_expansion_has_safety_comment(cx, span) { + match span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, } @@ -572,13 +586,6 @@ fn stmt_has_safety_comment( _ => return HasSafetyComment::Maybe, }; - // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib - // } - let mut span = span; - if accept_comment_above_attributes { - span = include_attrs_in_span(cx, hir_id, span); - } - let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) @@ -589,14 +596,12 @@ fn stmt_has_safety_comment( return if comment_start_line.line >= unsafe_line.line { HasSafetyComment::No } else { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) }; } HasSafetyComment::Maybe @@ -649,7 +654,11 @@ fn comment_start_before_item_in_mod( }) } -fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { +fn span_from_macro_expansion_has_safety_comment( + cx: &LateContext<'_>, + span: Span, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { @@ -665,14 +674,12 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && let Some(src) = unsafe_line.sf.src.as_deref() { if macro_line.line < unsafe_line.line { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) } else { HasSafetyComment::No } @@ -715,7 +722,7 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option { None } -fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt.is_root() @@ -731,12 +738,15 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } // ^-------------^ body_line.line < unsafe_line.line - && text_has_safety_comment( - src, - &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, + && matches!( + text_has_safety_comment( + src, + &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + accept_comment_above_attributes, + ), + HasSafetyComment::Yes(..) ) - .is_some() } else { // Problem getting source text. Pretend a comment was found. true @@ -747,7 +757,15 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } /// Checks if the given text has a safety comment for the immediately proceeding line. -fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos: BytePos) -> Option { +/// +/// If `accept_comment_above_attributes` is true, it will ignore attributes inbetween blocks of +/// comments +fn text_has_safety_comment( + src: &str, + line_starts: &[RelativeBytePos], + start_pos: BytePos, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { let mut lines = line_starts .array_windows::<2>() .rev() @@ -758,9 +776,12 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos let trimmed = text.trim_start(); Some((start + (text.len() - trimmed.len()), trimmed)) }) - .filter(|(_, text)| !text.is_empty()); + .filter(|(_, text)| !(text.is_empty() || (accept_comment_above_attributes && is_attribute(text)))); + + let Some((line_start, line)) = lines.next() else { + return HasSafetyComment::No; + }; - let (line_start, line) = lines.next()?; let mut in_codeblock = false; // Check for a sequence of line comments. if line.starts_with("//") { @@ -773,12 +794,17 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos in_codeblock = !in_codeblock; } - if line.to_ascii_uppercase().contains("SAFETY:") && !in_codeblock { - return Some(start_pos + BytePos(u32::try_from(line_start).unwrap())); + if !in_codeblock && let Some(safety_pos) = line.to_ascii_uppercase().find("SAFETY:") { + return HasSafetyComment::Yes( + start_pos + + BytePos(u32::try_from(line_start).unwrap()) + + BytePos(u32::try_from(safety_pos).unwrap()), + line.starts_with("///"), + ); } match lines.next() { Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s), - _ => return None, + _ => return HasSafetyComment::No, } } } @@ -789,19 +815,30 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos if line.starts_with("/*") { let src = &src[line_start..line_starts.last().unwrap().to_usize()]; let mut tokens = tokenize(src, FrontmatterAllowed::No); - return (src[..tokens.next().unwrap().len as usize] - .to_ascii_uppercase() - .contains("SAFETY:") - && tokens.all(|t| t.kind == TokenKind::Whitespace)) - .then_some(start_pos + BytePos(u32::try_from(line_start).unwrap())); + let a = tokens.next(); + if let Some(safety_pos) = src[..a.unwrap().len as usize].to_ascii_uppercase().find("SAFETY:") + && tokens.all(|t| t.kind == TokenKind::Whitespace) + { + return HasSafetyComment::Yes( + start_pos + + BytePos(u32::try_from(line_start).unwrap()) + + BytePos(u32::try_from(safety_pos).unwrap()), + line.starts_with("/**"), + ); + } + return HasSafetyComment::No; } match lines.next() { Some(x) => (line_start, line) = x, - None => return None, + None => return HasSafetyComment::No, } } } +fn is_attribute(text: &str) -> bool { + (text.starts_with("#[") || text.starts_with("#![")) && text.trim_end().ends_with(']') +} + fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> { match node { Node::Item(item) => Some((item.span, item.owner_id.into())), diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 51116b5eba9e1..df06982904b37 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; 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, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::ty::is_uninit_value_valid_for_ty; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, 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; @@ -139,7 +140,7 @@ enum VecLocation<'tcx> { impl<'tcx> VecLocation<'tcx> { pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { match self { - VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id), + VecLocation::Local(hir_id) => expr.res_local_id() == Some(hir_id), VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), } } @@ -183,7 +184,10 @@ 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) + cx.typeck_results() + .expr_ty(self_expr) + .peel_refs() + .is_diag_item(cx, sym::Vec) && path.ident.name == sym::reserve } @@ -205,10 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt match expr.kind { 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 == sym::set_len - && !is_integer_literal(arg, 0) - { + if self_type.is_diag_item(cx, sym::Vec) && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) } else { None diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs index 9f107fbeec03a..2603884440d16 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound { return; }; - if path_res(cx, inner_hir_ty) != Res::PrimTy(PrimTy::Str) { + if !matches!(inner_hir_ty.basic_res(), Res::PrimTy(PrimTy::Str)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs index d3700d05b014b..94b1a34455ff0 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { return; } if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind - && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)) + && let Some(sym::Option | sym::Result) = cx.typeck_results().expr_ty(recv).opt_diag_name(cx) { let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind && let hir::ExprKind::Path(constructor_path) = constructor.kind diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs index 28f4884fa3110..0388450c9f7e2 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let LitKind::Str(symbol, _) = spanned.node && symbol.is_empty() && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr) - && is_type_lang_item(cx, inner_expr_type, LangItem::String) + && inner_expr_type.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs index 5792b6b3178d7..51596859e4c7b 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; -use clippy_utils::{get_parent_expr, is_mutable, path_to_local}; +use clippy_utils::{get_parent_expr, is_mutable}; use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -162,7 +163,7 @@ fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) && parent_ty.is_any_ptr() { - if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && expr_b.res_local_id().is_some() { // When the type implements `Copy`, a reference to the new struct works on the // copy. Using the original would borrow it. return false; diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 849c0b438a5a0..29747cf0e447a 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -2,9 +2,10 @@ use std::borrow::Cow; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; +use clippy_utils::{contains_return, return_ty}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::intravisit::FnKind; @@ -122,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion() // Check if a function call. && let ExprKind::Call(func, [arg]) = ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, func), lang_item) + && func.res(cx).ctor_parent(cx).is_lang_item(cx, lang_item) // Make sure the function argument does not contain a return expression. && !contains_return(arg) { diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index af3ad4566c46a..cff798f7a89a0 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,7 @@ 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, paths, peel_blocks, sym}; +use clippy_utils::res::MaybeDef; +use clippy_utils::{paths, peel_blocks, sym}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -136,7 +137,10 @@ fn non_consuming_err_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { } if let PatKind::TupleStruct(ref path, [inner_pat], _) = arm.pat.kind { - return is_res_lang_ctor(cx, cx.qpath_res(path, inner_pat.hir_id), hir::LangItem::ResultErr); + return cx + .qpath_res(path, inner_pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, hir::LangItem::ResultErr); } false @@ -203,7 +207,7 @@ fn is_ok_wild_or_dotdot_pattern<'a>(cx: &LateContext<'a>, pat: &hir::Pat<'a>) -> if let PatKind::TupleStruct(ref path, inner_pat, _) = pat.kind // we check against Result::Ok to avoid linting on Err(_) or something else. - && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk) + && cx.qpath_res(path, pat.hir_id).ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { if matches!(inner_pat, []) { return true; diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index 5224b62e9fc71..668663e4cf794 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; -use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::ty::peel_and_count_ty_refs; +use clippy_utils::{fn_def_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; @@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { // Don't lint `Peekable`s returned from a block if let Some(expr) = block.expr && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr)) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { return; } @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -116,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> ControlFlow<()> { - if path_to_local_id(ex, self.expected_hir_id) { + if ex.res_local_id() == Some(self.expected_hir_id) { for (_, node) in self.cx.tcx.hir_parent_iter(ex.hir_id) { match node { Node::Expr(expr) => { @@ -160,7 +161,11 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { // foo.some_method() excluding Iterator methods if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) - && !is_trait_method(self.cx, expr, sym::Iterator) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } @@ -212,7 +217,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { true } else { diff --git a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs index f5ed10fb76094..fe323a0b7b0c9 100644 --- a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -37,7 +37,7 @@ impl LateLintPass<'_> for UnusedResultOk { if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result.ok(, _) && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && !stmt.span.in_external_macro(cx.sess().source_map()) { let ctxt = expr.span.ctxt(); diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index aee8028a75de7..99201a1ca215f 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -1,9 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::ty::get_type_diagnostic_name; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, 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, UnOp}; @@ -147,7 +147,7 @@ fn collect_unwrap_info<'tcx>( is_entire_condition: bool, ) -> Vec> { fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { - match (get_type_diagnostic_name(cx, ty)?, method_name) { + match (ty.opt_diag_name(cx)?, method_name) { (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), @@ -169,7 +169,7 @@ fn collect_unwrap_info<'tcx>( }, ExprKind::Unary(UnOp::Not, expr) => collect_unwrap_info(cx, if_expr, expr, branch, !invert, false), ExprKind::MethodCall(method_name, receiver, [], _) - if let Some(local_id) = path_to_local(receiver) + if let Some(local_id) = receiver.res_local_id() && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => @@ -318,7 +318,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { // find `unwrap[_err]()` or `expect("...")` calls: 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) + && let Some(id) = self_arg.res_local_id() && 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() diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 9d5be922f43f8..f46dedf1261e9 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -58,6 +58,7 @@ declare_clippy_lint! { pub struct UseSelf { msrv: Msrv, stack: Vec, + recursive_self_in_type_definitions: bool, } impl UseSelf { @@ -65,6 +66,7 @@ impl UseSelf { Self { msrv: conf.msrv, stack: Vec::new(), + recursive_self_in_type_definitions: conf.recursive_self_in_type_definitions, } } } @@ -84,10 +86,10 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - // We push the self types of `impl`s on a stack here. Only the top type on the stack is - // relevant for linting, since this is the self type of the `impl` we're currently in. To - // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that - // we're in an `impl` or nested item, that we don't want to lint + // We push the self types of items on a stack here. Only the top type on the stack is + // relevant for linting, since this is the self type of the item we're currently in. To + // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal that + // we're in an item or nested item that we don't want to lint let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args @@ -112,6 +114,15 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { impl_id: item.owner_id.def_id, types_to_skip, } + } else if let ItemKind::Struct(..) | ItemKind::Enum(..) = item.kind + && self.recursive_self_in_type_definitions + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + StackItem::Check { + impl_id: item.owner_id.def_id, + types_to_skip: FxHashSet::default(), + } } else { StackItem::NoCheck }; diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 1b137017ecb46..0cf5b9431a342 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; -use clippy_utils::{ - get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, -}; +use clippy_utils::ty::{is_copy, same_type_modulo_regions}; +use clippy_utils::{get_parent_expr, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -133,7 +132,7 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter { Some(recv) @@ -181,7 +180,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { path.ident.name, sym::map | sym::map_err | sym::map_break | sym::map_continue ) && has_eligible_receiver(cx, recv, e) - && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) + && matches!( + arg.res(cx).assoc_parent(cx).opt_diag_name(cx), + Some(sym::Into | sym::From) + ) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() && same_type_modulo_regions(from_ty, to_ty) @@ -204,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -308,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, ..) = pat.kind && ann != BindingMode::MUT @@ -364,11 +366,11 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if is_trait_method(cx, e, sym::TryInto) + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && name.ident.name == sym::try_into && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) @@ -393,7 +395,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); if name == sym::try_from_fn - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) @@ -439,13 +441,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { - if is_inherent_method_call(cx, expr) { + if cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { matches!( - get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)), + cx.typeck_results().expr_ty(recv).opt_diag_name(cx), Some(sym::Option | sym::Result | sym::ControlFlow) ) } else { - is_trait_method(cx, expr, sym::Iterator) + cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) } } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index ece29362a39f9..68e51dace2db2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,4 +1,5 @@ -use clippy_utils::{MaybePath, get_attr, higher, path_def_id, sym}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{get_attr, higher, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -205,7 +206,6 @@ struct PrintVisitor<'a, 'tcx> { first: Cell, } -#[allow(clippy::unused_self)] impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { @@ -269,16 +269,16 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { + fn qpath(&self, qpath: &Binding<&QPath<'_>>, hir_id_binding: &str, hir_id: HirId) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + } else if let Some(def_id) = self.cx.qpath_res(qpath.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()" + "let Some({def_id}) = cx.qpath_res({qpath}, {hir_id_binding}.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})"); @@ -292,14 +292,14 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } } - fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { - if let Some(id) = path_def_id(self.cx, path.value) + fn maybe_path<'p>(&self, path: &Binding>) { + if let Some(id) = path.value.res(self.cx).opt_def_id() && !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()); + chain!(self, "{path}.res(cx).is_lang_item(cx, 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})"); + chain!(self, "{path}.res(cx).is_diag_item(cx, sym::{name})"); } else { chain!( self, @@ -410,7 +410,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(field!(arm.body)); } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) { bind!(self, condition, body); @@ -672,7 +672,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath, expr); + self.qpath(qpath, &expr.name, expr.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -758,7 +758,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { let ignore = etc.is_some(); bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -772,7 +772,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, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 52b30ddce12b8..b87db836869d6 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -10,7 +10,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment, sym}; +use clippy_utils::{get_parent_expr, higher, is_in_test, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -124,7 +124,7 @@ impl UselessVec { if let Some(parent) = get_parent_expr(cx, expr) && (adjusts_to_slice(cx, expr) || matches!(parent.kind, ExprKind::Index(..)) - || is_allowed_vec_method(cx, parent)) + || is_allowed_vec_method(parent)) { ControlFlow::Continue(()) } else { @@ -304,11 +304,11 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Checks if the given expression is a method call to a `Vec` method /// that also exists on slices. If this returns true, it means that /// this expression does not actually require a `Vec` and could just work with an array. -pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +pub fn is_allowed_vec_method(e: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, _, [], _) = e.kind { matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) } else { - is_trait_method(cx, e, sym::IntoIterator) + false } } diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index 8d873536295dc..5d074208c029c 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, path_to_local_id, sym}; +use clippy_utils::{get_parent_expr, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -201,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { self.searcher = Some(VecPushSearcher { diff --git a/src/tools/clippy/clippy_lints/src/volatile_composites.rs b/src/tools/clippy/clippy_lints/src/volatile_composites.rs new file mode 100644 index 0000000000000..6402c3ef72c41 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/volatile_composites.rs @@ -0,0 +1,180 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeDef; +use clippy_utils::sym; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// + /// This lint warns when volatile load/store operations + /// (`write_volatile`/`read_volatile`) are applied to composite types. + /// + /// ### Why is this bad? + /// + /// Volatile operations are typically used with memory mapped IO devices, + /// where the precise number and ordering of load and store instructions is + /// important because they can have side effects. This is well defined for + /// primitive types like `u32`, but less well defined for structures and + /// other composite types. In practice it's implementation defined, and the + /// behavior can be rustc-version dependent. + /// + /// As a result, code should only apply `write_volatile`/`read_volatile` to + /// primitive types to be fully well-defined. + /// + /// ### Example + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// device.write_volatile(MyDevice { addr, count }); + /// } + /// } + /// ``` + /// Instead, operate on each primtive field individually: + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// (&raw mut (*device).addr).write_volatile(addr); + /// (&raw mut (*device).count).write_volatile(count); + /// } + /// } + /// ``` + #[clippy::version = "1.92.0"] + pub VOLATILE_COMPOSITES, + nursery, + "warn about volatile read/write applied to composite types" +} +declare_lint_pass!(VolatileComposites => [VOLATILE_COMPOSITES]); + +/// Zero-sized types are intrinsically safe to use volatile on since they won't +/// actually generate *any* loads or stores. But this is also used to skip zero-sized +/// fields of `#[repr(transparent)]` structures. +fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.layout_of(ty).is_ok_and(|layout| layout.is_zst()) +} + +/// A thin raw pointer or reference. +fn is_narrow_ptr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::RawPtr(inner, _) | ty::Ref(_, inner, _) => inner.has_trivial_sizedness(cx.tcx, ty::SizedTraitKind::Sized), + _ => false, + } +} + +/// Enum with some fixed representation and no data-carrying variants. +fn is_enum_repr_c<'tcx>(_cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.ty_adt_def().is_some_and(|adt_def| { + adt_def.is_enum() && adt_def.repr().inhibit_struct_field_reordering() && adt_def.is_payloadfree() + }) +} + +/// `#[repr(transparent)]` structures are also OK if the only non-zero +/// sized field contains a volatile-safe type. +fn is_struct_repr_transparent<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().transparent() + && let [fieldty] = adt_def + .all_fields() + .filter_map(|field| { + let fty = field.ty(cx.tcx, args); + if is_zero_sized_ty(cx, fty) { None } else { Some(fty) } + }) + .collect::>() + .as_slice() + { + is_volatile_safe_ty(cx, *fieldty) + } else { + false + } +} + +/// SIMD can be useful to get larger single loads/stores, though this is still +/// pretty machine-dependent. +fn is_simd_repr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, _args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().simd() + { + let (_size, simdty) = ty.simd_size_and_type(cx.tcx); + is_volatile_safe_ty(cx, simdty) + } else { + false + } +} + +/// Top-level predicate for whether a type is volatile-safe or not. +fn is_volatile_safe_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_primitive() + || is_narrow_ptr(cx, ty) + || is_zero_sized_ty(cx, ty) + || is_enum_repr_c(cx, ty) + || is_simd_repr(cx, ty) + || is_struct_repr_transparent(cx, ty) + // We can't know about a generic type, so just let it pass to avoid noise + || ty.has_non_region_param() +} + +/// Print diagnostic for volatile read/write on non-volatile-safe types. +fn report_volatile_safe<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { + if !is_volatile_safe_ty(cx, ty) { + span_lint( + cx, + VOLATILE_COMPOSITES, + expr.span, + format!("type `{ty}` is not volatile-compatible"), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for VolatileComposites { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + // Check our expr is calling a method with pattern matching + match expr.kind { + // Look for method calls to `write_volatile`/`read_volatile`, which + // apply to both raw pointers and std::ptr::NonNull. + ExprKind::MethodCall(name, self_arg, _, _) + if matches!(name.ident.name, sym::read_volatile | sym::write_volatile) => + { + let self_ty = cx.typeck_results().expr_ty(self_arg); + match self_ty.kind() { + // Raw pointers + ty::RawPtr(innerty, _) => report_volatile_safe(cx, expr, *innerty), + // std::ptr::NonNull + ty::Adt(_, args) if self_ty.is_diag_item(cx, sym::NonNull) => { + report_volatile_safe(cx, expr, args.type_at(0)); + }, + _ => (), + } + }, + + // Also plain function calls to std::ptr::{read,write}_volatile + ExprKind::Call(func, [arg_ptr, ..]) => { + if let ExprKind::Path(ref qpath) = func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_read_volatile | sym::ptr_write_volatile) + ) + && let ty::RawPtr(ptrty, _) = cx.typeck_results().expr_ty_adjusted(arg_ptr).kind() + { + report_volatile_safe(cx, expr, *ptrty); + } + }, + _ => {}, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs index cd6c11b512742..a8351690068df 100644 --- a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_indent}; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::IsSuggestable; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -72,40 +73,60 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: // check if expr is a call or has a call inside it if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); + let inner_expr_ty = cx.typeck_results().expr_ty(inner_expr); let return_type = cx.typeck_results().expr_ty(expr); let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let indent = snippet_indent(cx, expr.span).unwrap_or_default(); let vec = if is_vec { "vec!" } else { "" }; let (span, sugg) = match parent_hir_node { Node::LetStmt(l) => ( l.span, format!( - "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + "{inner_expr};\n{indent}let {var_name}: {return_type} = {vec}[];", var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), ), Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( x.span, format!( - "{inner_expr}; {var_name} = {vec}[] as {return_type}", + "{inner_expr};\n{indent}{var_name} = {vec}[] as {return_type}", var_name = snippet(cx, l.span.source_callsite(), "..") ), ), - _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + // NOTE: don't use the stmt span to avoid touching the trailing semicolon + Node::Stmt(_) => (expr.span, format!("{inner_expr};\n{indent}{vec}[] as {return_type}")), + _ => ( + expr.span, + format!( + "\ +{{ +{indent} {inner_expr}; +{indent} {vec}[] as {return_type} +{indent}}}" + ), + ), }; + let span = span.source_callsite(); span_lint_and_then( cx, ZERO_REPEAT_SIDE_EFFECTS, - span.source_callsite(), + span, "expression with side effects as the initial value in a zero-sized array initializer", |diag| { - diag.span_suggestion_verbose( - span.source_callsite(), - "consider performing the side effect separately", - sugg, - Applicability::Unspecified, - ); + if (!inner_expr_ty.is_never() || cx.tcx.features().never_type()) + && return_type.is_suggestable(cx.tcx, true) + { + diag.span_suggestion_verbose( + span, + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); + } else { + diag.help("consider performing the side effect separately"); + } }, ); } diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index f1572fd65bbff..bf133d26ed9d0 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::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 _; @@ -48,7 +49,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && !in_trait_impl(cx, hir_ty.hir_id) // We don't care about infer vars && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) - && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index a934d2094e004..0319f3e656e1b 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -1,7 +1,7 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{fn_def_id, get_enclosing_block}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind && let child_ty = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, child_ty, sym::Child) + && child_ty.is_diag_item(cx, sym::Child) { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(local) @@ -168,7 +168,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { return walk_expr(self, ex); } - if path_to_local_id(ex, self.local_id) { + if ex.res_local_id() == Some(self.local_id) { match self.cx.tcx.parent_hir_node(ex.hir_id) { Node::Stmt(Stmt { kind: StmtKind::Semi(_), diff --git a/src/tools/clippy/clippy_lints_internal/Cargo.toml b/src/tools/clippy/clippy_lints_internal/Cargo.toml index a8293a1ad395d..16b45322e30f1 100644 --- a/src/tools/clippy/clippy_lints_internal/Cargo.toml +++ b/src/tools/clippy/clippy_lints_internal/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } +itertools = "0.12" regex = { version = "1.5" } rustc-semver = "1.1" diff --git a/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs b/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs index dc1e30ab2bdd3..95bdf27b019c6 100644 --- a/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs +++ b/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs @@ -2,13 +2,18 @@ use clippy_utils::paths::{PathLookup, PathNS}; use clippy_utils::{sym, type_path, value_path}; // Paths inside rustc +pub static APPLICABILITY: PathLookup = type_path!(rustc_errors::Applicability); +pub static EARLY_CONTEXT: PathLookup = type_path!(rustc_lint::EarlyContext); 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 LATE_CONTEXT: PathLookup = type_path!(rustc_lint::LateContext); 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); +#[expect(clippy::unnecessary_def_path, reason = "for uniform checking in internal lint")] +pub static TY_CTXT: PathLookup = type_path!(rustc_middle::ty::TyCtxt); // Paths in clippy itself pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs index 43cde86504f5c..d686ba73387cb 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lib.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -41,6 +41,7 @@ mod produce_ice; mod symbols; mod unnecessary_def_path; mod unsorted_clippy_utils_paths; +mod unusual_names; use rustc_lint::{Lint, LintStore}; @@ -59,6 +60,7 @@ static LINTS: &[&Lint] = &[ symbols::SYMBOL_AS_STR, unnecessary_def_path::UNNECESSARY_DEF_PATH, unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, + unusual_names::UNUSUAL_NAMES, ]; pub fn register_lints(store: &mut LintStore) { @@ -74,4 +76,5 @@ pub fn register_lints(store: &mut LintStore) { 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())); + store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); } diff --git a/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs index 3a813b4b9a223..630843049da25 100644 --- a/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs +++ b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs @@ -25,9 +25,9 @@ declare_tool_lint! { declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, ctx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { if is_trigger_fn(fn_kind) { - ctx.sess() + cx.sess() .dcx() .span_delayed_bug(span, "Would you like some help with that?"); } diff --git a/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs index 8877f1faf0ee8..f2e8d3579d851 100644 --- a/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,7 +1,8 @@ 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 clippy_utils::peel_ref_operators; +use clippy_utils::res::MaybeQPath; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -26,7 +27,7 @@ declare_tool_lint! { /// /// Use instead: /// ```rust,ignore - /// is_type_diagnostic_item(cx, ty, sym::Vec) + /// ty.is_diag_item(cx, sym::Vec) /// ``` pub clippy::UNNECESSARY_DEF_PATH, Warn, @@ -53,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { let path: Vec = segments .iter() .map(|segment| { - if let Some(const_def_id) = path_def_id(cx, segment) + if let Some(const_def_id) = segment.res(cx).opt_def_id() && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) && let Some(value) = value.to_u32().discard_err() { diff --git a/src/tools/clippy/clippy_lints_internal/src/unusual_names.rs b/src/tools/clippy/clippy_lints_internal/src/unusual_names.rs new file mode 100644 index 0000000000000..e11a2868fb69c --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/unusual_names.rs @@ -0,0 +1,99 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths::PathLookup; +use clippy_utils::sym; +use itertools::Itertools; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, Pat, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol}; + +use crate::internal_paths::{APPLICABILITY, EARLY_CONTEXT, LATE_CONTEXT, TY_CTXT}; + +declare_tool_lint! { + /// ### What it does + /// Checks if variables of some types use the usual name. + /// + /// ### Why is this bad? + /// Restricting the identifiers used for common things in + /// Clippy sources increases consistency. + /// + /// ### Example + /// Check that an `rustc_errors::Applicability` variable is + /// named either `app` or `applicability`, and not + /// `a` or `appl`. + pub clippy::UNUSUAL_NAMES, + Warn, + "commonly used concepts should use usual same variable name.", + report_in_external_macro: true +} + +declare_lint_pass!(UnusualNames => [UNUSUAL_NAMES]); + +const USUAL_NAMES: [(&PathLookup, &str, &[Symbol]); 4] = [ + ( + &APPLICABILITY, + "rustc_errors::Applicability", + &[sym::app, sym::applicability], + ), + (&EARLY_CONTEXT, "rustc_lint::EarlyContext", &[sym::cx]), + (&LATE_CONTEXT, "rustc_lint::LateContext", &[sym::cx]), + (&TY_CTXT, "rustc_middle::ty::TyCtxt", &[sym::tcx]), +]; + +impl<'tcx> LateLintPass<'tcx> for UnusualNames { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(let_stmt) = stmt.kind + && let Some(init_expr) = let_stmt.init + { + check_pat_name_for_ty(cx, let_stmt.pat, cx.typeck_results().expr_ty(init_expr), "variable"); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _span: Span, + def_id: LocalDefId, + ) { + if matches!(kind, FnKind::Closure) { + return; + } + for (param, ty) in body + .params + .iter() + .zip(cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs()) + { + check_pat_name_for_ty(cx, param.pat, *ty, "parameter"); + } + } +} + +fn check_pat_name_for_ty(cx: &LateContext<'_>, pat: &Pat<'_>, ty: Ty<'_>, kind: &str) { + if let PatKind::Binding(_, _, ident, _) = pat.kind { + let ty = ty.peel_refs(); + for (usual_ty, ty_str, usual_names) in USUAL_NAMES { + if usual_ty.matches_ty(cx, ty) + && !usual_names.contains(&ident.name) + && ident.name != kw::SelfLower + && !ident.name.as_str().starts_with('_') + { + let usual_names = usual_names.iter().map(|name| format!("`{name}`")).join(" or "); + span_lint_and_help( + cx, + UNUSUAL_NAMES, + ident.span, + format!("unusual name for a {kind} of type `{ty_str}`"), + None, + format!("prefer using {usual_names}"), + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 1f678a6a29f08..9d12b46b9546f 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-10-06 +nightly-2025-10-16 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index ad69e6eb184e1..b01e160e22972 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -143,7 +143,7 @@ pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { } } -#[allow(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, eq_attr) { @@ -328,7 +328,7 @@ pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> b over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } -#[expect(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 948a7203402d5..ff3e7b94f03bf 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -490,17 +490,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { None => ident_search_pat(last.ident).1, } } else { - // this shouldn't be possible, but sure - #[allow( - clippy::collapsible_else_if, - reason = "we want to keep these cases together, since they are both impossible" - )] - if qself_path.is_some() { - // last `>` in `` - Pat::Str(">") - } else { - Pat::Str("") - } + // this shouldn't be possible + Pat::Str( + if qself_path.is_some() { + ">" // last `>` in `` + } else { + "" + } + ) }; (start, end) }, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 9ba796137cc3a..7b8c7f3f0d6b4 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -2,10 +2,11 @@ //! //! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to //! executable MIR bodies, so we have to do this instead. -#![allow(clippy::float_cmp)] +#![expect(clippy::float_cmp)] +use crate::res::MaybeDef; use crate::source::{SpanRangeExt, walk_span_to_context}; -use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; +use crate::{clip, is_direct_expn_of, sext, sym, unsext}; use rustc_abi::Size; use rustc_apfloat::Float; @@ -50,15 +51,15 @@ pub enum Constant { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box), + Ref(Box), /// A literal with syntax error. Err, } @@ -805,10 +806,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { | sym::i128_legacy_const_max ) ) || self.tcx.opt_parent(did).is_some_and(|parent| { - paths::F16_CONSTS.matches(&self.tcx, parent) - || paths::F32_CONSTS.matches(&self.tcx, parent) - || paths::F64_CONSTS.matches(&self.tcx, parent) - || paths::F128_CONSTS.matches(&self.tcx, parent) + parent.is_diag_item(&self.tcx, sym::f16_consts_mod) + || parent.is_diag_item(&self.tcx, sym::f32_consts_mod) + || parent.is_diag_item(&self.tcx, sym::f64_consts_mod) + || parent.is_diag_item(&self.tcx, sym::f128_consts_mod) })) => { did diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 6f1bc28fbab81..3383e66fe3688 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -3,7 +3,7 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::ty::is_type_diagnostic_item; +use crate::res::MaybeDef; use crate::{is_expn_of, sym}; use rustc_ast::ast; @@ -14,6 +14,7 @@ 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)`. +#[derive(Debug)] pub struct ForLoop<'tcx> { /// `for` loop item pub pat: &'tcx Pat<'tcx>, @@ -212,7 +213,7 @@ pub struct Range<'a> { impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] pub fn hir(expr: &'a Expr<'_>) -> Option> { match expr.kind { ExprKind::Call(path, [arg1, arg2]) @@ -318,6 +319,7 @@ pub struct While<'hir> { pub body: &'hir Expr<'hir>, /// Span of the loop header pub span: Span, + pub label: Option, } impl<'hir> While<'hir> { @@ -333,13 +335,18 @@ impl<'hir> While<'hir> { }), .. }, - _, + label, LoopSource::While, span, ) = expr.kind && !has_let_expr(condition) { - return Some(Self { condition, body, span }); + return Some(Self { + condition, + body, + span, + label, + }); } None } @@ -446,7 +453,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - if let ExprKind::Call(func, args) = expr.kind { match func.kind { ExprKind::Path(QPath::TypeRelative(ty, name)) - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => + if cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::Vec) => { if name.ident.name == sym::new { return Some(VecInitKind::New); @@ -462,7 +469,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - }, ExprKind::Path(QPath::Resolved(_, path)) if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => + && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) => { return Some(VecInitKind::Default); }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 2218c5e4e44f0..c8943523e1798 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -30,8 +30,10 @@ extern crate rustc_ast; extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; -// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. -#[allow(unused_extern_crates)] +#[expect( + unused_extern_crates, + reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate." +)] extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; @@ -62,6 +64,7 @@ pub mod msrvs; pub mod numeric_literal; pub mod paths; pub mod qualify_min_const_fn; +pub mod res; pub mod source; pub mod str_utils; pub mod sugg; @@ -128,6 +131,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; +use crate::res::{MaybeDef, MaybeResPath}; 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; @@ -169,7 +173,8 @@ macro_rules! extract_msrv_attr { /// // ^^^ input /// ``` pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { - while let Some(init) = path_to_local(expr) + while let Some(init) = expr + .res_local_id() .and_then(|id| find_binding_init(cx, id)) .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) { @@ -247,19 +252,6 @@ pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { } } -/// Checks if a `Res` refers to a constructor of a `LangItem` -/// For example, use this to check whether a function call or a pattern is `Some(..)`. -pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { - if let Res::Def(DefKind::Ctor(..), id) = res - && let Some(lang_id) = cx.tcx.lang_items().get(lang_item) - && let Some(id) = cx.tcx.opt_parent(id) - { - id == lang_id - } else { - false - } -} - /// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`. pub fn is_enum_variant_ctor( cx: &LateContext<'_>, @@ -332,13 +324,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) + if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)) } /// Checks if `arm` has the form `None => None`. pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { is_none_pattern(cx, arm.pat) - && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) + && matches!( + peel_blocks(arm.body).kind, + ExprKind::Path(qpath) + if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone) + ) } /// Checks if the given `QPath` belongs to a type alias. @@ -350,40 +346,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// 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) { - cx.tcx.trait_of_assoc(method_id).is_none() - } else { - false - } -} - -/// Checks if a method is defined in an impl of a diagnostic item -pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() - { - return cx.tcx.is_diagnostic_item(diag_item, adt.did()); - } - false -} - -/// Checks if a method is in a diagnostic item trait -pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) { - return cx.tcx.is_diagnostic_item(diag_item, trait_did); - } - false -} - -/// Checks if the method call given in `expr` belongs to the given trait. -pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - cx.typeck_results() - .type_dependent_def_id(expr.hir_id) - .is_some_and(|did| is_diag_trait_item(cx, did, diag_item)) -} - /// Checks if the `def_id` belongs to a function that is part of a trait impl. pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id)) @@ -395,25 +357,6 @@ pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool } } -/// Checks if the given expression is a path referring an item on the trait -/// that is marked with the given diagnostic item. -/// -/// For checking method call expressions instead of path expressions, use -/// [`is_trait_method`]. -/// -/// For example, this can be used to find if an expression like `u64::default` -/// refers to an item of the trait `Default`, which is associated with the -/// `diag_item` of `sym::Default`. -pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - if let ExprKind::Path(ref qpath) = expr.kind { - cx.qpath_res(qpath, expr.hir_id) - .opt_def_id() - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item)) - } else { - false - } -} - pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), @@ -433,38 +376,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id)) -} - -/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if -/// it matches the given diagnostic item. -pub fn is_path_diagnostic_item<'tcx>( - cx: &LateContext<'_>, - maybe_path: &impl MaybePath<'tcx>, - diag_item: Symbol, -) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) -} - -/// If the expression is a path to a local, returns the canonical `HirId` of the local. -pub fn path_to_local(expr: &Expr<'_>) -> Option { - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind - && let Res::Local(id) = path.res - { - return Some(id); - } - None -} - -/// Returns true if the expression is a path to a local with the specified `HirId`. -/// Use this function to see if an expression matches a function argument or a match binding. -pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool { - path_to_local(expr) == Some(id) -} - /// If the expression is a path to a local (with optional projections), /// returns the canonical `HirId` of the local. /// @@ -482,56 +393,6 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -pub trait MaybePath<'hir> { - fn hir_id(&self) -> HirId; - fn qpath_opt(&self) -> Option<&QPath<'hir>>; -} - -macro_rules! maybe_path { - ($ty:ident, $kind:ident) => { - impl<'hir> MaybePath<'hir> for hir::$ty<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - hir::$kind::Path(qpath) => Some(qpath), - _ => None, - } - } - } - }; -} -maybe_path!(Expr, ExprKind); -impl<'hir> MaybePath<'hir> for Pat<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - .. - }) => Some(qpath), - _ => None, - } - } -} -maybe_path!(Ty, TyKind); - -/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` -pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res { - match maybe_path.qpath_opt() { - None => Res::Err, - Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()), - } -} - -/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID -pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option { - path_res(cx, maybe_path).opt_def_id() -} - /// 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: @@ -658,9 +519,9 @@ pub fn is_default_equivalent_call( whole_call_expr: Option<&Expr<'_>>, ) -> bool { if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind - && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() - && (is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath)) + && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx) + && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default) + || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath)) { return true; } @@ -769,7 +630,10 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { }, ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)), ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), - ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), + ExprKind::Path(qpath) => cx + .qpath_res(qpath, e.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)), _ => false, @@ -784,14 +648,14 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & ExprKind::Lit(hir::Lit { node: LitKind::Str(sym, _), .. - }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), - ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String), + ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec), ExprKind::Repeat(_, len) => { if let ConstArgKind::Anon(anon_const) = len.kind && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind && let LitKind::Int(v, _) = const_lit.node { - return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec); } }, _ => (), @@ -1670,9 +1534,12 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind && ddpos.as_opt_usize().is_none() - && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk) + && cx + .qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind - && path_to_local_id(arm.body, hir_id) + && arm.body.res_local_id() == Some(hir_id) { return true; } @@ -1681,7 +1548,9 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr) + cx.qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) } else { false } @@ -1975,7 +1844,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< match (pat.kind, expr.kind) { (PatKind::Binding(_, id, _, _), _) if by_hir => { - path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() + expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty() }, (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => { matches!(path.segments, [ segment] if segment.ident.name == ident.name) @@ -2048,7 +1917,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)), - _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)), + _ => expr.basic_res().is_diag_item(cx, sym::convert_identity), } } @@ -2812,7 +2681,6 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtx moved_before_use, same_ctxt, }, - #[allow(unreachable_patterns)] Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow"), None => ExprUseCtxt { node: Node::Crate(cx.tcx.hir_root_module()), @@ -2916,12 +2784,18 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( else_body: &Expr<'_>, ) -> Option<&'a Pat<'hir>> { if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome) + && cx + .qpath_res(&pat_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) && !is_refutable(cx, inner_pat) && let else_body = peel_blocks(else_body) && let ExprKind::Ret(Some(ret_val)) = else_body.kind && let ExprKind::Path(ret_path) = ret_val.kind - && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone) + && cx + .qpath_res(&ret_path, ret_val.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) { Some(inner_pat) } else { @@ -3495,7 +3369,7 @@ pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) - /// Returns `true` if `expr` designates a mutable static, a mutable local binding, or an expression /// that can be owned. pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index 9ba644fdd20ec..a066427d6bc11 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -134,7 +134,7 @@ pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { } /// Returns the `mir::Body` containing the node associated with `hir_id`. -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> { let body_owner_local_def_id = tcx.hir_enclosing_body_owner(hir_id); if tcx.hir_body_owner_kind(body_owner_local_def_id).is_fn_or_closure() { diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 152b4272c26ca..f2bffc8156afc 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -15,7 +15,6 @@ use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. -#[allow(clippy::module_name_repetitions)] struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, @@ -167,7 +166,6 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } /// Result of `PossibleBorrowerVisitor`. -#[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` pub map: FxHashMap>, diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs index 3d253fd2bb148..fee22c436b0fb 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs @@ -8,7 +8,6 @@ use rustc_middle::mir; /// Collect possible borrowed for every `&mut` local. /// For example, `_1 = &mut _2` generate _1: {_2,...} /// Known Problems: not sure all borrowed are tracked -#[allow(clippy::module_name_repetitions)] pub(super) struct PossibleOriginVisitor<'a, 'tcx> { possible_origin: TransitiveRelation, body: &'a mir::Body<'tcx>, diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 5ab8e16d88ed3..8aa663163caf1 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -4,7 +4,8 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -use crate::{MaybePath, path_def_id, sym}; +use crate::res::MaybeQPath; +use crate::sym; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -96,8 +97,11 @@ impl PathLookup { } /// 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)) + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> bool { + maybe_path + .res(cx) + .opt_def_id() + .is_some_and(|def_id| self.matches(cx, def_id)) } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` @@ -127,11 +131,6 @@ path_macros! { macro_path: PathNS::Macro, } -pub static F16_CONSTS: PathLookup = type_path!(core::f16::consts); -pub static F32_CONSTS: PathLookup = type_path!(core::f32::consts); -pub static F64_CONSTS: PathLookup = type_path!(core::f64::consts); -pub static F128_CONSTS: PathLookup = type_path!(core::f128::consts); - // Paths in external crates pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index b9027fea468eb..7722bebdbbe0d 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -232,9 +232,7 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(cx, *place, span, body, msrv), // just an assignment - StatementKind::SetDiscriminant { place, .. } => { - check_place(cx, **place, span, body, msrv) - }, + StatementKind::SetDiscriminant { place, .. } => check_place(cx, **place, span, body, msrv), StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(cx, op, span, body, msrv), diff --git a/src/tools/clippy/clippy_utils/src/res.rs b/src/tools/clippy/clippy_utils/src/res.rs new file mode 100644 index 0000000000000..90b9529278964 --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/res.rs @@ -0,0 +1,643 @@ +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ + self as hir, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, TyKind, +}; +use rustc_lint::LateContext; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{AdtDef, AdtKind, Binder, EarlyBinder, Ty, TypeckResults}; +use rustc_span::{Ident, Symbol}; + +/// Either a `HirId` or a type which can be identified by one. +pub trait HasHirId: Copy { + fn hir_id(self) -> HirId; +} +impl HasHirId for HirId { + #[inline] + fn hir_id(self) -> HirId { + self + } +} +impl HasHirId for &Expr<'_> { + #[inline] + fn hir_id(self) -> HirId { + self.hir_id + } +} + +type DefRes = (DefKind, DefId); + +pub trait MaybeTypeckRes<'tcx> { + /// Gets the contained `TypeckResults`. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>>; + + /// Gets the type-dependent resolution of the specified node. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_based_def(&self, node: impl HasHirId) -> Option { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn f(typeck: &TypeckResults<'_>, id: HirId) -> Option { + if typeck.hir_owner == id.owner { + let def = typeck.type_dependent_def(id); + debug_assert!( + def.is_some(), + "attempted type-dependent lookup for a node with no definition\ + \n node `{id:?}`", + ); + def + } else { + debug_assert!( + false, + "attempted type-dependent lookup for a node in the wrong body\ + \n in body `{:?}`\ + \n expected body `{:?}`", + typeck.hir_owner, id.owner, + ); + None + } + } + self.typeck_res().and_then(|typeck| f(typeck, node.hir_id())) + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for LateContext<'tcx> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + if let Some(typeck) = self.maybe_typeck_results() { + Some(typeck) + } else { + // It's possible to get the `TypeckResults` for any other body, but + // attempting to lookup the type of something across bodies like this + // is a good indication of a bug. + debug_assert!(false, "attempted type-dependent lookup in a non-body context"); + None + } + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for TypeckResults<'tcx> { + #[inline] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + Some(self) + } +} + +/// A `QPath` with the `HirId` of the node containing it. +type QPathId<'tcx> = (&'tcx QPath<'tcx>, HirId); + +/// A HIR node which might be a `QPath`. +pub trait MaybeQPath<'a>: Copy { + /// If this node is a path gets both the contained path and the `HirId` to + /// use for type dependant lookup. + fn opt_qpath(self) -> Option>; + + /// If this node is a `QPath::LangItem` gets the item it resolves to. + #[inline] + fn opt_lang_path(self) -> Option { + match self.opt_qpath() { + Some((&QPath::LangItem(item, _), _)) => Some(item), + _ => None, + } + } + + /// If this is a path gets its resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved(_, p) => p.res, + QPath::TypeRelative(..) | QPath::LangItem(..) if let Some((kind, id)) = typeck.ty_based_def(id) => { + Res::Def(kind, id) + }, + QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path with the specified name as its final segment gets its + /// resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved(_, p) + if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative(_, seg) + if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a path gets both its resolution and final segment. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> (Res, Option<&'a PathSegment<'a>>) { + #[cfg_attr(debug_assertions, track_caller)] + fn f<'a>(qpath: &QPath<'a>, id: HirId, typeck: &TypeckResults<'_>) -> (Res, Option<&'a PathSegment<'a>>) { + match *qpath { + QPath::Resolved(_, p) if let [.., seg] = p.segments => (p.res, Some(seg)), + QPath::TypeRelative(_, seg) if let Some((kind, id)) = typeck.ty_based_def(id) => { + (Res::Def(kind, id), Some(seg)) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => (Res::Err, None), + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => (Res::Err, None), + } + } + + /// If this is a path without an explicit `Self` type gets its resolution. + /// Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) => p.res, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + _, + ) if let Some((kind, id)) = typeck.ty_based_def(id) => Res::Def(kind, id), + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path without an explicit `Self` type to an item with the + /// specified name gets its resolution. Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + seg, + ) if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a type-relative path gets the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option { + match self.opt_qpath() { + Some((QPath::TypeRelative(..), id)) => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path to an item with the specified name gets + /// the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Option { + match self.opt_qpath() { + Some((&QPath::TypeRelative(_, seg), id)) if seg.ident.name == name => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path gets the definition it resolves to and + /// its final segment. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option<(DefRes, &'a PathSegment<'a>)> { + match self.opt_qpath() { + Some((QPath::TypeRelative(_, seg), id)) if let Some(def) = typeck.ty_based_def(id) => Some((def, seg)), + _ => None, + } + } +} + +impl<'tcx> MaybeQPath<'tcx> for QPathId<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + Some((self.0, self.1)) + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx Expr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + ExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + PatExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx, AmbigArg> MaybeQPath<'tcx> for &'tcx hir::Ty<'_, AmbigArg> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + TyKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'_ Pat<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + match self.kind { + PatKind::Expr(e) => e.opt_qpath(), + _ => None, + } + } +} +impl<'tcx, T: MaybeQPath<'tcx>> MaybeQPath<'tcx> for Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} +impl<'tcx, T: Copy + MaybeQPath<'tcx>> MaybeQPath<'tcx> for &Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} + +/// A resolved path and the explicit `Self` type if there is one. +type OptResPath<'tcx> = (Option<&'tcx hir::Ty<'tcx>>, Option<&'tcx Path<'tcx>>); + +/// A HIR node which might be a `QPath::Resolved`. +/// +/// The following are resolved paths: +/// * A path to a module or crate item. +/// * A path to a trait item via the trait's name. +/// * A path to a struct or variant constructor via the original type's path. +/// * A local. +/// +/// All other paths are `TypeRelative` and require using `PathRes` to lookup the +/// resolution. +pub trait MaybeResPath<'a>: Copy { + /// If this node is a resolved path gets both the contained path and the + /// type associated with it. + fn opt_res_path(self) -> OptResPath<'a>; + + /// If this node is a resolved path gets it's resolution. Returns `Res::Err` + /// otherwise. + #[inline] + fn basic_res(self) -> &'a Res { + self.opt_res_path().1.map_or(&Res::Err, |p| &p.res) + } + + /// If this node is a path to a local gets the local's `HirId`. + #[inline] + fn res_local_id(self) -> Option { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + { + Some(id) + } else { + None + } + } + + /// If this node is a path to a local gets the local's `HirId` and identifier. + fn res_local_id_and_ident(self) -> Option<(HirId, &'a Ident)> { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + && let [seg] = p.segments + { + Some((id, &seg.ident)) + } else { + None + } + } +} +impl<'a> MaybeResPath<'a> for &'a Path<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + (None, Some(self)) + } + + #[inline] + fn basic_res(self) -> &'a Res { + &self.res + } +} +impl<'a> MaybeResPath<'a> for &QPath<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match *self { + QPath::Resolved(ty, path) => (ty, Some(path)), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Expr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + ExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &PatExpr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + PatExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, AmbigArg> MaybeResPath<'a> for &hir::Ty<'a, AmbigArg> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + TyKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Pat<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self.kind { + PatKind::Expr(e) => e.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, T: MaybeResPath<'a>> MaybeResPath<'a> for Option { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self { + Some(x) => T::opt_res_path(x), + None => (None, None), + } + } + + #[inline] + fn basic_res(self) -> &'a Res { + self.map_or(&Res::Err, T::basic_res) + } +} + +/// A type which may either contain a `DefId` or be referred to by a `DefId`. +pub trait MaybeDef: Copy { + fn opt_def_id(self) -> Option; + + /// Gets this definition's id and kind. This will lookup the kind in the def + /// tree if needed. + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)>; + + /// Gets the diagnostic name of this definition if it has one. + #[inline] + fn opt_diag_name<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().get_diagnostic_name(id)) + } + + /// Checks if this definition has the specified diagnostic name. + #[inline] + fn is_diag_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, name: Symbol) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().is_diagnostic_item(name, id)) + } + + /// Checks if this definition is the specified `LangItem`. + #[inline] + fn is_lang_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, item: LangItem) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().lang_items().get(item) == Some(id)) + } + + /// If this definition is an impl block gets its type. + #[inline] + fn opt_impl_ty<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option>> { + match self.opt_def(tcx) { + Some((DefKind::Impl { .. }, id)) => Some(tcx.tcx().type_of(id)), + _ => None, + } + } + + /// Gets the parent of this definition if it has one. + #[inline] + fn opt_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().opt_parent(id)) + } + + /// Checks if this definition is an impl block. + #[inline] + fn is_impl<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> bool { + matches!(self.opt_def(tcx), Some((DefKind::Impl { .. }, _))) + } + + /// If this definition is a constructor gets the `DefId` of it's type or variant. + #[inline] + fn ctor_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::Ctor(..), id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated item of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated function of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_fn_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocFn, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } +} +impl MaybeDef for DefId { + #[inline] + fn opt_def_id(self) -> Option { + Some(self) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.opt_def_id().map(|id| (tcx.tcx().def_kind(id), id)) + } +} +impl MaybeDef for (DefKind, DefId) { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.1) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + Some(self) + } +} +impl MaybeDef for AdtDef<'_> { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.did()) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + let did = self.did(); + match self.adt_kind() { + AdtKind::Enum => Some((DefKind::Enum, did)), + AdtKind::Struct => Some((DefKind::Struct, did)), + AdtKind::Union => Some((DefKind::Union, did)), + } + } +} +impl MaybeDef for Ty<'_> { + #[inline] + fn opt_def_id(self) -> Option { + self.ty_adt_def().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.ty_adt_def().opt_def(tcx) + } +} +impl MaybeDef for Res { + #[inline] + fn opt_def_id(self) -> Option { + Res::opt_def_id(&self) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + match self { + Res::Def(kind, id) => Some((kind, id)), + _ => None, + } + } +} +impl MaybeDef for Option { + #[inline] + fn opt_def_id(self) -> Option { + self.and_then(T::opt_def_id) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.and_then(|x| T::opt_def(x, tcx)) + } +} +impl MaybeDef for EarlyBinder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} +impl MaybeDef for Binder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 638d329031231..83d7f6bc565d7 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -143,7 +143,6 @@ 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. /// /// The range will not be expanded if it would cross a line boundary, the line the range would diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index a63333c9b48f9..581c2b02839dc 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -33,7 +33,7 @@ pub enum Sugg<'a> { /// or `-`, but only if the type with and without the operator is kept identical. /// It means that doubling the operator can be used to remove it instead, in /// order to provide better suggestions. - UnOp(UnOp, Box>), + UnOp(UnOp, Box), } /// Literal constant `0`, for convenience. diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 2d0d4a5319f3c..8e8a80a6a9c95 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -34,6 +34,7 @@ macro_rules! generate { // // `cargo dev fmt` ensures that the content of the `generate!()` macro call stays sorted. generate! { + Applicability, AsyncReadExt, AsyncWriteExt, BACKSLASH_SINGLE_QUOTE: r"\'", @@ -45,10 +46,12 @@ generate! { Current, DOUBLE_QUOTE: "\"", Deserialize, + EarlyContext, EarlyLintPass, IntoIter, Itertools, LF: "\n", + LateContext, Lazy, Lint, LowerExp, @@ -75,7 +78,9 @@ generate! { Weak, abs, ambiguous_glob_reexports, + app, append, + applicability, arg, as_bytes, as_deref, @@ -115,7 +120,6 @@ generate! { collapsible_if, collect, const_ptr, - consts, contains, copied, copy_from, @@ -125,6 +129,7 @@ generate! { count_ones, create, create_new, + cx, cycle, cyclomatic_complexity, de, @@ -176,6 +181,7 @@ generate! { hidden_glob_reexports, hygiene, insert, + insert_str, inspect, int_roundings, into, @@ -259,12 +265,14 @@ generate! { powi, product, push, + push_str, read, read_exact, read_line, read_to_end, read_to_string, read_unaligned, + read_volatile, redundant_imports, redundant_pub_crate, regex, @@ -286,8 +294,10 @@ generate! { rsplit_terminator, rsplitn, rsplitn_mut, + rustc_errors, rustc_lint, rustc_lint_defs, + rustc_middle, rustc_span, rustfmt_skip, rwlock, @@ -330,6 +340,7 @@ generate! { symbol, take, take_while, + tcx, then, then_some, to_ascii_lowercase, @@ -374,6 +385,7 @@ generate! { wrapping_offset, write, write_unaligned, + write_volatile, writeln, zip, } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index ebf4f2cd3263c..a90d64e972c1f 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -3,7 +3,6 @@ #![allow(clippy::module_name_repetitions)] use core::ops::ControlFlow; -use itertools::Itertools; use rustc_abi::VariantIdx; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -22,8 +21,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, - GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + GenericArgKind, GenericArgsRef, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -34,8 +33,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::{iter, mem}; -use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; +use crate::res::{MaybeDef, MaybeQPath}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -157,20 +156,6 @@ pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Optio .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 -/// implements a trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), - _ => None, - } -} - /// Returns true if `ty` is a type on which calling `Clone` through a function instead of /// as a method, such as `Arc::clone()` is considered idiomatic. /// @@ -178,7 +163,7 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option, ty: Ty<'_>) -> bool { matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Arc | sym::ArcWeak | sym::Rc | sym::RcWeak) ) } @@ -379,42 +364,6 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Checks if the type is a reference equals to a diagnostic item -pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item), - _ => false, - } -} - -/// Checks if the type is equal to a diagnostic item. To check if a type implements a -/// trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// --- -/// -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), - _ => false, - } -} - -/// Checks if the type is equal to a lang item. -/// -/// Returns `false` if the `LangItem` is not defined. -pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()), - _ => false, - } -} - /// Return `true` if the passed `typ` is `isize` or `usize`. pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) @@ -433,9 +382,9 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { false } // Check for std types which implement drop, but only for memory allocation. - else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + else if ty.is_lang_item(cx, LangItem::OwnedBox) || matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak) ) { @@ -628,7 +577,7 @@ impl<'tcx> ExprFnSig<'tcx> { /// If the expression is function like, get the signature for it. pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option> { - if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) { + if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr.res(cx) { Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity(), Some(id))) } else { ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) @@ -953,9 +902,10 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { } } +#[cfg(debug_assertions)] /// Asserts that the given arguments match the generic parameters of the given item. -#[allow(dead_code)] fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) { + use itertools::Itertools; let g = tcx.generics_of(did); let parent = g.parent.map(|did| tcx.generics_of(did)); let count = g.parent_count + g.own_params.len(); @@ -971,7 +921,7 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi note: the expected arguments are: `[{}]`\n\ the given arguments are: `{args:#?}`", args.len(), - params.clone().map(GenericParamDefKind::descr).format(", "), + params.clone().map(ty::GenericParamDefKind::descr).format(", "), ); if let Some((idx, (param, arg))) = @@ -980,13 +930,13 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi .zip(args.iter().map(|&x| x.kind())) .enumerate() .find(|(_, (param, arg))| match (param, arg) { - (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) - | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) - | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false, + (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false, ( - GenericParamDefKind::Lifetime - | GenericParamDefKind::Type { .. } - | GenericParamDefKind::Const { .. }, + ty::GenericParamDefKind::Lifetime + | ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. }, _, ) => true, }) @@ -996,7 +946,7 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi note: the expected arguments are `[{}]`\n\ the given arguments are `{args:#?}`", param.descr(), - params.clone().map(GenericParamDefKind::descr).format(", "), + params.clone().map(ty::GenericParamDefKind::descr).format(", "), ); } } @@ -1299,11 +1249,14 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> /// Check if `ty` is an `Option` and return its argument type if it is. pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - match ty.kind() { - ty::Adt(adt, args) => cx - .tcx - .is_diagnostic_item(sym::Option, adt.did()) - .then(|| args.type_at(0)), + match *ty.kind() { + ty::Adt(adt, args) + if let [arg] = &**args + && let Some(arg) = arg.as_type() + && adt.is_diag_item(cx, sym::Option) => + { + Some(arg) + }, _ => None, } } @@ -1357,7 +1310,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' /// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec) + ty.is_slice() || ty.is_array() || ty.is_diag_item(cx, sym::Vec) } pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index d9c7e6eac9f67..d46c7bdcd4c1d 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -329,7 +329,7 @@ fn update_res( None } -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let Some(callee_def_id) = (match expr.kind { ExprKind::Call(callee, _) => { diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 6eccbcdb12281..e27f1dabeefab 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -1,4 +1,5 @@ use crate::macros::root_macro_call_first_node; +use crate::res::MaybeResPath; use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_closures}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; @@ -196,7 +197,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { for_each_expr(cx, v, |e| { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -222,7 +223,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let mut past_expr = false; for_each_expr(cx, block, |e| { if past_expr { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index c9f5401ebe776..84e4f043e34f1 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,7 +1,8 @@ +use crate::get_enclosing_block; use crate::msrvs::Msrv; use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; -use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -312,7 +313,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) { + if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -564,7 +565,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( if self.res.is_break() { return; } - if path_to_local_id(e, self.local_id) { + if e.res_local_id() == Some(self.local_id) { self.res = (self.f)(e); } else { walk_expr(self, e); @@ -591,7 +592,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( // Calls the given function for every unconsumed temporary created by the expression. Note the // function is only guaranteed to be called for types which need to be dropped, but it may be called // for other types. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn for_each_unconsumed_temporary<'tcx, B>( cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>, @@ -740,7 +741,7 @@ pub fn for_each_local_assignment<'tcx, B>( fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && self.res.is_continue() - && path_to_local_id(lhs, self.local_id) + && lhs.res_local_id() == Some(self.local_id) { self.res = (self.f)(rhs); self.visit_expr(rhs); @@ -785,7 +786,7 @@ pub fn local_used_once<'tcx>( let mut expr = None; let cf = for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) && expr.replace(e).is_some() { + if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { ControlFlow::Continue(()) diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index 3b2ebf0c28ac2..5d5214805b968 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::num::NonZero; use std::path::PathBuf; -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] #[derive(Parser, Clone, Debug)] #[command(args_conflicts_with_subcommands = true)] pub(crate) struct LintcheckConfig { diff --git a/src/tools/clippy/lintcheck/src/input.rs b/src/tools/clippy/lintcheck/src/input.rs index 1ed059d2fb114..7dda2b7b25f81 100644 --- a/src/tools/clippy/lintcheck/src/input.rs +++ b/src/tools/clippy/lintcheck/src/input.rs @@ -180,7 +180,7 @@ impl CrateWithSource { /// copies a local folder #[expect(clippy::too_many_lines)] fn download_and_extract(&self) -> Crate { - #[allow(clippy::result_large_err)] + #[expect(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 3a60cfa79f410..b30df79023784 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -66,7 +66,7 @@ struct Crate { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued - #[allow(clippy::too_many_arguments, clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn run_clippy_lints( &self, clippy_driver_path: &Path, @@ -314,7 +314,7 @@ fn main() { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn lintcheck(config: LintcheckConfig) { let clippy_ver = build_clippy(config.perf); let clippy_driver_path = fs::canonicalize(format!( diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index e936f5dc3b7a5..d5d96448a97d3 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-10-06" +channel = "nightly-2025-10-16" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 6bddcbfd94ce4..102ca3fa69f7e 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -133,8 +133,7 @@ struct ClippyCallbacks { } impl rustc_driver::Callbacks for ClippyCallbacks { - // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` - #[allow(rustc::bad_opt_access)] + #[expect(rustc::bad_opt_access, reason = "necessary in clippy driver to set `mir_opt_level`")] fn config(&mut self, config: &mut interface::Config) { let conf_path = clippy_config::lookup_conf_file(); let previous = config.register_lints.take(); @@ -182,15 +181,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks { } } -#[allow(clippy::ignored_unit_patterns)] fn display_help() { println!("{}", help_message()); } const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; -#[allow(clippy::too_many_lines)] -#[allow(clippy::ignored_unit_patterns)] +#[expect(clippy::too_many_lines)] pub fn main() { // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs // about jemalloc. diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index 3c2eec1f05b90..688161c7bfcb9 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -10,12 +10,10 @@ use std::process::{self, Command}; use anstream::println; -#[allow(clippy::ignored_unit_patterns)] fn show_help() { println!("{}", help_message()); } -#[allow(clippy::ignored_unit_patterns)] fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{version_info}"); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 71cd8a6c03cc9..1ac6889352786 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -291,7 +291,6 @@ fn run_ui_toml(cx: &TestContext) { } // Allow `Default::default` as `OptWithSpan` is not nameable -#[allow(clippy::default_trait_access)] fn run_ui_cargo(cx: &TestContext) { if IS_RUSTC_TEST_SUITE { return; @@ -473,7 +472,7 @@ struct DiagnosticCollector { } impl DiagnosticCollector { - #[allow(clippy::assertions_on_constants)] + #[expect(clippy::assertions_on_constants)] fn spawn() -> (Self, thread::JoinHandle<()>) { assert!(!IS_RUSTC_TEST_SUITE && !RUN_INTERNAL_TESTS); diff --git a/src/tools/clippy/tests/symbols-used.rs b/src/tools/clippy/tests/symbols-used.rs index a1049ba64d549..f78f15103cc4e 100644 --- a/src/tools/clippy/tests/symbols-used.rs +++ b/src/tools/clippy/tests/symbols-used.rs @@ -18,7 +18,6 @@ type Result = std::result::Result; type AnyError = Box; #[test] -#[allow(clippy::case_sensitive_file_extension_comparisons)] fn all_symbols_are_used() -> Result<()> { if option_env!("RUSTC_TEST_SUITE").is_some() { return Ok(()); diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr new file mode 100644 index 0000000000000..51c46e4f97b2c --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr @@ -0,0 +1,7 @@ +error: error reading Clippy's configuration file: unknown variant `FooBar`, expected one of `crate`, `file`, `module` + --> $DIR/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml:1:28 + | +1 | inherent-impl-lint-scope = "FooBar" + | ^^^^^^^^ + +error: could not compile `config_fail` (bin "config_fail") due to 1 previous error diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml new file mode 100644 index 0000000000000..0a4cb6e368f67 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "config_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml new file mode 100644 index 0000000000000..7ad426743fb8b --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "FooBar" diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs new file mode 100644 index 0000000000000..7d5e783022480 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs @@ -0,0 +1,3 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] +fn main() {} diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr new file mode 100644 index 0000000000000..15e0086cf99cc --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr @@ -0,0 +1,57 @@ +error: multiple implementations of this structure + --> src/main.rs:11:1 + | +11 | / impl S { +12 | | //^ Must trigger +13 | | fn second() {} +14 | | } + | |_^ + | +note: first implementation here + --> src/main.rs:7:1 + | + 7 | / impl S { + 8 | | fn first() {} + 9 | | } + | |_^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/main.rs:22:5 + | +22 | / impl T { +23 | | //^ Must trigger +24 | | fn second() {} +25 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:16:1 + | +16 | / impl T { +17 | | fn first() {} +18 | | } + | |_^ + +error: multiple implementations of this structure + --> src/main.rs:36:1 + | +36 | / impl b::T { +37 | | //^ Must trigger +38 | | fn second() {} +39 | | } + | |_^ + | +note: first implementation here + --> src/b.rs:4:1 + | + 4 | / impl T { + 5 | | fn first() {} + 6 | | } + | |_^ + +error: could not compile `crate_fail` (bin "crate_fail") due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml new file mode 100644 index 0000000000000..a280da1bd8964 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "crate_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml new file mode 100644 index 0000000000000..262e42e6fccad --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "crate" diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs new file mode 100644 index 0000000000000..9d01e61925f75 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs @@ -0,0 +1,6 @@ +pub struct S; +pub struct T; + +impl T { + fn first() {} +} diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs new file mode 100644 index 0000000000000..ad95a42955093 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs @@ -0,0 +1,41 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; +struct T; + +impl S { + fn first() {} +} + +impl S { + //^ Must trigger + fn second() {} +} + +impl T { + fn first() {} +} + +mod a { + use super::T; + impl T { + //^ Must trigger + fn second() {} + } +} + +mod b; + +impl b::S { + //^ Must NOT trigger + fn first() {} + fn second() {} +} + +impl b::T { + //^ Must trigger + fn second() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr new file mode 100644 index 0000000000000..eb7cf4e0e6ffb --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr @@ -0,0 +1,58 @@ +error: multiple implementations of this structure + --> src/main.rs:13:5 + | +13 | / impl S { +14 | | //^ Must trigger +15 | | fn second() {} +16 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:6:1 + | + 6 | / impl S { + 7 | | fn first() {} + 8 | | } + | |_^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/main.rs:26:5 + | +26 | / impl S { +27 | | //^ Must trigger +28 | | +29 | | fn second() {} +30 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:22:5 + | +22 | / impl S { +23 | | fn first() {} +24 | | } + | |_____^ + +error: multiple implementations of this structure + --> src/c.rs:17:5 + | +17 | / impl T { +18 | | //^ Must trigger +19 | | fn second() {} +20 | | } + | |_____^ + | +note: first implementation here + --> src/c.rs:10:5 + | +10 | / impl T { +11 | | fn first() {} +12 | | } + | |_____^ + +error: could not compile `file_fail` (bin "file_fail") due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml new file mode 100644 index 0000000000000..7f767c65b98cb --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "file_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml new file mode 100644 index 0000000000000..4229797a91f36 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "file" diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs new file mode 100644 index 0000000000000..7757061e87b04 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs @@ -0,0 +1,21 @@ +pub struct S; +struct T; + +impl S { + fn first() {} +} + +mod d { + use super::T; + impl T { + fn first() {} + } +} + +mod e { + use super::T; + impl T { + //^ Must trigger + fn second() {} + } +} diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs new file mode 100644 index 0000000000000..97514fd30e047 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; + +impl S { + fn first() {} +} + +mod a { + use super::S; + + impl S { + //^ Must trigger + fn second() {} + } +} + +mod b { + struct S; + + impl S { + fn first() {} + } + + impl S { + //^ Must trigger + + fn second() {} + } +} + +mod c; + +impl c::S { + //^ Must NOT trigger + fn second() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr new file mode 100644 index 0000000000000..dd02afa1d39b3 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr @@ -0,0 +1,41 @@ +error: multiple implementations of this structure + --> src/main.rs:26:5 + | +26 | / impl S { +27 | | //^ Must trigger +28 | | +29 | | fn second() {} +30 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:22:5 + | +22 | / impl S { +23 | | fn first() {} +24 | | } + | |_____^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/c.rs:12:1 + | +12 | / impl T { +13 | | //^ Must trigger +14 | | fn second() {} +15 | | } + | |_^ + | +note: first implementation here + --> src/c.rs:8:1 + | + 8 | / impl T { + 9 | | fn first() {} +10 | | } + | |_^ + +error: could not compile `module_fail` (bin "module_fail") due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml new file mode 100644 index 0000000000000..4b57af0d8fefb --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "module_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml new file mode 100644 index 0000000000000..293dfa183fcea --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "module" diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs new file mode 100644 index 0000000000000..1d51ebe5d22a0 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs @@ -0,0 +1,15 @@ +pub struct S; +struct T; + +impl S { + fn first() {} +} + +impl T { + fn first() {} +} + +impl T { + //^ Must trigger + fn second() {} +} diff --git a/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs new file mode 100644 index 0000000000000..17b1b777f7be0 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; + +impl S { + fn first() {} +} + +mod a { + use super::S; + + impl S { + //^ Must NOT trigger + fn second() {} + } +} + +mod b { + struct S; + + impl S { + fn first() {} + } + + impl S { + //^ Must trigger + + fn second() {} + } +} + +mod c; + +impl c::S { + //^ Must NOT trigger + fn second() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr index 59a7146ac90fd..bfe2486c85029 100644 --- a/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr @@ -5,10 +5,10 @@ error: module has unnecessary safety comment | ^^^^^^^^ | help: consider removing the safety comment - --> src/main.rs:1:1 + --> src/main.rs:1:4 | 1 | // SAFETY: ... - | ^^^^^^^^^^^^^^ + | ^^^^^^^ = note: requested on the command line with `-D clippy::unnecessary-safety-comment` error: module has unnecessary safety comment @@ -18,9 +18,9 @@ error: module has unnecessary safety comment | ^^^^^^^^ | help: consider removing the safety comment - --> src/main.rs:4:1 + --> src/main.rs:4:4 | 4 | // SAFETY: ... - | ^^^^^^^^^^^^^^ + | ^^^^^^^ error: could not compile `undocumented_unsafe_blocks` (bin "undocumented_unsafe_blocks") due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 20aeb4bb8498e..2d9503c5ac53a 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -49,6 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -66,6 +67,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -144,6 +146,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -161,6 +164,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -239,6 +243,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -256,6 +261,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index bfc14be5421fc..61e5af81d8276 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -247,10 +247,10 @@ LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -289,10 +289,10 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8 | LL | // SAFETY: this is more than one level away, so it should warn - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:545:12 @@ -342,17 +342,137 @@ LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; | = help: consider adding a safety comment on the preceding line +error: constant has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:701:5 + | +LL | const UNIX_EPOCH_JULIAN_DAY: i32 = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:699:8 + | +LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + | ^^^^^^^ + error: statement has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5 | LL | _ = bar(); | ^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8 | LL | // SAFETY: unnecessary_safety_comment triggers here - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5 + | +LL | mod x {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:746:5 + | +LL | mod y {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:744:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5 + | +LL | mod z {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:759:5 + | +LL | mod y {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:757:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:774:9 + | +LL | let x = 34; + | ^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:772:12 + | +LL | // SAFETY: ... + | ^^^^^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5 + | +LL | unsafe fn unsafe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5 + | +LL | unsafe fn unsafe_block_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8 + | +LL | SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5 + | +LL | fn safe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5 + | +LL | fn safe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9 + | +LL | /// SAFETY: Bla + | ^^^^^^^ -error: aborting due to 40 previous errors +error: aborting due to 50 previous errors diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index cebfc48a884f6..e252cffea9160 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -247,10 +247,10 @@ LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -297,10 +297,10 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8 | LL | // SAFETY: this is more than one level away, so it should warn - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:545:12 @@ -439,24 +439,104 @@ LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5 | LL | _ = bar(); | ^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8 | LL | // SAFETY: unnecessary_safety_comment triggers here - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:735:12 | LL | return unsafe { h() }; | ^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 53 previous errors +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5 + | +LL | mod x {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5 + | +LL | mod z {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:766:9 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5 + | +LL | unsafe fn unsafe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5 + | +LL | unsafe fn unsafe_block_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8 + | +LL | SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5 + | +LL | fn safe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5 + | +LL | fn safe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9 + | +LL | /// SAFETY: Bla + | ^^^^^^^ + +error: aborting due to 60 previous errors diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index a2d7c1b6c7967..db9e81cf10a1a 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -701,6 +701,8 @@ mod issue_11709_regression { const UNIX_EPOCH_JULIAN_DAY: i32 = unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); //~[disabled]^ undocumented_unsafe_blocks + // This shouldn't be linted, Issue #15755 + //~[default]^^^^ unnecessary_safety_comment } fn issue_13039() { @@ -734,4 +736,64 @@ fn rfl_issue15034() -> i32 { //~[disabled]^ ERROR: unsafe block missing a safety comment } +mod issue_14555 { + // SAFETY: ... + mod x {} + //~^ unnecessary_safety_comment + + // SAFETY: ... + #[doc(hidden)] + mod y {} + //~[default]^ unnecessary_safety_comment + + #[doc(hidden)] + // SAFETY: ... + mod z {} + //~^ unnecessary_safety_comment +} + +mod issue_15754 { + #[must_use] + // SAFETY: ... + #[doc(hidden)] + mod y {} + //~[default]^ unnecessary_safety_comment + + fn foo() { + #[doc(hidden)] + // SAFETY: unnecessary_safety_comment should not trigger here + #[allow(unsafe_code)] + unsafe {} + //~[disabled]^ undocumented_unsafe_blocks + } + + fn bar() { + #[doc(hidden)] + // SAFETY: ... + #[allow(clippy::unnecessary_cast)] + let x = 34; + //~[default]^ unnecessary_safety_comment + } +} + +mod unsafe_fns { + // SAFETY: Bla + unsafe fn unsafe_comment() {} + //~^ unnecessary_safety_comment + + /* + SAFETY: Bla + */ + unsafe fn unsafe_block_comment() {} + //~^ unnecessary_safety_comment + + // SAFETY: Bla + fn safe_comment() {} + //~^ unnecessary_safety_comment + + /// SAFETY: Bla + fn safe_doc_comment() {} + //~^ unnecessary_safety_comment +} + fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed new file mode 100644 index 0000000000000..cc8d5028b7276 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// # Safety Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * # Safety Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr new file mode 100644 index 0000000000000..95e47dc7ed639 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr @@ -0,0 +1,22 @@ +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:10:5 + | +LL | /// SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | unsafe fn unsafe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:16:5 + | +LL | * SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | */ +LL | unsafe fn unsafe_block_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed new file mode 100644 index 0000000000000..cc8d5028b7276 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// # Safety Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * # Safety Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr new file mode 100644 index 0000000000000..95e47dc7ed639 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr @@ -0,0 +1,22 @@ +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:10:5 + | +LL | /// SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | unsafe fn unsafe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:16:5 + | +LL | * SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | */ +LL | unsafe fn unsafe_block_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs new file mode 100644 index 0000000000000..14b91126caa66 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// SAFETY: Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * SAFETY: Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/use_self/default/clippy.toml b/src/tools/clippy/tests/ui-toml/use_self/default/clippy.toml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/clippy/tests/ui-toml/use_self/disabled/clippy.toml b/src/tools/clippy/tests/ui-toml/use_self/disabled/clippy.toml new file mode 100644 index 0000000000000..866cc5c624b5f --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/use_self/disabled/clippy.toml @@ -0,0 +1 @@ +recursive-self-in-type-definitions = false diff --git a/src/tools/clippy/tests/ui-toml/use_self/use_self.default.fixed b/src/tools/clippy/tests/ui-toml/use_self/use_self.default.fixed new file mode 100644 index 0000000000000..288e304c60a8e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/use_self/use_self.default.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/src/tools/clippy/tests/ui-toml/use_self/use_self.default.stderr b/src/tools/clippy/tests/ui-toml/use_self/use_self.default.stderr new file mode 100644 index 0000000000000..34cfdfd938aa1 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/use_self/use_self.default.stderr @@ -0,0 +1,17 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:10:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/use_self/use_self.disabled.fixed b/src/tools/clippy/tests/ui-toml/use_self/use_self.disabled.fixed new file mode 100644 index 0000000000000..227606e69005e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/use_self/use_self.disabled.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/src/tools/clippy/tests/ui-toml/use_self/use_self.disabled.stderr b/src/tools/clippy/tests/ui-toml/use_self/use_self.disabled.stderr new file mode 100644 index 0000000000000..1801744f0d417 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/use_self/use_self.disabled.stderr @@ -0,0 +1,11 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/use_self/use_self.rs b/src/tools/clippy/tests/ui-toml/use_self/use_self.rs new file mode 100644 index 0000000000000..d20006d4df1ae --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/use_self/use_self.rs @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Basic) {} + //~[default,disabled]^ use_self +} diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index e453299edbcfc..ff9fe2425ff9f 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -23,13 +23,13 @@ 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 - && is_path_diagnostic_item(cx, func, sym::string_new) + && func.res(cx).is_diag_item(cx, 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 - && is_path_diagnostic_item(cx, func1, sym::mem_drop) + && func1.res(cx).is_diag_item(cx, sym::mem_drop) && args1.len() == 1 { // report your lint here diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout index 2b179d45112ee..024121b14f9bc 100644 --- a/src/tools/clippy/tests/ui/author/call.stdout +++ b/src/tools/clippy/tests/ui/author/call.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::cmp_min) + && func.res(cx).is_diag_item(cx, 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/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout index f02ea5bf075f7..b88058f974dba 100644 --- a/src/tools/clippy/tests/ui/author/issue_3849.stdout +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::transmute) + && func.res(cx).is_diag_item(cx, sym::transmute) && args.len() == 1 && let PatKind::Wild = local.pat.kind { diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs index e5c094f158ec7..c8774c7f30916 100644 --- a/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![warn(clippy::char_lit_as_u8)] fn main() { diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr index 49e555ae638a2..4c2770cd2a4e0 100644 --- a/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr @@ -1,5 +1,5 @@ error: casting a character literal to `u8` truncates - --> tests/ui/char_lit_as_u8_unfixable.rs:6:13 + --> tests/ui/char_lit_as_u8_unfixable.rs:5:13 | LL | let _ = '❤' as u8; | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/clone_on_ref_ptr.fixed b/src/tools/clippy/tests/ui/clone_on_ref_ptr.fixed index 8ef4b36566363..ede9d171517e7 100644 --- a/src/tools/clippy/tests/ui/clone_on_ref_ptr.fixed +++ b/src/tools/clippy/tests/ui/clone_on_ref_ptr.fixed @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = std::rc::Weak::::clone(&rec) as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/src/tools/clippy/tests/ui/clone_on_ref_ptr.rs b/src/tools/clippy/tests/ui/clone_on_ref_ptr.rs index fbd787099aee3..5999b4069d0f2 100644 --- a/src/tools/clippy/tests/ui/clone_on_ref_ptr.rs +++ b/src/tools/clippy/tests/ui/clone_on_ref_ptr.rs @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = rec.clone() as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/src/tools/clippy/tests/ui/clone_on_ref_ptr.stderr b/src/tools/clippy/tests/ui/clone_on_ref_ptr.stderr index b15f0e803a352..b8ddc3058c012 100644 --- a/src/tools/clippy/tests/ui/clone_on_ref_ptr.stderr +++ b/src/tools/clippy/tests/ui/clone_on_ref_ptr.stderr @@ -37,5 +37,11 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 6 previous errors +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:65:23 + | +LL | let rec = rec.clone() as Weak u32>; + | ^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rec)` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs index 8931a3aa09c61..84f958ee84584 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.rs +++ b/src/tools/clippy/tests/ui/collapsible_match.rs @@ -304,16 +304,25 @@ pub fn test_2(x: Issue9647) { } } -// https://github.com/rust-lang/rust-clippy/issues/14281 -fn lint_emitted_at_right_node(opt: Option>) { - let n = match opt { - #[expect(clippy::collapsible_match)] - Some(n) => match n { - Ok(n) => n, - _ => return, - }, - None => return, - }; +mod issue_13287 { + enum Token { + Name, + Other, + } + + struct Error { + location: u32, + token: Option, + } + + fn struct_field_pat_with_binding_mode(err: Option) { + if let Some(Error { ref token, .. }) = err { + if let Some(Token::Name) = token { + //~^ collapsible_match + println!("token used as a ref"); + } + } + } } pub fn issue_14155() { @@ -357,6 +366,18 @@ pub fn issue_14155() { } } +// https://github.com/rust-lang/rust-clippy/issues/14281 +fn lint_emitted_at_right_node(opt: Option>) { + let n = match opt { + #[expect(clippy::collapsible_match)] + Some(n) => match n { + Ok(n) => n, + _ => return, + }, + None => return, + }; +} + fn make() -> T { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr index 14b1c1b187e4f..d217948d4ca6c 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match.stderr @@ -230,7 +230,7 @@ help: the outer pattern can be modified to include the inner pattern LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding LL | if let Some(u) = a { - | ^^^^^^^ with this pattern, prefixed by `a`: + | ^^^^^^^ with this pattern, prefixed by `a: ` error: this `if let` can be collapsed into the outer `if let` --> tests/ui/collapsible_match.rs:299:9 @@ -250,8 +250,25 @@ LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Some(u) = a { | ^^^^^^^ with this pattern +error: this `if let` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:320:13 + | +LL | / if let Some(Token::Name) = token { +LL | | +LL | | println!("token used as a ref"); +LL | | } + | |_____________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:319:29 + | +LL | if let Some(Error { ref token, .. }) = err { + | ^^^^^^^^^ replace this binding +LL | if let Some(Token::Name) = token { + | ^^^^^^^^^^^^^^^^^ with this pattern, prefixed by `token: ` + error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:322:9 + --> tests/ui/collapsible_match.rs:331:9 | LL | / match *last { LL | | @@ -263,7 +280,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:321:17 + --> tests/ui/collapsible_match.rs:330:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().copied()` @@ -274,7 +291,7 @@ LL | "a" | "b" => { | ^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:332:9 + --> tests/ui/collapsible_match.rs:341:9 | LL | / match &last { LL | | @@ -286,7 +303,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:331:17 + --> tests/ui/collapsible_match.rs:340:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().as_ref()` @@ -297,7 +314,7 @@ LL | &&"a" | &&"b" => { | ^^^^^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:342:9 + --> tests/ui/collapsible_match.rs:351:9 | LL | / match &mut last { LL | | @@ -309,7 +326,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:341:17 + --> tests/ui/collapsible_match.rs:350:17 | LL | if let Some(mut last) = arr.last_mut() { | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` @@ -319,5 +336,5 @@ LL | if let Some(mut last) = arr.last_mut() { LL | &mut &mut "a" | &mut &mut "b" => { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-15684.rs b/src/tools/clippy/tests/ui/crashes/ice-15684.rs new file mode 100644 index 0000000000000..12f36042a0fea --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-15684.rs @@ -0,0 +1,10 @@ +#![warn(clippy::unnecessary_safety_comment)] + +fn foo() -> i32 { + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + #[must_use] + return 33; + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-15684.stderr b/src/tools/clippy/tests/ui/crashes/ice-15684.stderr new file mode 100644 index 0000000000000..0d4eb624a2b12 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-15684.stderr @@ -0,0 +1,16 @@ +error: statement has unnecessary safety comment + --> tests/ui/crashes/ice-15684.rs:6:5 + | +LL | return 33; + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui/crashes/ice-15684.rs:4:8 + | +LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs index b8adb7601db64..6b60421c35490 100644 --- a/src/tools/clippy/tests/ui/derive.rs +++ b/src/tools/clippy/tests/ui/derive.rs @@ -133,7 +133,7 @@ fn issue14558() { fn main() {} mod issue15708 { - // Check that the lint posts on the type definition node + // Check that `allow`/`expect` attributes are recognized on the type definition node #[expect(clippy::expl_impl_clone_on_copy)] #[derive(Copy)] struct S; @@ -144,3 +144,16 @@ mod issue15708 { } } } + +mod issue15842 { + #[derive(Copy)] + struct S; + + // Check that `allow`/`expect` attributes are recognized on the `impl Clone` node + #[expect(clippy::expl_impl_clone_on_copy)] + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr index 7dbc38a2f621f..2b97a58e9d6f6 100644 --- a/src/tools/clippy/tests/ui/derive.stderr +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -9,16 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:16:1 - | -LL | / impl Clone for Qux { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` @@ -33,16 +24,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:42:1 - | -LL | / impl<'a> Clone for Lt<'a> { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:55:1 @@ -55,16 +37,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:55:1 - | -LL | / impl Clone for BigArray { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:68:1 @@ -77,16 +50,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:68:1 - | -LL | / impl Clone for FnPtr { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:90:1 @@ -99,16 +63,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:90:1 - | -LL | / impl Clone for Generic2 { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_write_in_test.rs b/src/tools/clippy/tests/ui/explicit_write_in_test.rs new file mode 100644 index 0000000000000..df020b7f1382a --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write_in_test.rs @@ -0,0 +1,9 @@ +//@ check-pass +#![warn(clippy::explicit_write)] + +#[test] +fn test() { + use std::io::Write; + writeln!(std::io::stderr(), "I am an explicit write.").unwrap(); + eprintln!("I am not an explicit write."); +} diff --git a/src/tools/clippy/tests/ui/explicit_write_in_test.stderr b/src/tools/clippy/tests/ui/explicit_write_in_test.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs index 1b9e4a5cdee13..15cb61c6eebd9 100644 --- a/src/tools/clippy/tests/ui/impl.rs +++ b/src/tools/clippy/tests/ui/impl.rs @@ -68,7 +68,20 @@ struct OneAllowedImpl; impl OneAllowedImpl {} #[allow(clippy::multiple_inherent_impl)] impl OneAllowedImpl {} -impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. +impl OneAllowedImpl {} +//~^ multiple_inherent_impl + +#[expect(clippy::multiple_inherent_impl)] +struct ExpectedFulfilled; + +impl ExpectedFulfilled {} +impl ExpectedFulfilled {} + +struct OneExpected; +impl OneExpected {} +#[expect(clippy::multiple_inherent_impl)] +impl OneExpected {} +impl OneExpected {} //~^ multiple_inherent_impl fn main() {} diff --git a/src/tools/clippy/tests/ui/impl.stderr b/src/tools/clippy/tests/ui/impl.stderr index 355927b782537..93d4b3998f908 100644 --- a/src/tools/clippy/tests/ui/impl.stderr +++ b/src/tools/clippy/tests/ui/impl.stderr @@ -57,7 +57,7 @@ LL | | } error: multiple implementations of this structure --> tests/ui/impl.rs:71:1 | -LL | impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. +LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ | note: first implementation here @@ -66,5 +66,17 @@ note: first implementation here LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: multiple implementations of this structure + --> tests/ui/impl.rs:84:1 + | +LL | impl OneExpected {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> tests/ui/impl.rs:81:1 + | +LL | impl OneExpected {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index 7d01a7fb61fc1..0bde31aca0304 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -1,7 +1,7 @@ //@no-rustfix: multiple suggestions add `-> !` to the same fn //@aux-build:proc_macros.rs -#![allow(clippy::never_loop)] +#![allow(clippy::never_loop, clippy::while_let_loop)] #![warn(clippy::infinite_loop)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs b/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs index 9bf0f7f355aee..084d97fdc0f74 100644 --- a/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs @@ -77,3 +77,14 @@ fn msrv_juust_right() { use std::u32::MAX; //~^ ERROR: importing a legacy numeric constant } + +macro_rules! foo { + ($a: ty) => { + let _ = <$a>::max_value(); + let _ = (<$a>::max_value)(); + }; +} + +fn issue15805() { + foo!(u8); +} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/src/tools/clippy/tests/ui/manual_assert.rs b/src/tools/clippy/tests/ui/manual_assert.rs index ab02bd5f5e533..7611795542230 100644 --- a/src/tools/clippy/tests/ui/manual_assert.rs +++ b/src/tools/clippy/tests/ui/manual_assert.rs @@ -2,8 +2,6 @@ //@[edition2018] edition:2018 //@[edition2021] edition:2021 -//@no-rustfix: need to change the suggestion to a multipart suggestion - #![warn(clippy::manual_assert)] #![allow(dead_code, unused_doc_comments)] #![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index 58ee6978fc125..cd91be87ec175 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = size.div_ceil(*c); + //~^ manual_div_ceil +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index aa0d81b22a0e2..9899c7d775c23 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = (size + c - 1) / c; + //~^ manual_div_ceil +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index 9be5a19bf391f..44de3ba99be78 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -125,5 +125,11 @@ error: manually reimplementing `div_ceil` LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 19 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:105:13 + | +LL | let _ = (size + c - 1) / c; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `size.div_ceil(*c)` + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/manual_rotate.fixed b/src/tools/clippy/tests/ui/manual_rotate.fixed index 49db8661369e9..1012ffc1aa2d8 100644 --- a/src/tools/clippy/tests/ui/manual_rotate.fixed +++ b/src/tools/clippy/tests/ui/manual_rotate.fixed @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = x_u8.rotate_right(3); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64).rotate_right(8); //~^ manual_rotate + // shift by a const + let _ = x_i64.rotate_right(N); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); @@ -40,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = x.rotate_left(s); + //~^ manual_rotate + let _ = x.rotate_left(s); + //~^ manual_rotate + // still works with consts + let _ = x.rotate_right(9); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/src/tools/clippy/tests/ui/manual_rotate.rs b/src/tools/clippy/tests/ui/manual_rotate.rs index 6445e60aa25da..3cdc79673c816 100644 --- a/src/tools/clippy/tests/ui/manual_rotate.rs +++ b/src/tools/clippy/tests/ui/manual_rotate.rs @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = (x_u8 >> 3) | (x_u8 << 5); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); //~^ manual_rotate + // shift by a const + let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); @@ -40,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = (x << s) | (x >> (32 - s)); + //~^ manual_rotate + let _ = (x << s) | (x >> (31 ^ s)); + //~^ manual_rotate + // still works with consts + let _ = (x >> 9) | (x << (32 - 9)); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/src/tools/clippy/tests/ui/manual_rotate.stderr b/src/tools/clippy/tests/ui/manual_rotate.stderr index a28721fbb94c8..ea04ee028db67 100644 --- a/src/tools/clippy/tests/ui/manual_rotate.stderr +++ b/src/tools/clippy/tests/ui/manual_rotate.stderr @@ -1,5 +1,5 @@ error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:8:16 + --> tests/ui/manual_rotate.rs:9:16 | LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u8.rotate_right(3)` @@ -8,64 +8,88 @@ LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); = help: to override `-D warnings` add `#[allow(clippy::manual_rotate)]` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:10:17 + --> tests/ui/manual_rotate.rs:11:17 | LL | let y_u16 = (x_u16 >> 7) | (x_u16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:12:17 + --> tests/ui/manual_rotate.rs:13:17 | LL | let y_u32 = (x_u32 >> 8) | (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:14:17 + --> tests/ui/manual_rotate.rs:15:17 | LL | let y_u64 = (x_u64 >> 9) | (x_u64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:16:16 + --> tests/ui/manual_rotate.rs:17:16 | LL | let y_i8 = (x_i8 >> 3) | (x_i8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i8.rotate_right(3)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:18:17 + --> tests/ui/manual_rotate.rs:19:17 | LL | let y_i16 = (x_i16 >> 7) | (x_i16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:20:17 + --> tests/ui/manual_rotate.rs:21:17 | LL | let y_i32 = (x_i32 >> 8) | (x_i32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:22:17 + --> tests/ui/manual_rotate.rs:23:17 | LL | let y_i64 = (x_i64 >> 9) | (x_i64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:25:22 + --> tests/ui/manual_rotate.rs:26:22 | LL | let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:28:25 + --> tests/ui/manual_rotate.rs:29:25 | LL | let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 | 3256).rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:30:20 + --> tests/ui/manual_rotate.rs:31:20 | LL | let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 as u64).rotate_right(8)` -error: aborting due to 11 previous errors +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:34:13 + | +LL | let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(N)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:53:13 + | +LL | let _ = (x << s) | (x >> (32 - s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:55:13 + | +LL | let _ = (x << s) | (x >> (31 ^ s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:58:13 + | +LL | let _ = (x >> 9) | (x << (32 - 9)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_right(9)` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index 189fe876aa5d4..11023ac1142a7 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -32,6 +32,15 @@ fn main() { let x: Result = Ok(String::new()); x.unwrap_or_default(); + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index ca87926763c9c..bf06d9af7d219 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -64,6 +64,15 @@ fn main() { } else { String::new() }; + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr index e8f38a2e3899e..031100832b16f 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -76,7 +76,7 @@ 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:74:24 + --> tests/ui/manual_unwrap_or_default.rs:83:24 | LL | Some(_) => match *b { | ________________________^ @@ -87,7 +87,7 @@ 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:143:5 + --> tests/ui/manual_unwrap_or_default.rs:152:5 | LL | / if let Some(x) = Some(42) { LL | | diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_like_matches_macro.fixed similarity index 97% rename from src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed rename to src/tools/clippy/tests/ui/match_like_matches_macro.fixed index 8530ab16bfd7d..a1c95e8a94f14 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed +++ b/src/tools/clippy/tests/ui/match_like_matches_macro.fixed @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -14,11 +13,11 @@ fn main() { let _y = matches!(x, Some(0)); //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = x.is_some(); //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = x.is_none(); //~^^^^ redundant_pattern_matching diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs b/src/tools/clippy/tests/ui/match_like_matches_macro.rs similarity index 97% rename from src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs rename to src/tools/clippy/tests/ui/match_like_matches_macro.rs index 81017936889e4..eb419ba5bf8de 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs +++ b/src/tools/clippy/tests/ui/match_like_matches_macro.rs @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -17,14 +16,14 @@ fn main() { }; //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = match x { Some(_) => true, _ => false, }; //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = match x { Some(_) => false, None => true, diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr b/src/tools/clippy/tests/ui/match_like_matches_macro.stderr similarity index 84% rename from src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr rename to src/tools/clippy/tests/ui/match_like_matches_macro.stderr index 8fceb05bc6e87..ae277ce4dca68 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr +++ b/src/tools/clippy/tests/ui/match_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:14:14 + --> tests/ui/match_like_matches_macro.rs:13:14 | LL | let _y = match x { | ______________^ @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/match_expr_like_matches_macro.rs:21:14 + --> tests/ui/match_like_matches_macro.rs:20:14 | LL | let _w = match x { | ______________^ @@ -25,7 +25,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/match_expr_like_matches_macro.rs:28:14 + --> tests/ui/match_like_matches_macro.rs:27:14 | LL | let _z = match x { | ______________^ @@ -35,7 +35,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:35:15 + --> tests/ui/match_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -45,13 +45,13 @@ LL | | }; | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:42:16 + --> tests/ui/match_like_matches_macro.rs:41:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:67:20 + --> tests/ui/match_like_matches_macro.rs:66:20 | LL | let _ans = match x { | ____________________^ @@ -62,7 +62,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:78:20 + --> tests/ui/match_like_matches_macro.rs:77:20 | LL | let _ans = match x { | ____________________^ @@ -74,7 +74,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:89:20 + --> tests/ui/match_like_matches_macro.rs:88:20 | LL | let _ans = match x { | ____________________^ @@ -85,7 +85,7 @@ LL | | }; | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:150:18 + --> tests/ui/match_like_matches_macro.rs:149:18 | LL | let _z = match &z { | __________________^ @@ -95,7 +95,7 @@ LL | | }; | |_________^ help: try: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:160:18 + --> tests/ui/match_like_matches_macro.rs:159:18 | LL | let _z = match &z { | __________________^ @@ -105,7 +105,7 @@ LL | | }; | |_________^ help: try: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:178:21 + --> tests/ui/match_like_matches_macro.rs:177:21 | LL | let _ = match &z { | _____________________^ @@ -115,7 +115,7 @@ LL | | }; | |_____________^ help: try: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:193:20 + --> tests/ui/match_like_matches_macro.rs:192:20 | LL | let _res = match &val { | ____________________^ @@ -125,7 +125,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:206:20 + --> tests/ui/match_like_matches_macro.rs:205:20 | LL | let _res = match &val { | ____________________^ @@ -135,7 +135,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:265:14 + --> tests/ui/match_like_matches_macro.rs:264:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/src/tools/clippy/tests/ui/must_use_unit_unfixable.rs b/src/tools/clippy/tests/ui/must_use_unit_unfixable.rs index 0dba7996bac33..8eeaf36dca290 100644 --- a/src/tools/clippy/tests/ui/must_use_unit_unfixable.rs +++ b/src/tools/clippy/tests/ui/must_use_unit_unfixable.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #[cfg_attr(all(), must_use, deprecated)] fn issue_12320() {} //~^ must_use_unit diff --git a/src/tools/clippy/tests/ui/must_use_unit_unfixable.stderr b/src/tools/clippy/tests/ui/must_use_unit_unfixable.stderr index 087682199afb9..8b5e556b1b2ed 100644 --- a/src/tools/clippy/tests/ui/must_use_unit_unfixable.stderr +++ b/src/tools/clippy/tests/ui/must_use_unit_unfixable.stderr @@ -1,11 +1,11 @@ error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:4:1 + --> tests/ui/must_use_unit_unfixable.rs:2:1 | LL | fn issue_12320() {} | ^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:3:19 + --> tests/ui/must_use_unit_unfixable.rs:1:19 | LL | #[cfg_attr(all(), must_use, deprecated)] | ^^^^^^^^ @@ -13,13 +13,13 @@ LL | #[cfg_attr(all(), must_use, deprecated)] = help: to override `-D warnings` add `#[allow(clippy::must_use_unit)]` error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:8:1 + --> tests/ui/must_use_unit_unfixable.rs:6:1 | LL | fn issue_12320_2() {} | ^^^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:7:44 + --> tests/ui/must_use_unit_unfixable.rs:5:44 | LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] | ^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mutex_atomic.fixed b/src/tools/clippy/tests/ui/mutex_atomic.fixed new file mode 100644 index 0000000000000..e4218726019f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/mutex_atomic.fixed @@ -0,0 +1,67 @@ +#![warn(clippy::mutex_integer)] +#![warn(clippy::mutex_atomic)] +#![allow(clippy::borrow_as_ptr)] + +use std::sync::Mutex; + +fn main() { + let _ = std::sync::atomic::AtomicBool::new(true); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicUsize::new(5usize); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicIsize::new(9isize); + //~^ mutex_atomic + + let mut x = 4u32; + // `AtomicPtr` only accepts `*mut T`, so this should not lint + let _ = Mutex::new(&x as *const u32); + + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicU32::new(0u32); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI32::new(0i32); + //~^ mutex_integer + + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = std::sync::atomic::AtomicU8::new(0u8); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI16::new(0i16); + //~^ mutex_integer + + let _x = std::sync::atomic::AtomicI8::new(0); + //~^ mutex_integer + + const X: i64 = 0; + let _ = std::sync::atomic::AtomicI64::new(X); + //~^ mutex_integer + + // there are no 128 atomics, so these two should not lint + { + let _ = Mutex::new(0u128); + let _x: Mutex = Mutex::new(0); + } +} + +// don't lint on _use_, only declaration +fn issue13378() { + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + //~^ mutex_integer + + let mtx = std::sync::atomic::AtomicI32::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); + //~^ mutex_integer +} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 7db5c9f274f6e..95f2b135903f7 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -2,47 +2,66 @@ #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] +use std::sync::Mutex; + fn main() { - use std::sync::Mutex; - Mutex::new(true); + let _ = Mutex::new(true); //~^ mutex_atomic - Mutex::new(5usize); + let _ = Mutex::new(5usize); //~^ mutex_atomic - Mutex::new(9isize); + let _ = Mutex::new(9isize); //~^ mutex_atomic let mut x = 4u32; - Mutex::new(&x as *const u32); - //~^ mutex_atomic + // `AtomicPtr` only accepts `*mut T`, so this should not lint + let _ = Mutex::new(&x as *const u32); - Mutex::new(&mut x as *mut u32); + let _ = Mutex::new(&mut x as *mut u32); //~^ mutex_atomic - Mutex::new(0u32); + let _ = Mutex::new(0u32); //~^ mutex_integer - Mutex::new(0i32); + let _ = Mutex::new(0i32); //~^ mutex_integer - Mutex::new(0f32); // there are no float atomics, so this should not lint - Mutex::new(0u8); + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = Mutex::new(0u8); //~^ mutex_integer - Mutex::new(0i16); + let _ = Mutex::new(0i16); //~^ mutex_integer let _x: Mutex = Mutex::new(0); //~^ mutex_integer const X: i64 = 0; - Mutex::new(X); + let _ = Mutex::new(X); //~^ mutex_integer // there are no 128 atomics, so these two should not lint { - Mutex::new(0u128); + let _ = Mutex::new(0u128); let _x: Mutex = Mutex::new(0); } } + +// don't lint on _use_, only declaration +fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + + let mtx = Mutex::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx): Mutex = Mutex::new(0); + //~^ mutex_integer +} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index a6d5d60fbf05b..0afc6d541deab 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,74 +1,134 @@ -error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:7:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:8:13 | -LL | Mutex::new(true); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(true); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicBool::new(true)` | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-atomic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` -error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:10:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:11:13 | -LL | Mutex::new(5usize); - | ^^^^^^^^^^^^^^^^^^ - -error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:13:5 +LL | let _ = Mutex::new(5usize); + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicUsize::new(5usize)` | -LL | Mutex::new(9isize); - | ^^^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:17:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:14:13 + | +LL | let _ = Mutex::new(9isize); + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicIsize::new(9isize)` | -LL | Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:20:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:21:13 | -LL | Mutex::new(&mut x as *mut u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(&mut x as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&mut x as *mut u32)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:23:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:24:13 | -LL | Mutex::new(0u32); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u32); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0u32)` | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-integer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` -error: consider using an `AtomicI32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:26:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:27:13 + | +LL | let _ = Mutex::new(0i32); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0i32)` | -LL | Mutex::new(0i32); - | ^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:30:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:31:13 | -LL | Mutex::new(0u8); - | ^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u8); + | ^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU8::new(0u8)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI16` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:33:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:34:13 + | +LL | let _ = Mutex::new(0i16); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI16::new(0i16)` | -LL | Mutex::new(0i16); - | ^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:36:25 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:37:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let _x: Mutex = Mutex::new(0); +LL + let _x = std::sync::atomic::AtomicI8::new(0); + | + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:41:13 + | +LL | let _ = Mutex::new(X); + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI64::new(X)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:53:30 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:56:15 + | +LL | let mtx = Mutex::new(0); + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI64` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:40:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:60:22 + | +LL | let reassigned = mtx; + | ^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:65:35 + | +LL | let (funky_mtx): Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let (funky_mtx): Mutex = Mutex::new(0); +LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); | -LL | Mutex::new(X); - | ^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/mutex_atomic_unfixable.rs b/src/tools/clippy/tests/ui/mutex_atomic_unfixable.rs new file mode 100644 index 0000000000000..0c04f48cf8a9e --- /dev/null +++ b/src/tools/clippy/tests/ui/mutex_atomic_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] + +use std::sync::Mutex; + +fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + + // unfixable because we don't fix this `lock` + let mut guard = MTX.lock().unwrap(); + *guard += 1; +} diff --git a/src/tools/clippy/tests/ui/mutex_atomic_unfixable.stderr b/src/tools/clippy/tests/ui/mutex_atomic_unfixable.stderr new file mode 100644 index 0000000000000..27ffb1304c695 --- /dev/null +++ b/src/tools/clippy/tests/ui/mutex_atomic_unfixable.stderr @@ -0,0 +1,17 @@ +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic_unfixable.rs:7:30 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + = note: `-D clippy::mutex-integer` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/needless_borrow_pat.fixed b/src/tools/clippy/tests/ui/needless_borrow_pat.fixed index fe966a716df71..507186676c160 100644 --- a/src/tools/clippy/tests/ui/needless_borrow_pat.fixed +++ b/src/tools/clippy/tests/ui/needless_borrow_pat.fixed @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/src/tools/clippy/tests/ui/needless_borrow_pat.rs b/src/tools/clippy/tests/ui/needless_borrow_pat.rs index a6b43855cad10..ef0f97301bcf2 100644 --- a/src/tools/clippy/tests/ui/needless_borrow_pat.rs +++ b/src/tools/clippy/tests/ui/needless_borrow_pat.rs @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/src/tools/clippy/tests/ui/needless_borrow_pat.stderr b/src/tools/clippy/tests/ui/needless_borrow_pat.stderr index 25c570eb7ff73..34f167cca2235 100644 --- a/src/tools/clippy/tests/ui/needless_borrow_pat.stderr +++ b/src/tools/clippy/tests/ui/needless_borrow_pat.stderr @@ -1,5 +1,5 @@ error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:59:14 + --> tests/ui/needless_borrow_pat.rs:57:14 | LL | Some(ref x) => x, | ^^^^^ help: try: `x` @@ -8,7 +8,7 @@ LL | Some(ref x) => x, = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:66:14 + --> tests/ui/needless_borrow_pat.rs:64:14 | LL | Some(ref x) => *x, | ^^^^^ @@ -20,7 +20,7 @@ LL + Some(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:73:14 + --> tests/ui/needless_borrow_pat.rs:71:14 | LL | Some(ref x) => { | ^^^^^ @@ -35,19 +35,19 @@ LL ~ f1(x); | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:85:14 + --> tests/ui/needless_borrow_pat.rs:83:14 | LL | Some(ref x) => m1!(x), | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:91:15 + --> tests/ui/needless_borrow_pat.rs:89:15 | LL | let _ = |&ref x: &&String| { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:98:10 + --> tests/ui/needless_borrow_pat.rs:96:10 | LL | let (ref y,) = (&x,); | ^^^^^ @@ -61,13 +61,13 @@ LL ~ let _: &String = y; | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:110:14 + --> tests/ui/needless_borrow_pat.rs:108:14 | LL | Some(ref x) => x.0, | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:121:14 + --> tests/ui/needless_borrow_pat.rs:119:14 | LL | E::A(ref x) | E::B(ref x) => *x, | ^^^^^ ^^^^^ @@ -79,13 +79,13 @@ LL + E::A(x) | E::B(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:128:21 + --> tests/ui/needless_borrow_pat.rs:126:21 | LL | if let Some(ref x) = Some(&String::new()); | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:138:12 + --> tests/ui/needless_borrow_pat.rs:136:12 | LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ @@ -100,13 +100,13 @@ LL ~ x | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:147:11 + --> tests/ui/needless_borrow_pat.rs:145:11 | LL | fn f(&ref x: &&String) { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:157:11 + --> tests/ui/needless_borrow_pat.rs:155:11 | LL | fn f(&ref x: &&String) { | ^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs index e873db6dee14e..88b7905e7fa80 100644 --- a/src/tools/clippy/tests/ui/needless_continue.rs +++ b/src/tools/clippy/tests/ui/needless_continue.rs @@ -244,3 +244,101 @@ mod issue_4077 { true } } + +#[allow(clippy::let_unit_value)] +mod issue14550 { + fn match_with_value(mut producer: impl Iterator>) -> Result { + let mut counter = 2; + loop { + match producer.next().unwrap() { + Ok(ok) => break Ok((ok + 1) as u32), + Err(12) => { + counter -= 1; + continue; + }, + err => err?, + }; + } + } + + fn inside_macro() { + macro_rules! mac { + ($e:expr => $($rest:tt);*) => { + loop { + match $e { + 1 => continue, + 2 => break, + n => println!("{n}"), + } + $($rest;)* + } + }; + } + + mac!(2 => ); + mac!(1 => {println!("foobar")}); + } + + mod partially_inside_macro { + macro_rules! select { + ( + $expr:expr, + $( $pat:pat => $then:expr ),* + ) => { + fn foo() { + loop { + match $expr { + $( + $pat => $then, + )* + } + } + } + }; + } + + select!(Some(1), + Some(1) => { + println!("one"); + continue; + }, + Some(2) => {}, + None => break, + _ => () + ); + + macro_rules! choose { + ( + $expr:expr, + $case:expr + ) => { + fn bar() { + loop { + match $expr { + $case => { + println!("matched"); + continue; + }, + _ => { + println!("not matched"); + break; + }, + } + } + } + }; + } + + choose!(todo!(), 5); + } +} + +fn issue15548() { + loop { + if todo!() { + } else { + //~^ needless_continue + continue; + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr index 878c1e731e32a..7a65872c85cd2 100644 --- a/src/tools/clippy/tests/ui/needless_continue.stderr +++ b/src/tools/clippy/tests/ui/needless_continue.stderr @@ -220,5 +220,21 @@ LL | | } do_something(); } -error: aborting due to 15 previous errors +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:339:16 + | +LL | } else { + | ________________^ +LL | | +LL | | continue; +LL | | } + | |_________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if todo!() { + // merged code follows: + + } + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed index 11616f28825b0..d9be98de69b17 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -100,18 +100,45 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { *self } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs index 7d101915517fe..3db22bdbcf318 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -114,18 +114,48 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { + //~^ non_canonical_clone_impl + todo!() + } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr index 5500984527170..cf36a8f49f812 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr @@ -41,5 +41,17 @@ LL | | *self = source.clone(); LL | | } | |_____^ help: remove it -error: aborting due to 4 previous errors +error: non-canonical implementation of `clone` on a `Copy` type + --> tests/ui/non_canonical_clone_impl.rs:127:37 + | +LL | fn clone(&self) -> Self { + | _____________________________________^ +LL | | +LL | | todo!() +LL | | } + | |_____________^ help: change this to: `{ *self }` + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed index 6915e1984fb1e..aa23fd99ad77b 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -163,6 +167,73 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs index 7ce4cdc9aec83..da7f73f7c4bef 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -167,6 +171,75 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr index 9bd6b1f726df2..8e55603dd9dac 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:16:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:20:1 | LL | / impl PartialOrd for A { LL | | @@ -15,7 +15,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:51:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:55:1 | LL | / impl PartialOrd for C { LL | | @@ -32,7 +32,22 @@ LL + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp | error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:198:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:191:9 + | +LL | / impl PartialOrd for A { +LL | | +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________________- +LL | || todo!(); +LL | || } + | ||_____________- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__________^ + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> tests/ui/non_canonical_partial_ord_impl.rs:271:1 | LL | / impl PartialOrd for K { LL | | @@ -45,7 +60,7 @@ LL | | } | |__^ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:216:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:289:1 | LL | / impl PartialOrd for L { LL | | @@ -57,5 +72,5 @@ LL | || } LL | | } | |__^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index 6ce067f5c2462..a2ecea773efe5 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index 096d3aabf28db..3adbc785237f6 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index 21a80ae038d8c..f5578f63c946b 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:13:5 + --> tests/ui/option_if_let_else.rs:14:5 | LL | / if let Some(x) = string { LL | | @@ -13,19 +13,19 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::option_if_let_else)]` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:32:13 + --> tests/ui/option_if_let_else.rs:33:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:34:13 + --> tests/ui/option_if_let_else.rs:35:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:36:13 + --> tests/ui/option_if_let_else.rs:37:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -47,13 +47,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:43:13 + --> tests/ui/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:45:13 + --> tests/ui/option_if_let_else.rs:46:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -75,7 +75,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:52:13 + --> tests/ui/option_if_let_else.rs:53:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -97,7 +97,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:62:5 + --> tests/ui/option_if_let_else.rs:63:5 | LL | / if let Some(x) = arg { LL | | @@ -118,7 +118,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:76:13 + --> tests/ui/option_if_let_else.rs:77:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -131,7 +131,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(side_effect, |x| x)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:86:13 + --> tests/ui/option_if_let_else.rs:87:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -154,7 +154,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:120:13 + --> tests/ui/option_if_let_else.rs:121:13 | LL | / if let Some(idx) = s.find('.') { LL | | @@ -165,7 +165,7 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:132:5 + --> tests/ui/option_if_let_else.rs:133:5 | LL | / if let Ok(binding) = variable { LL | | @@ -189,7 +189,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:5 + --> tests/ui/option_if_let_else.rs:158:5 | LL | / match r { LL | | @@ -199,7 +199,7 @@ LL | | } | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:166:5 + --> tests/ui/option_if_let_else.rs:167:5 | LL | / if let Ok(s) = r { s.to_owned() } LL | | @@ -207,13 +207,13 @@ LL | | else { Vec::new() } | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:173:13 + --> tests/ui/option_if_let_else.rs:174:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:184:13 + --> tests/ui/option_if_let_else.rs:185:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -235,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:213:13 + --> tests/ui/option_if_let_else.rs:214:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:218:13 + --> tests/ui/option_if_let_else.rs:219:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -263,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:258:13 + --> tests/ui/option_if_let_else.rs:259:13 | LL | let _ = match s { | _____________^ @@ -274,7 +274,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:263:13 + --> tests/ui/option_if_let_else.rs:264:13 | LL | let _ = match Some(10) { | _____________^ @@ -285,7 +285,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:270:13 + --> tests/ui/option_if_let_else.rs:271:13 | LL | let _ = match res { | _____________^ @@ -296,7 +296,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:275:13 + --> tests/ui/option_if_let_else.rs:276:13 | LL | let _ = match res { | _____________^ @@ -307,13 +307,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:280:13 + --> tests/ui/option_if_let_else.rs:281:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:298:17 + --> tests/ui/option_if_let_else.rs:299:17 | LL | let _ = match initial { | _________________^ @@ -324,7 +324,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:306:17 + --> tests/ui/option_if_let_else.rs:307:17 | LL | let _ = match initial { | _________________^ @@ -335,7 +335,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:330:24 + --> tests/ui/option_if_let_else.rs:331:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -347,19 +347,19 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:337:19 + --> tests/ui/option_if_let_else.rs:338:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:388:22 + --> tests/ui/option_if_let_else.rs:389:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:394:13 + --> tests/ui/option_if_let_else.rs:395:13 | LL | let _ = match res { | _____________^ diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 386351aa39f51..314da0804a5fa 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index e27f9aa65c37a..2a19614026ec5 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 6bce06ab20eb6..3d55f2cd1f9fc 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:52:22 + --> tests/ui/or_fun_call.rs:53:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:56:14 + --> tests/ui/or_fun_call.rs:57:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:60:21 + --> tests/ui/or_fun_call.rs:61:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:64:14 + --> tests/ui/or_fun_call.rs:65:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:68:19 + --> tests/ui/or_fun_call.rs:69:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:72:24 + --> tests/ui/or_fun_call.rs:73:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:76:23 + --> tests/ui/or_fun_call.rs:77:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:96:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:100:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:104:14 + --> tests/ui/or_fun_call.rs:105:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:108:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:112:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:116:23 + --> tests/ui/or_fun_call.rs:117:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:120:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:124:25 + --> tests/ui/or_fun_call.rs:125:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:128:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:133:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:138:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:141:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:166:35 + --> tests/ui/or_fun_call.rs:167:35 | 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:209:18 + --> tests/ui/or_fun_call.rs:210:18 | 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:217:14 + --> tests/ui/or_fun_call.rs:218: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:220:14 + --> tests/ui/or_fun_call.rs:221: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:296:25 + --> tests/ui/or_fun_call.rs:297: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:298:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:301:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:332:18 + --> tests/ui/or_fun_call.rs:333: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:336:28 + --> tests/ui/or_fun_call.rs:337: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:340:27 + --> tests/ui/or_fun_call.rs:341: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:344:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:348:23 + --> tests/ui/or_fun_call.rs:349: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:352:25 + --> tests/ui/or_fun_call.rs:353: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:356:25 + --> tests/ui/or_fun_call.rs:357: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:398:17 + --> tests/ui/or_fun_call.rs:399: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:403:17 + --> tests/ui/or_fun_call.rs:404: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:408:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,79 +235,79 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:414:17 + --> tests/ui/or_fun_call.rs:415: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:419:17 + --> tests/ui/or_fun_call.rs:420: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:426:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:441:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.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:443:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:446:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:457:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:467:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:477:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:483:17 + --> tests/ui/or_fun_call.rs:484:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:485:17 + --> tests/ui/or_fun_call.rs:486:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:491:17 + --> tests/ui/or_fun_call.rs:492:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:493:17 + --> tests/ui/or_fun_call.rs:494:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` diff --git a/src/tools/clippy/tests/ui/replace_box.fixed b/src/tools/clippy/tests/ui/replace_box.fixed new file mode 100644 index 0000000000000..58c8ed1691d77 --- /dev/null +++ b/src/tools/clippy/tests/ui/replace_box.fixed @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + **b = T::default(); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + **b = t; + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + *b = Default::default(); + //~^ replace_box + *b = Default::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + *b = 5; + //~^ replace_box + + *b = mac!(three); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/src/tools/clippy/tests/ui/replace_box.rs b/src/tools/clippy/tests/ui/replace_box.rs new file mode 100644 index 0000000000000..e1fb223e4f211 --- /dev/null +++ b/src/tools/clippy/tests/ui/replace_box.rs @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + *b = Box::new(T::default()); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + *b = Box::new(t); + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + b = Default::default(); + //~^ replace_box + b = Box::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + b = Box::new(5); + //~^ replace_box + + b = Box::new(mac!(three)); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/src/tools/clippy/tests/ui/replace_box.stderr b/src/tools/clippy/tests/ui/replace_box.stderr new file mode 100644 index 0000000000000..7d9c85da17314 --- /dev/null +++ b/src/tools/clippy/tests/ui/replace_box.stderr @@ -0,0 +1,52 @@ +error: creating a new box + --> tests/ui/replace_box.rs:4:5 + | +LL | *b = Box::new(T::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = T::default()` + | + = note: this creates a needless allocation + = note: `-D clippy::replace-box` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::replace_box)]` + +error: creating a new box + --> tests/ui/replace_box.rs:9:5 + | +LL | *b = Box::new(t); + | ^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = t` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:44:5 + | +LL | b = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:46:5 + | +LL | b = Box::default(); + | ^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:58:5 + | +LL | b = Box::new(5); + | ^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = 5` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:61:5 + | +LL | b = Box::new(mac!(three)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = mac!(three)` + | + = note: this creates a needless allocation + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.fixed b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.fixed new file mode 100644 index 0000000000000..9974ee2d08eb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.fixed @@ -0,0 +1,75 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.unwrap_or_else(|| ()); + + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.unwrap_or_else(|| string); //~ ERROR: unused "map closure" when calling + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.rs b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..9b53f3fcd521c --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.rs @@ -0,0 +1,82 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.map_or_else(|| (), |x| x); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.map_or_else(|| (), |x: ()| x); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |x| x); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.map_or_else( + //~^ ERROR: unused "map closure" when calling + || (), + |x| { + let tmp = x; + tmp + }, + ); + + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, identity); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.map_or_else(|| string, do_nothing); //~ ERROR: unused "map closure" when calling + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.stderr b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.stderr new file mode 100644 index 0000000000000..d90875e4efc7c --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.stderr @@ -0,0 +1,47 @@ +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:21:5 + | +LL | option.map_or_else(|| (), |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + | + = note: `-D clippy::unnecessary-option-map-or-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_option_map_or_else)]` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:25:5 + | +LL | option.map_or_else(|| (), |x: ()| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:30:19 + | +LL | let _: &str = option.map_or_else(|| &string, |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:34:5 + | +LL | / option.map_or_else( +LL | | +LL | | || (), +LL | | |x| { +... | +LL | | }, +LL | | ); + | |_____^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:46:19 + | +LL | let _: &str = option.map_or_else(|| &string, identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:52:21 + | +LL | let _: String = option.map_or_else(|| string, do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| string)` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr b/src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr index 732e6767c1780..6ad94f986434d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr @@ -5,10 +5,10 @@ LL | const CONST: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:5:5 + --> tests/ui/unnecessary_safety_comment.rs:5:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -19,10 +19,10 @@ LL | static STATIC: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:9:5 + --> tests/ui/unnecessary_safety_comment.rs:9:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: struct has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:14:5 @@ -31,10 +31,10 @@ LL | struct Struct; | ^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:13:5 + --> tests/ui/unnecessary_safety_comment.rs:13:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: enum has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:18:5 @@ -43,10 +43,10 @@ LL | enum Enum {} | ^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:17:5 + --> tests/ui/unnecessary_safety_comment.rs:17:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: module has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:22:5 @@ -55,10 +55,10 @@ LL | mod module {} | ^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:21:5 + --> tests/ui/unnecessary_safety_comment.rs:21:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: impl has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:42:13 @@ -70,10 +70,10 @@ LL | with_safety_comment!(i32); | ------------------------- in this macro invocation | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:41:13 + --> tests/ui/unnecessary_safety_comment.rs:41:16 | LL | // Safety: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: expression has unnecessary safety comment @@ -83,10 +83,10 @@ LL | 24 | ^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:59:5 + --> tests/ui/unnecessary_safety_comment.rs:59:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: statement has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:52:5 @@ -95,10 +95,10 @@ LL | let num = 42; | ^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:51:5 + --> tests/ui/unnecessary_safety_comment.rs:51:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: statement has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:56:5 @@ -107,10 +107,10 @@ LL | if num > 24 {} | ^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:55:5 + --> tests/ui/unnecessary_safety_comment.rs:55:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/use_self_structs.fixed b/src/tools/clippy/tests/ui/use_self_structs.fixed new file mode 100644 index 0000000000000..bd7bc3e0c1f69 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_structs.fixed @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/src/tools/clippy/tests/ui/use_self_structs.rs b/src/tools/clippy/tests/ui/use_self_structs.rs new file mode 100644 index 0000000000000..624f158164ab0 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_structs.rs @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/src/tools/clippy/tests/ui/use_self_structs.stderr b/src/tools/clippy/tests/ui/use_self_structs.stderr new file mode 100644 index 0000000000000..766d1d4fd2c05 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_structs.stderr @@ -0,0 +1,77 @@ +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:7:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:17:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:38:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:62:22 + | +LL | left: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:64:23 + | +LL | right: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:75:23 + | +LL | right: Option>, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:80:33 + | +LL | flag: Option>>>, + | ^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:88:25 + | +LL | struct Tuple(Option>); + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:97:46 + | +LL | flag: Vec>>>>>>, + | ^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:108:20 + | +LL | flag: Wrappers, + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:117:23 + | +LL | flag: [Option>>; N], + | ^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:127:14 + | +LL | Cons(Box), + | ^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/volatile_composites.rs b/src/tools/clippy/tests/ui/volatile_composites.rs new file mode 100644 index 0000000000000..e7e7dafe18afa --- /dev/null +++ b/src/tools/clippy/tests/ui/volatile_composites.rs @@ -0,0 +1,221 @@ +#![feature(ptr_metadata)] +#![feature(portable_simd)] +#![warn(clippy::volatile_composites)] + +use std::ptr::null_mut; + +#[repr(C)] +#[derive(Copy, Clone, Default)] +struct MyDevRegisters { + baseaddr: usize, + count: usize, +} + +#[repr(transparent)] +struct Wrapper((), T, ()); + +// Not to be confused with std::ptr::NonNull +struct NonNull(T); + +impl NonNull { + fn write_volatile(&self, _arg: &T) { + unimplemented!("Something entirely unrelated to std::ptr::NonNull"); + } +} + +fn main() { + let regs = MyDevRegisters { + baseaddr: 0xabc123, + count: 42, + }; + + const DEVICE_ADDR: *mut MyDevRegisters = 0xdead as *mut _; + + // Raw pointer methods + unsafe { + (&raw mut (*DEVICE_ADDR).baseaddr).write_volatile(regs.baseaddr); // OK + (&raw mut (*DEVICE_ADDR).count).write_volatile(regs.count); // OK + + DEVICE_ADDR.write_volatile(regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: (&raw const (*DEVICE_ADDR).baseaddr).read_volatile(), // OK + count: (&raw const (*DEVICE_ADDR).count).read_volatile(), // OK + }; + + let _regs = DEVICE_ADDR.read_volatile(); + //~^ volatile_composites + } + + // std::ptr functions + unsafe { + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + std::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = std::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // core::ptr functions + unsafe { + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + core::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = core::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // std::ptr::NonNull + unsafe { + let ptr = std::ptr::NonNull::new(DEVICE_ADDR).unwrap(); + + ptr.write_volatile(regs); + //~^ volatile_composites + + let _regs = ptr.read_volatile(); + //~^ volatile_composites + } + + // Red herring + { + let thing = NonNull("hello".to_string()); + + thing.write_volatile(&"goodbye".into()); // OK + } + + // Zero size types OK + unsafe { + struct Empty; + + (0xdead as *mut Empty).write_volatile(Empty); // OK + // Note that this is OK because Wrapper is itself ZST, not because of the repr transparent + // handling tested below. + (0xdead as *mut Wrapper).write_volatile(Wrapper((), Empty, ())); // OK + } + + // Via repr transparent newtype + unsafe { + (0xdead as *mut Wrapper).write_volatile(Wrapper((), 123, ())); // OK + (0xdead as *mut Wrapper>).write_volatile(Wrapper((), Wrapper((), 123, ()), ())); // OK + + (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + //~^ volatile_composites + } + + // Plain type alias OK + unsafe { + type MyU64 = u64; + + (0xdead as *mut MyU64).write_volatile(123); // OK + } + + // Wide pointers are not OK as data + unsafe { + let things: &[u32] = &[1, 2, 3]; + + (0xdead as *mut *const u32).write_volatile(things.as_ptr()); // OK + + let wideptr: *const [u32] = std::ptr::from_raw_parts(things.as_ptr(), things.len()); + (0xdead as *mut *const [u32]).write_volatile(wideptr); + //~^ volatile_composites + } + + // Plain pointers and pointers with lifetimes are OK + unsafe { + let v: u32 = 123; + let rv: &u32 = &v; + + (0xdead as *mut &u32).write_volatile(rv); // OK + } + + // C-style enums are OK + unsafe { + // Bad: need some specific repr + enum PlainEnum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + //~^ volatile_composites + + // OK + #[repr(u32)] + enum U32Enum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut U32Enum).write_volatile(U32Enum::A); // OK + + // OK + #[repr(C)] + enum CEnum { + A = 1, + B = 2, + C = 3, + } + (0xdead as *mut CEnum).write_volatile(CEnum::A); // OK + + // Nope + enum SumType { + A(String), + B(u32), + C, + } + (0xdead as *mut SumType).write_volatile(SumType::C); + //~^ volatile_composites + + // A repr on a complex sum type is not good enough + #[repr(C)] + enum ReprSumType { + A(String), + B(u32), + C, + } + (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + //~^ volatile_composites + } + + // SIMD is OK + unsafe { + (0xdead as *mut std::simd::u32x4).write_volatile(std::simd::u32x4::splat(1)); // OK + } + + // Can't see through generic wrapper + unsafe { + do_device_write::(0xdead as *mut _, Default::default()); // OK + } + + let mut s = String::from("foo"); + unsafe { + std::ptr::write_volatile(&mut s, String::from("bar")); + //~^ volatile_composites + } +} + +// Generic OK +unsafe fn do_device_write(ptr: *mut T, v: T) { + unsafe { + ptr.write_volatile(v); // OK + } +} diff --git a/src/tools/clippy/tests/ui/volatile_composites.stderr b/src/tools/clippy/tests/ui/volatile_composites.stderr new file mode 100644 index 0000000000000..1545fc913ed00 --- /dev/null +++ b/src/tools/clippy/tests/ui/volatile_composites.stderr @@ -0,0 +1,89 @@ +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:39:9 + | +LL | DEVICE_ADDR.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::volatile-composites` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::volatile_composites)]` + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:47:21 + | +LL | let _regs = DEVICE_ADDR.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:56:9 + | +LL | std::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:64:21 + | +LL | let _regs = std::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:73:9 + | +LL | core::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:81:21 + | +LL | let _regs = core::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:89:9 + | +LL | ptr.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:92:21 + | +LL | let _regs = ptr.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^ + +error: type `Wrapper` is not volatile-compatible + --> tests/ui/volatile_composites.rs:118:9 + | +LL | (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `*const [u32]` is not volatile-compatible + --> tests/ui/volatile_composites.rs:136:9 + | +LL | (0xdead as *mut *const [u32]).write_volatile(wideptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::PlainEnum` is not volatile-compatible + --> tests/ui/volatile_composites.rs:157:9 + | +LL | (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::SumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:185:9 + | +LL | (0xdead as *mut SumType).write_volatile(SumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::ReprSumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:195:9 + | +LL | (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `std::string::String` is not volatile-compatible + --> tests/ui/volatile_composites.rs:211:9 + | +LL | std::ptr::write_volatile(&mut s, String::from("bar")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/while_let_loop.rs b/src/tools/clippy/tests/ui/while_let_loop.rs index 95062c9f46c7d..f28c504742fd7 100644 --- a/src/tools/clippy/tests/ui/while_let_loop.rs +++ b/src/tools/clippy/tests/ui/while_let_loop.rs @@ -22,6 +22,19 @@ fn main() { break; } + loop { + //~^ while_let_loop + let Some(_x) = y else { break }; + } + + loop { + // no error, else branch does something other than break + let Some(_x) = y else { + let _z = 1; + break; + }; + } + loop { //~^ while_let_loop diff --git a/src/tools/clippy/tests/ui/while_let_loop.stderr b/src/tools/clippy/tests/ui/while_let_loop.stderr index ed42628a53e7f..b9aee6eb42ecf 100644 --- a/src/tools/clippy/tests/ui/while_let_loop.stderr +++ b/src/tools/clippy/tests/ui/while_let_loop.stderr @@ -17,6 +17,15 @@ error: this loop could be written as a `while let` loop | LL | / loop { LL | | +LL | | let Some(_x) = y else { break }; +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:38:5 + | +LL | / loop { +LL | | LL | | LL | | match y { ... | @@ -25,7 +34,7 @@ LL | | } | |_____^ help: try: `while let Some(_x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:34:5 + --> tests/ui/while_let_loop.rs:47:5 | LL | / loop { LL | | @@ -37,7 +46,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:45:5 + --> tests/ui/while_let_loop.rs:58:5 | LL | / loop { LL | | @@ -48,7 +57,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:77:5 + --> tests/ui/while_let_loop.rs:90:5 | LL | / loop { LL | | @@ -68,7 +77,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:167:9 + --> tests/ui/while_let_loop.rs:180:9 | LL | / loop { LL | | @@ -88,7 +97,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:182:5 + --> tests/ui/while_let_loop.rs:195:5 | LL | / loop { LL | | @@ -107,7 +116,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:194:5 + --> tests/ui/while_let_loop.rs:207:5 | LL | / loop { LL | | @@ -126,7 +135,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:206:5 + --> tests/ui/while_let_loop.rs:219:5 | LL | / loop { LL | | @@ -137,7 +146,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = Some(3) { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:218:5 + --> tests/ui/while_let_loop.rs:231:5 | LL | / loop { LL | | @@ -156,7 +165,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:230:5 + --> tests/ui/while_let_loop.rs:243:5 | LL | / loop { LL | | @@ -177,5 +186,5 @@ LL + .. LL + } | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed b/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed index e6c451ce7399e..b5fca36f3f089 100644 --- a/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); @@ -13,36 +14,47 @@ fn main() { // should trigger // on arrays - f(); let a: [i32; 0] = []; + f(); + let a: [i32; 0] = []; //~^ zero_repeat_side_effects let mut b; - f(); b = [] as [i32; 0]; + f(); + b = [] as [i32; 0]; //~^ zero_repeat_side_effects // on vecs // vecs dont support inferring value of consts - f(); let c: std::vec::Vec = vec![]; + f(); + let c: std::vec::Vec = vec![]; //~^ zero_repeat_side_effects let d; - f(); d = vec![] as std::vec::Vec; + f(); + d = vec![] as std::vec::Vec; //~^ zero_repeat_side_effects // for macros - println!("side effect"); let e: [(); 0] = []; + println!("side effect"); + let e: [(); 0] = []; //~^ zero_repeat_side_effects // for nested calls - { f() }; let g: [i32; 0] = []; + { f() }; + let g: [i32; 0] = []; //~^ zero_repeat_side_effects // as function param - drop({ f(); vec![] as std::vec::Vec }); + drop({ + f(); + vec![] as std::vec::Vec + }); //~^ zero_repeat_side_effects // when singled out/not part of assignment/local - { f(); vec![] as std::vec::Vec }; + f(); + vec![] as std::vec::Vec; //~^ zero_repeat_side_effects - { f(); [] as [i32; 0] }; + f(); + [] as [i32; 0]; //~^ zero_repeat_side_effects // should not trigger @@ -96,8 +108,14 @@ fn issue_14681() { foo(&[Some(0i64); 0]); foo(&[Some(Some(0i64)); 0]); - foo(&{ Some(f()); [] as [std::option::Option; 0] }); + foo(&{ + Some(f()); + [] as [std::option::Option; 0] + }); //~^ zero_repeat_side_effects - foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + foo(&{ + Some(Some(S::new())); + [] as [std::option::Option>; 0] + }); //~^ zero_repeat_side_effects } diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs b/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs index f8a497976aa43..ea043d21638cc 100644 --- a/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr b/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr index 771b71c686ae4..49e850d035343 100644 --- a/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr @@ -1,5 +1,5 @@ error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:16:5 + --> tests/ui/zero_repeat_side_effects.rs:17:5 | LL | let a = [f(); 0]; | ^^^^^^^^^^^^^^^^^ @@ -8,128 +8,134 @@ LL | let a = [f(); 0]; = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` help: consider performing the side effect separately | -LL - let a = [f(); 0]; -LL + f(); let a: [i32; 0] = []; +LL ~ f(); +LL + let a: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:19:5 + --> tests/ui/zero_repeat_side_effects.rs:20:5 | LL | b = [f(); 0]; | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - b = [f(); 0]; -LL + f(); b = [] as [i32; 0]; +LL ~ f(); +LL ~ b = [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:24:5 + --> tests/ui/zero_repeat_side_effects.rs:25:5 | LL | let c = vec![f(); 0]; | ^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - let c = vec![f(); 0]; -LL + f(); let c: std::vec::Vec = vec![]; +LL ~ f(); +LL + let c: std::vec::Vec = vec![]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:27:5 + --> tests/ui/zero_repeat_side_effects.rs:28:5 | LL | d = vec![f(); 0]; | ^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - d = vec![f(); 0]; -LL + f(); d = vec![] as std::vec::Vec; +LL ~ f(); +LL ~ d = vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:31:5 + --> tests/ui/zero_repeat_side_effects.rs:32:5 | LL | let e = [println!("side effect"); 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - let e = [println!("side effect"); 0]; -LL + println!("side effect"); let e: [(); 0] = []; +LL ~ println!("side effect"); +LL + let e: [(); 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:35:5 + --> tests/ui/zero_repeat_side_effects.rs:36:5 | LL | let g = [{ f() }; 0]; | ^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - let g = [{ f() }; 0]; -LL + { f() }; let g: [i32; 0] = []; +LL ~ { f() }; +LL + let g: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:39:10 + --> tests/ui/zero_repeat_side_effects.rs:40:10 | LL | drop(vec![f(); 0]); | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - drop(vec![f(); 0]); -LL + drop({ f(); vec![] as std::vec::Vec }); +LL ~ drop({ +LL + f(); +LL + vec![] as std::vec::Vec +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:43:5 + --> tests/ui/zero_repeat_side_effects.rs:44:5 | LL | vec![f(); 0]; | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - vec![f(); 0]; -LL + { f(); vec![] as std::vec::Vec }; +LL ~ f(); +LL ~ vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:45:5 + --> tests/ui/zero_repeat_side_effects.rs:46:5 | LL | [f(); 0]; | ^^^^^^^^ | help: consider performing the side effect separately | -LL - [f(); 0]; -LL + { f(); [] as [i32; 0] }; +LL ~ f(); +LL ~ [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:99:10 + --> tests/ui/zero_repeat_side_effects.rs:100:10 | LL | foo(&[Some(f()); 0]); | ^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - foo(&[Some(f()); 0]); -LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); +LL ~ foo(&{ +LL + Some(f()); +LL + [] as [std::option::Option; 0] +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:101:10 + --> tests/ui/zero_repeat_side_effects.rs:102:10 | LL | foo(&[Some(Some(S::new())); 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - foo(&[Some(Some(S::new())); 0]); -LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); +LL ~ foo(&{ +LL + Some(Some(S::new())); +LL + [] as [std::option::Option>; 0] +LL ~ }); | error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.fixed b/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.fixed new file mode 100644 index 0000000000000..3d037516f75c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + panic!(); + let _data: [!; 0] = []; + //~^ zero_repeat_side_effects +} diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.rs b/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.rs new file mode 100644 index 0000000000000..3dc1929bcdc76 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.rs @@ -0,0 +1,9 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.stderr b/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.stderr new file mode 100644 index 0000000000000..280955740cc4c --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects_never_pattern.stderr @@ -0,0 +1,16 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_never_pattern.rs:7:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` +help: consider performing the side effect separately + | +LL ~ panic!(); +LL + let _data: [!; 0] = []; + | + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects_unfixable.rs b/src/tools/clippy/tests/ui/zero_repeat_side_effects_unfixable.rs new file mode 100644 index 0000000000000..82f0884056ab0 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::zero_repeat_side_effects)] +#![expect(clippy::diverging_sub_expression)] + +fn issue_14998() { + // unnameable types, don't suggest + let _data = [|| 3i32; 0]; + //~^ zero_repeat_side_effects + + // unnameable type because `never_type` is not enabled, don't suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects_unfixable.stderr b/src/tools/clippy/tests/ui/zero_repeat_side_effects_unfixable.stderr new file mode 100644 index 0000000000000..450617f3782c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects_unfixable.stderr @@ -0,0 +1,20 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:7:5 + | +LL | let _data = [|| 3i32; 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:11:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index b2fb50918f583..db951b95ef50a 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -45,6 +45,9 @@ reviewed_label = "S-waiting-on-author" [autolabel."S-waiting-on-review"] new_pr = true +[autolabel."needs-fcp"] +trigger_files = ["clippy_lints/src/declared_lints.rs"] + [concern] # These labels are set when there are unresolved concerns, removed otherwise labels = ["S-waiting-on-concerns"] @@ -60,6 +63,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", + "blyxyas", ] [assign.owners]