Merge commit 'f4850f7292efa33759b4f7f9b7621268979e9914' into clippyup

This commit is contained in:
Philipp Krones 2022-11-21 20:34:47 +01:00
parent 3597ed5a09
commit 46c5a5d234
895 changed files with 8247 additions and 18379 deletions

View file

@ -4,13 +4,157 @@ All notable changes to this project will be documented in this file.
See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
document. document.
## Unreleased / In Rust Nightly ## Unreleased / Beta / In Rust Nightly
[3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master) [b52fb523...master](https://github.com/rust-lang/rust-clippy/compare/b52fb523...master)
## Rust 1.65
Current stable, released 2022-11-03
[3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523)
### Important Changes
* Clippy now has an `--explain <LINT>` command to show the lint description in the console
[#8952](https://github.com/rust-lang/rust-clippy/pull/8952)
### New Lints
* [`unused_peekable`]
[#9258](https://github.com/rust-lang/rust-clippy/pull/9258)
* [`collapsible_str_replace`]
[#9269](https://github.com/rust-lang/rust-clippy/pull/9269)
* [`manual_string_new`]
[#9295](https://github.com/rust-lang/rust-clippy/pull/9295)
* [`iter_on_empty_collections`]
[#9187](https://github.com/rust-lang/rust-clippy/pull/9187)
* [`iter_on_single_items`]
[#9187](https://github.com/rust-lang/rust-clippy/pull/9187)
* [`bool_to_int_with_if`]
[#9412](https://github.com/rust-lang/rust-clippy/pull/9412)
* [`multi_assignments`]
[#9379](https://github.com/rust-lang/rust-clippy/pull/9379)
* [`result_large_err`]
[#9373](https://github.com/rust-lang/rust-clippy/pull/9373)
* [`partialeq_to_none`]
[#9288](https://github.com/rust-lang/rust-clippy/pull/9288)
* [`suspicious_to_owned`]
[#8984](https://github.com/rust-lang/rust-clippy/pull/8984)
* [`cast_slice_from_raw_parts`]
[#9247](https://github.com/rust-lang/rust-clippy/pull/9247)
* [`manual_instant_elapsed`]
[#9264](https://github.com/rust-lang/rust-clippy/pull/9264)
### Moves and Deprecations
* Moved [`significant_drop_in_scrutinee`] to `nursery` (now allow-by-default)
[#9302](https://github.com/rust-lang/rust-clippy/pull/9302)
* Rename `logic_bug` to [`overly_complex_bool_expr`]
[#9306](https://github.com/rust-lang/rust-clippy/pull/9306)
* Rename `arithmetic` to [`arithmetic_side_effects`]
[#9443](https://github.com/rust-lang/rust-clippy/pull/9443)
* Moved [`only_used_in_recursion`] to complexity (now warn-by-default)
[#8804](https://github.com/rust-lang/rust-clippy/pull/8804)
* Moved [`assertions_on_result_states`] to restriction (now allow-by-default)
[#9273](https://github.com/rust-lang/rust-clippy/pull/9273)
* Renamed `blacklisted_name` to [`disallowed_names`]
[#8974](https://github.com/rust-lang/rust-clippy/pull/8974)
### Enhancements
* [`option_if_let_else`]: Now also checks for match expressions
[#8696](https://github.com/rust-lang/rust-clippy/pull/8696)
* [`explicit_auto_deref`]: Now lints on implicit returns in closures
[#9126](https://github.com/rust-lang/rust-clippy/pull/9126)
* [`needless_borrow`]: Now considers trait implementations
[#9136](https://github.com/rust-lang/rust-clippy/pull/9136)
* [`suboptimal_flops`], [`imprecise_flops`]: Now lint on constant expressions
[#9404](https://github.com/rust-lang/rust-clippy/pull/9404)
* [`if_let_mutex`]: Now detects mutex behind references and warns about deadlocks
[#9318](https://github.com/rust-lang/rust-clippy/pull/9318)
### False Positive Fixes
* [`unit_arg`] [`default_trait_access`] [`missing_docs_in_private_items`]: No longer
trigger in code generated from proc-macros
[#8694](https://github.com/rust-lang/rust-clippy/pull/8694)
* [`unwrap_used`]: Now lints uses of `unwrap_err`
[#9338](https://github.com/rust-lang/rust-clippy/pull/9338)
* [`expect_used`]: Now lints uses of `expect_err`
[#9338](https://github.com/rust-lang/rust-clippy/pull/9338)
* [`transmute_undefined_repr`]: Now longer lints if the first field is compatible
with the other type
[#9287](https://github.com/rust-lang/rust-clippy/pull/9287)
* [`unnecessary_to_owned`]: No longer lints, if type change cased errors in
the caller function
[#9424](https://github.com/rust-lang/rust-clippy/pull/9424)
* [`match_like_matches_macro`]: No longer lints, if there are comments inside the
match expression
[#9276](https://github.com/rust-lang/rust-clippy/pull/9276)
* [`partialeq_to_none`]: No longer trigger in code generated from macros
[#9389](https://github.com/rust-lang/rust-clippy/pull/9389)
* [`arithmetic_side_effects`]: No longer lints expressions that only use literals
[#9365](https://github.com/rust-lang/rust-clippy/pull/9365)
* [`explicit_auto_deref`]: Now ignores references on block expressions when the type
is `Sized`, on `dyn Trait` returns and when the suggestion is non-trivial
[#9126](https://github.com/rust-lang/rust-clippy/pull/9126)
* [`trait_duplication_in_bounds`]: Now better tracks bounds to avoid false positives
[#9167](https://github.com/rust-lang/rust-clippy/pull/9167)
* [`format_in_format_args`]: Now suggests cases where the result is formatted again
[#9349](https://github.com/rust-lang/rust-clippy/pull/9349)
* [`only_used_in_recursion`]: No longer lints on function without recursions and
takes external functions into account
[#8804](https://github.com/rust-lang/rust-clippy/pull/8804)
* [`missing_const_for_fn`]: No longer lints in proc-macros
[#9308](https://github.com/rust-lang/rust-clippy/pull/9308)
* [`non_ascii_literal`]: Allow non-ascii comments in tests and make sure `#[allow]`
attributes work in tests
[#9327](https://github.com/rust-lang/rust-clippy/pull/9327)
* [`question_mark`]: No longer lint `if let`s with subpatterns
[#9348](https://github.com/rust-lang/rust-clippy/pull/9348)
* [`needless_collect`]: No longer lints in loops
[#8992](https://github.com/rust-lang/rust-clippy/pull/8992)
* [`mut_mutex_lock`]: No longer lints if the mutex is behind an immutable reference
[#9418](https://github.com/rust-lang/rust-clippy/pull/9418)
* [`needless_return`]: Now ignores returns with arguments
[#9381](https://github.com/rust-lang/rust-clippy/pull/9381)
* [`range_plus_one`], [`range_minus_one`]: Now ignores code with macros
[#9446](https://github.com/rust-lang/rust-clippy/pull/9446)
* [`assertions_on_result_states`]: No longer lints on the unit type
[#9273](https://github.com/rust-lang/rust-clippy/pull/9273)
### Suggestion Fixes/Improvements
* [`unwrap_or_else_default`]: Now suggests `unwrap_or_default()` for empty strings
[#9421](https://github.com/rust-lang/rust-clippy/pull/9421)
* [`if_then_some_else_none`]: Now also suggests `bool::then_some`
[#9289](https://github.com/rust-lang/rust-clippy/pull/9289)
* [`redundant_closure_call`]: The suggestion now works for async closures
[#9053](https://github.com/rust-lang/rust-clippy/pull/9053)
* [`suboptimal_flops`]: Now suggests parenthesis when they are required
[#9394](https://github.com/rust-lang/rust-clippy/pull/9394)
* [`case_sensitive_file_extension_comparisons`]: Now suggests `map_or(..)` instead of `map(..).unwrap_or`
[#9341](https://github.com/rust-lang/rust-clippy/pull/9341)
* Deprecated configuration values can now be updated automatically
[#9252](https://github.com/rust-lang/rust-clippy/pull/9252)
* [`or_fun_call`]: Now suggest `Entry::or_default` for `Entry::or_insert(Default::default())`
[#9342](https://github.com/rust-lang/rust-clippy/pull/9342)
* [`unwrap_used`]: Only suggests `expect` if [`expect_used`] is allowed
[#9223](https://github.com/rust-lang/rust-clippy/pull/9223)
### ICE Fixes
* Fix ICE in [`useless_format`] for literals
[#9406](https://github.com/rust-lang/rust-clippy/pull/9406)
* Fix infinite loop in [`vec_init_then_push`]
[#9441](https://github.com/rust-lang/rust-clippy/pull/9441)
* Fix ICE when reading literals with weird proc-macro spans
[#9303](https://github.com/rust-lang/rust-clippy/pull/9303)
## Rust 1.64 ## Rust 1.64
Current stable, released 2022-09-22 Released 2022-09-22
[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc) [d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc)
@ -3903,6 +4047,7 @@ Released 2018-09-13
[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
[`from_raw_with_void_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_raw_with_void_ptr
[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first [`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
@ -3978,6 +4123,7 @@ Released 2018-09-13
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop [`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
[`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future
[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
@ -3996,6 +4142,8 @@ Released 2018-09-13
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
@ -4198,6 +4346,8 @@ Released 2018-09-13
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method [`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
[`seek_from_current`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current
[`seek_to_start_instead_of_rewind`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_to_start_instead_of_rewind
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
@ -4247,6 +4397,7 @@ Released 2018-09-13
[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned [`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
[`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow
[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
@ -4277,6 +4428,7 @@ Released 2018-09-13
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction
[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
@ -4298,6 +4450,7 @@ Released 2018-09-13
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`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_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned

View file

@ -1,70 +1,3 @@
# The Rust Code of Conduct # The Rust Code of Conduct
A version of this document [can be found online](https://www.rust-lang.org/conduct.html). The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html).
## Conduct
**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience,
gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, nationality, or other similar characteristic.
* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and
welcoming environment for all.
* Please be kind and courteous. There's no need to be mean or rude.
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and
numerous costs. There is seldom a right answer.
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and
see how it works.
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We
interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen
Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their
definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or
made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation
team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a
safe place for you and we've got your back.
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
## Moderation
These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation,
please contact the [Rust moderation team][mod_team].
1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks,
are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
3. Moderators will first respond to such remarks with a warning.
4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended
party a genuine apology.
7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a
different moderator, **in private**. Complaints about bans in-channel are not allowed.
8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate
situation, they should expect less leeway than others.
In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically
unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly
if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can
drive people away from the community entirely.
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was
they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good
there was something you could've communicated better — remember that it's your responsibility to make your fellow
Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about
cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their
trust.
The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust,
#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo);
GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org
(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the
maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider
explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the
[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
[mod_team]: https://www.rust-lang.org/team.html#Moderation-team

View file

@ -23,6 +23,7 @@ All contributors are expected to follow the [Rust Code of Conduct].
- [Issue and PR triage](#issue-and-pr-triage) - [Issue and PR triage](#issue-and-pr-triage)
- [Bors and Homu](#bors-and-homu) - [Bors and Homu](#bors-and-homu)
- [Contributions](#contributions) - [Contributions](#contributions)
- [License](#license)
[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy [Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
@ -245,6 +246,38 @@ Contributions to Clippy should be made in the form of GitHub pull requests. Each
be reviewed by a core contributor (someone with permission to land patches) and either landed in the be reviewed by a core contributor (someone with permission to land patches) and either landed in the
main tree or given feedback for changes that would be required. main tree or given feedback for changes that would be required.
All PRs should include a `changelog` entry with a short comment explaining the change. The rule of thumb is basically,
"what do you believe is important from an outsider's perspective?" Often, PRs are only related to a single property of a
lint, and then it's good to mention that one. Otherwise, it's better to include too much detail than too little.
Clippy's [changelog] is created from these comments. Every release, someone gets all commits from bors with a
`changelog: XYZ` entry and combines them into the changelog. This is a manual process.
Examples:
- New lint
```
changelog: new lint: [`missing_trait_methods`]
```
- False positive fix
```
changelog: Fix [`unused_peekable`] false positive when peeked in a closure or called as `f(&mut peekable)`
```
- Purely internal change
```
changelog: none
```
Note this it is fine for a PR to include multiple `changelog` entries, e.g.:
```
changelog: Something 1
changelog: Something 2
changelog: Something 3
```
[changelog]: CHANGELOG.md
## License
All code in this repository is under the [Apache-2.0] or the [MIT] license. All code in this repository is under the [Apache-2.0] or the [MIT] license.
<!-- adapted from https://github.com/servo/servo/blob/master/CONTRIBUTING.md --> <!-- adapted from https://github.com/servo/servo/blob/master/CONTRIBUTING.md -->

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.1.66" version = "0.1.67"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"

View file

@ -1,6 +1,6 @@
# Clippy # Clippy
[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) [![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto)
[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license) [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
@ -204,12 +204,6 @@ lints can be configured and the meaning of the variables.
> >
> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. > `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints.
> **Note**
>
> Configuration changes will not apply for code that has already been compiled and cached under `./target/`;
> for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure
> that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch.
To deactivate the “for further information visit *lint-link*” message you can To deactivate the “for further information visit *lint-link*” message you can
define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.

View file

@ -10,7 +10,6 @@ indoc = "1.0"
itertools = "0.10.1" itertools = "0.10.1"
opener = "0.5" opener = "0.5"
shell-escape = "0.1" shell-escape = "0.1"
tempfile = "3.2"
walkdir = "2.3" walkdir = "2.3"
[features] [features]

View file

@ -36,20 +36,12 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
} else { } else {
exit_if_err(Command::new("cargo").arg("build").status()); exit_if_err(Command::new("cargo").arg("build").status());
// Run in a tempdir as changes to clippy do not retrigger linting
let target = tempfile::Builder::new()
.prefix("clippy")
.tempdir()
.expect("failed to create tempdir");
let status = Command::new(cargo_clippy_path()) let status = Command::new(cargo_clippy_path())
.arg("clippy") .arg("clippy")
.args(args) .args(args)
.current_dir(path) .current_dir(path)
.env("CARGO_TARGET_DIR", target.as_ref())
.status(); .status();
target.close().expect("failed to remove tempdir");
exit_if_err(status); exit_if_err(status);
} }
} }

View file

@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder;
use indoc::writedoc; use indoc::writedoc;
use itertools::Itertools; use itertools::Itertools;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use std::collections::{BTreeSet, HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt::Write; use std::fmt::Write;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
@ -36,6 +36,60 @@ pub enum UpdateMode {
pub fn update(update_mode: UpdateMode) { pub fn update(update_mode: UpdateMode) {
let (lints, deprecated_lints, renamed_lints) = gather_all(); let (lints, deprecated_lints, renamed_lints) = gather_all();
generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
remove_old_files(update_mode);
}
/// Remove files no longer needed after <https://github.com/rust-lang/rust-clippy/pull/9541>
/// that may be reintroduced unintentionally
///
/// FIXME: This is a temporary measure that should be removed when there are no more PRs that
/// include the stray files
fn remove_old_files(update_mode: UpdateMode) {
let mut failed = false;
let mut remove_file = |path: &Path| match update_mode {
UpdateMode::Check => {
if path.exists() {
failed = true;
println!("unexpected file: {}", path.display());
}
},
UpdateMode::Change => {
if fs::remove_file(path).is_ok() {
println!("removed file: {}", path.display());
}
},
};
let files = [
"clippy_lints/src/lib.register_all.rs",
"clippy_lints/src/lib.register_cargo.rs",
"clippy_lints/src/lib.register_complexity.rs",
"clippy_lints/src/lib.register_correctness.rs",
"clippy_lints/src/lib.register_internal.rs",
"clippy_lints/src/lib.register_lints.rs",
"clippy_lints/src/lib.register_nursery.rs",
"clippy_lints/src/lib.register_pedantic.rs",
"clippy_lints/src/lib.register_perf.rs",
"clippy_lints/src/lib.register_restriction.rs",
"clippy_lints/src/lib.register_style.rs",
"clippy_lints/src/lib.register_suspicious.rs",
"src/docs.rs",
];
for file in files {
remove_file(Path::new(file));
}
if let Ok(docs_dir) = fs::read_dir("src/docs") {
for doc_file in docs_dir {
let path = doc_file.unwrap().path();
remove_file(&path);
}
}
if failed {
exit_with_failure();
}
} }
fn generate_lint_files( fn generate_lint_files(
@ -104,9 +158,9 @@ fn generate_lint_files(
); );
process_file( process_file(
"clippy_lints/src/lib.register_lints.rs", "clippy_lints/src/declared_lints.rs",
update_mode, update_mode,
&gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), &gen_declared_lints(internal_lints.iter(), usable_lints.iter()),
); );
process_file( process_file(
"clippy_lints/src/lib.deprecated.rs", "clippy_lints/src/lib.deprecated.rs",
@ -114,26 +168,6 @@ fn generate_lint_files(
&gen_deprecated(deprecated_lints), &gen_deprecated(deprecated_lints),
); );
let all_group_lints = usable_lints.iter().filter(|l| {
matches!(
&*l.group,
"correctness" | "suspicious" | "style" | "complexity" | "perf"
)
});
let content = gen_lint_group_list("all", all_group_lints);
process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
update_docs(update_mode, &usable_lints);
for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
let content = gen_lint_group_list(&lint_group, lints.iter());
process_file(
format!("clippy_lints/src/lib.register_{lint_group}.rs"),
update_mode,
&content,
);
}
let content = gen_deprecated_lints_test(deprecated_lints); let content = gen_deprecated_lints_test(deprecated_lints);
process_file("tests/ui/deprecated.rs", update_mode, &content); process_file("tests/ui/deprecated.rs", update_mode, &content);
@ -141,62 +175,6 @@ fn generate_lint_files(
process_file("tests/ui/rename.rs", update_mode, &content); process_file("tests/ui/rename.rs", update_mode, &content);
} }
fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
writeln!(res, r#" "{name}","#).unwrap();
}
});
if update_mode == UpdateMode::Check {
let mut extra = BTreeSet::new();
let mut lint_names = usable_lints
.iter()
.map(|lint| lint.name.clone())
.collect::<BTreeSet<_>>();
for file in std::fs::read_dir("src/docs").unwrap() {
let filename = file.unwrap().file_name().into_string().unwrap();
if let Some(name) = filename.strip_suffix(".txt") {
if !lint_names.remove(name) {
extra.insert(name.to_string());
}
}
}
let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);
if failed {
exit_with_failure();
}
} else {
if std::fs::remove_dir_all("src/docs").is_err() {
eprintln!("could not remove src/docs directory");
}
if std::fs::create_dir("src/docs").is_err() {
eprintln!("could not recreate src/docs directory");
}
}
for lint in usable_lints {
process_file(
Path::new("src/docs").join(lint.name.clone() + ".txt"),
update_mode,
&lint.documentation,
);
}
}
fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
if lints.is_empty() {
return false;
}
println!("{header}");
for lint in lints.iter().sorted() {
println!(" {lint}");
}
println!();
true
}
pub fn print_lints() { pub fn print_lints() {
let (lint_list, _, _) = gather_all(); let (lint_list, _, _) = gather_all();
let usable_lints = Lint::usable_lints(&lint_list); let usable_lints = Lint::usable_lints(&lint_list);
@ -641,26 +619,17 @@ struct Lint {
desc: String, desc: String,
module: String, module: String,
declaration_range: Range<usize>, declaration_range: Range<usize>,
documentation: String,
} }
impl Lint { impl Lint {
#[must_use] #[must_use]
fn new( fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
name: &str,
group: &str,
desc: &str,
module: &str,
declaration_range: Range<usize>,
documentation: String,
) -> Self {
Self { Self {
name: name.to_lowercase(), name: name.to_lowercase(),
group: group.into(), group: group.into(),
desc: remove_line_splices(desc), desc: remove_line_splices(desc),
module: module.into(), module: module.into(),
declaration_range, declaration_range,
documentation,
} }
} }
@ -716,25 +685,6 @@ impl RenamedLint {
} }
} }
/// Generates the code for registering a group
fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
details.sort_unstable();
let mut output = GENERATED_FILE_COMMENT.to_string();
let _ = writeln!(
output,
"store.register_group(true, \"clippy::{group_name}\", Some(\"clippy_{group_name}\"), vec![",
);
for (module, name) in details {
let _ = writeln!(output, " LintId::of({module}::{name}),");
}
output.push_str("])\n");
output
}
/// Generates the `register_removed` code /// Generates the `register_removed` code
#[must_use] #[must_use]
fn gen_deprecated(lints: &[DeprecatedLint]) -> String { fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
@ -759,7 +709,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
/// Generates the code for registering lints /// Generates the code for registering lints
#[must_use] #[must_use]
fn gen_register_lint_list<'a>( fn gen_declared_lints<'a>(
internal_lints: impl Iterator<Item = &'a Lint>, internal_lints: impl Iterator<Item = &'a Lint>,
usable_lints: impl Iterator<Item = &'a Lint>, usable_lints: impl Iterator<Item = &'a Lint>,
) -> String { ) -> String {
@ -770,15 +720,15 @@ fn gen_register_lint_list<'a>(
details.sort_unstable(); details.sort_unstable();
let mut output = GENERATED_FILE_COMMENT.to_string(); let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("store.register_lints(&[\n"); output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n");
for (is_public, module_name, lint_name) in details { for (is_public, module_name, lint_name) in details {
if !is_public { if !is_public {
output.push_str(" #[cfg(feature = \"internal\")]\n"); output.push_str(" #[cfg(feature = \"internal\")]\n");
} }
let _ = writeln!(output, " {module_name}::{lint_name},"); let _ = writeln!(output, " crate::{module_name}::{lint_name}_INFO,");
} }
output.push_str("])\n"); output.push_str("];\n");
output output
} }
@ -910,35 +860,26 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
}| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint", }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
) { ) {
let start = range.start; let start = range.start;
let mut docs = String::with_capacity(128); let mut iter = iter
let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace)); .by_ref()
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
// matches `!{` // matches `!{`
match_tokens!(iter, Bang OpenBrace); match_tokens!(iter, Bang OpenBrace);
let mut in_code = false; match iter.next() {
while let Some(t) = iter.next() { // #[clippy::version = "version"] pub
match t.token_kind { Some(LintDeclSearchResult {
TokenKind::LineComment { .. } => { token_kind: TokenKind::Pound,
if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) { ..
if line.starts_with("```") { }) => {
docs += "```\n"; match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
in_code = !in_code; },
} else if !(in_code && line.starts_with("# ")) { // pub
docs += line; Some(LintDeclSearchResult {
docs.push('\n'); token_kind: TokenKind::Ident,
} ..
} }) => (),
}, _ => continue,
TokenKind::Pound => {
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
break;
},
TokenKind::Ident => {
break;
},
_ => {},
}
} }
docs.pop(); // remove final newline
let (name, group, desc) = match_tokens!( let (name, group, desc) = match_tokens!(
iter, iter,
@ -956,7 +897,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
.. ..
}) = iter.next() }) = iter.next()
{ {
lints.push(Lint::new(name, group, desc, module, start..range.end, docs)); lints.push(Lint::new(name, group, desc, module, start..range.end));
} }
} }
} }
@ -1186,7 +1127,6 @@ mod tests {
"\"really long text\"", "\"really long text\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
), ),
Lint::new( Lint::new(
"doc_markdown", "doc_markdown",
@ -1194,7 +1134,6 @@ mod tests {
"\"single line\"", "\"single line\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
), ),
]; ];
assert_eq!(expected, result); assert_eq!(expected, result);
@ -1234,7 +1173,6 @@ mod tests {
"\"abc\"", "\"abc\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
), ),
Lint::new( Lint::new(
"should_assert_eq2", "should_assert_eq2",
@ -1242,7 +1180,6 @@ mod tests {
"\"abc\"", "\"abc\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
), ),
Lint::new( Lint::new(
"should_assert_eq2", "should_assert_eq2",
@ -1250,7 +1187,6 @@ mod tests {
"\"abc\"", "\"abc\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
), ),
]; ];
let expected = vec![Lint::new( let expected = vec![Lint::new(
@ -1259,7 +1195,6 @@ mod tests {
"\"abc\"", "\"abc\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
)]; )];
assert_eq!(expected, Lint::usable_lints(&lints)); assert_eq!(expected, Lint::usable_lints(&lints));
} }
@ -1267,51 +1202,22 @@ mod tests {
#[test] #[test]
fn test_by_lint_group() { fn test_by_lint_group() {
let lints = vec![ let lints = vec![
Lint::new( Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new( Lint::new(
"should_assert_eq2", "should_assert_eq2",
"group2", "group2",
"\"abc\"", "\"abc\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
),
Lint::new(
"incorrect_match",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
), ),
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
]; ];
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new(); let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
expected.insert( expected.insert(
"group1".to_string(), "group1".to_string(),
vec![ vec![
Lint::new( Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
"should_assert_eq", Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"incorrect_match",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
], ],
); );
expected.insert( expected.insert(
@ -1322,7 +1228,6 @@ mod tests {
"\"abc\"", "\"abc\"",
"module_name", "module_name",
Range::default(), Range::default(),
String::new(),
)], )],
); );
assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
@ -1357,48 +1262,4 @@ mod tests {
assert_eq!(expected, gen_deprecated(&lints)); assert_eq!(expected, gen_deprecated(&lints));
} }
#[test]
fn test_gen_lint_group_list() {
let lints = vec![
Lint::new(
"abc",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"internal",
"internal_style",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
];
let expected = GENERATED_FILE_COMMENT.to_string()
+ &[
"store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
" LintId::of(module_name::ABC),",
" LintId::of(module_name::INTERNAL),",
" LintId::of(module_name::SHOULD_ASSERT_EQ),",
"])",
]
.join("\n")
+ "\n";
let result = gen_lint_group_list("group1", lints.iter());
assert_eq!(expected, result);
}
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.66" version = "0.1.67"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"
@ -11,6 +11,7 @@ edition = "2021"
[dependencies] [dependencies]
cargo_metadata = "0.14" cargo_metadata = "0.14"
clippy_utils = { path = "../clippy_utils" } clippy_utils = { path = "../clippy_utils" }
declare_clippy_lint = { path = "../declare_clippy_lint" }
if_chain = "1.0" if_chain = "1.0"
itertools = "0.10.1" itertools = "0.10.1"
pulldown-cmark = { version = "0.9", default-features = false } pulldown-cmark = { version = "0.9", default-features = false }

View file

@ -11,14 +11,14 @@ use rustc_errors::Applicability;
use rustc_hir::{ use rustc_hir::{
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
}; };
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_span::{sym, DUMMY_SP};
use semver::Version; use semver::Version;
static UNIX_SYSTEMS: &[&str] = &[ static UNIX_SYSTEMS: &[&str] = &[
@ -303,6 +303,26 @@ declare_lint_pass!(Attributes => [
]); ]);
impl<'tcx> LateLintPass<'tcx> for Attributes { impl<'tcx> LateLintPass<'tcx> for Attributes {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
for (name, level) in &cx.sess().opts.lint_opts {
if name == "clippy::restriction" && *level > Level::Allow {
span_lint_and_then(
cx,
BLANKET_CLIPPY_RESTRICTION_LINTS,
DUMMY_SP,
"`clippy::restriction` is not meant to be enabled as a group",
|diag| {
diag.note(format!(
"because of the command line `--{} clippy::restriction`",
level.as_str()
));
diag.help("enable the restriction lints you need individually");
},
);
}
}
}
fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
if let Some(items) = &attr.meta_item_list() { if let Some(items) = &attr.meta_item_list() {
if let Some(ident) = attr.ident() { if let Some(ident) = attr.ident() {
@ -358,7 +378,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
| "enum_glob_use" | "enum_glob_use"
| "redundant_pub_crate" | "redundant_pub_crate"
| "macro_use_imports" | "macro_use_imports"
| "unsafe_removed_from_name", | "unsafe_removed_from_name"
| "module_name_repetitions"
| "single_component_path_imports"
) )
}) })
{ {
@ -441,9 +463,9 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
cx, cx,
BLANKET_CLIPPY_RESTRICTION_LINTS, BLANKET_CLIPPY_RESTRICTION_LINTS,
lint.span(), lint.span(),
"restriction lints are not meant to be all enabled", "`clippy::restriction` is not meant to be enabled as a group",
None, None,
"try enabling only the lints you really need", "enable the restriction lints you need individually",
); );
} }
} }
@ -464,6 +486,11 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem
return; return;
} }
// Check if the attribute is in an external macro and therefore out of the developer's control
if in_external_macro(cx.sess(), attr.span) {
return;
}
span_lint_and_help( span_lint_and_help(
cx, cx,
ALLOW_ATTRIBUTES_WITHOUT_REASON, ALLOW_ATTRIBUTES_WITHOUT_REASON,

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths}; use clippy_utils::{match_def_path, paths};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -189,7 +188,7 @@ impl LateLintPass<'_> for AwaitHolding {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for conf in &self.conf_invalid_types { for conf in &self.conf_invalid_types {
let segs: Vec<_> = conf.path().split("::").collect(); let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.def_ids.insert(id, conf.clone()); self.def_ids.insert(id, conf.clone());
} }
} }

View file

@ -1,9 +1,10 @@
use clippy_utils::higher::If;
use rustc_ast::LitKind; use rustc_ast::LitKind;
use rustc_hir::{Block, ExprKind}; use rustc_hir::{Block, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg}; use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg};
use rustc_errors::Applicability; use rustc_errors::Applicability;
declare_clippy_lint! { declare_clippy_lint! {
@ -12,7 +13,7 @@ declare_clippy_lint! {
/// this lint suggests using a `from()` function or an `as` coercion. /// this lint suggests using a `from()` function or an `as` coercion.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// Coercion or `from()` is idiomatic way to convert bool to a number. /// Coercion or `from()` is another way to convert bool to a number.
/// Both methods are guaranteed to return 1 for true, and 0 for false. /// Both methods are guaranteed to return 1 for true, and 0 for false.
/// ///
/// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
@ -38,23 +39,23 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "1.65.0"] #[clippy::version = "1.65.0"]
pub BOOL_TO_INT_WITH_IF, pub BOOL_TO_INT_WITH_IF,
style, pedantic,
"using if to convert bool to int" "using if to convert bool to int"
} }
declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
if !expr.span.from_expansion() { if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
check_if_else(ctx, expr); check_if_else(cx, expr);
} }
} }
} }
fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
if let ExprKind::If(check, then, Some(else_)) = expr.kind if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr)
&& let Some(then_lit) = int_literal(then) && let Some(then_lit) = int_literal(then)
&& let Some(else_lit) = int_literal(else_) && let Some(else_lit) = int_literal(r#else)
{ {
let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) {
false false
@ -66,17 +67,17 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx
}; };
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let snippet = { let snippet = {
let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability); let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
if inverted { if inverted {
sugg = !sugg; sugg = !sugg;
} }
sugg sugg
}; };
let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type
let suggestion = { let suggestion = {
let wrap_in_curly = is_else_clause(ctx.tcx, expr); let wrap_in_curly = is_else_clause(cx.tcx, expr);
let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into()); let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into());
if wrap_in_curly { if wrap_in_curly {
s = s.blockify(); s = s.blockify();
@ -87,7 +88,7 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx
let into_snippet = snippet.clone().maybe_par(); let into_snippet = snippet.clone().maybe_par();
let as_snippet = snippet.as_ty(ty); let as_snippet = snippet.as_ty(ty);
span_lint_and_then(ctx, span_lint_and_then(cx,
BOOL_TO_INT_WITH_IF, BOOL_TO_INT_WITH_IF,
expr.span, expr.span,
"boolean to int conversion using if", "boolean to int conversion using if",

View file

@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
} }
} }
fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
cx.tcx cx.tcx
.get_diagnostic_item(sym::Ord) .get_diagnostic_item(sym::Ord)

View file

@ -593,7 +593,7 @@ declare_clippy_lint! {
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
/// ``` /// ```
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
#[clippy::version = "1.64.0"] #[clippy::version = "1.65.0"]
pub CAST_SLICE_FROM_RAW_PARTS, pub CAST_SLICE_FROM_RAW_PARTS,
suspicious, suspicious,
"casting a slice created from a pointer and length to a slice pointer" "casting a slice created from a pointer and length to a slice pointer"

View file

@ -4,11 +4,11 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_expr; use clippy_utils::visitors::for_each_expr;
use clippy_utils::LimitStack; use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_ast::ast::Attribute; use rustc_ast::ast::Attribute;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, ExprKind, FnDecl, HirId}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
@ -56,15 +56,13 @@ impl CognitiveComplexity {
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
kind: FnKind<'tcx>, kind: FnKind<'tcx>,
decl: &'tcx FnDecl<'_>, decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>, expr: &'tcx Expr<'_>,
body_span: Span, body_span: Span,
) { ) {
if body_span.from_expansion() { if body_span.from_expansion() {
return; return;
} }
let expr = body.value;
let mut cc = 1u64; let mut cc = 1u64;
let mut returns = 0u64; let mut returns = 0u64;
let _: Option<!> = for_each_expr(expr, |e| { let _: Option<!> = for_each_expr(expr, |e| {
@ -146,7 +144,18 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
) { ) {
let def_id = cx.tcx.hir().local_def_id(hir_id); let def_id = cx.tcx.hir().local_def_id(hir_id);
if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) {
self.check(cx, kind, decl, body, span); let expr = if is_async_fn(kind) {
match get_async_fn_body(cx.tcx, body) {
Some(b) => b,
None => {
return;
},
}
} else {
body.value
};
self.check(cx, kind, decl, expr, span);
} }
} }

View file

@ -0,0 +1,628 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
pub(crate) static LINTS: &[&crate::LintInfo] = &[
#[cfg(feature = "internal")]
crate::utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
crate::almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE_INFO,
crate::approx_const::APPROX_CONSTANT_INFO,
crate::as_conversions::AS_CONVERSIONS_INFO,
crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO,
crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO,
crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO,
crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO,
crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO,
crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO,
crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO,
crate::attrs::DEPRECATED_CFG_ATTR_INFO,
crate::attrs::DEPRECATED_SEMVER_INFO,
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
crate::attrs::INLINE_ALWAYS_INFO,
crate::attrs::MISMATCHED_TARGET_OS_INFO,
crate::attrs::USELESS_ATTRIBUTE_INFO,
crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO,
crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO,
crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO,
crate::blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS_INFO,
crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO,
crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO,
crate::booleans::NONMINIMAL_BOOL_INFO,
crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO,
crate::borrow_deref_ref::BORROW_DEREF_REF_INFO,
crate::box_default::BOX_DEFAULT_INFO,
crate::cargo::CARGO_COMMON_METADATA_INFO,
crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO,
crate::cargo::NEGATIVE_FEATURE_NAMES_INFO,
crate::cargo::REDUNDANT_FEATURE_NAMES_INFO,
crate::cargo::WILDCARD_DEPENDENCIES_INFO,
crate::casts::AS_PTR_CAST_MUT_INFO,
crate::casts::AS_UNDERSCORE_INFO,
crate::casts::BORROW_AS_PTR_INFO,
crate::casts::CAST_ABS_TO_UNSIGNED_INFO,
crate::casts::CAST_ENUM_CONSTRUCTOR_INFO,
crate::casts::CAST_ENUM_TRUNCATION_INFO,
crate::casts::CAST_LOSSLESS_INFO,
crate::casts::CAST_NAN_TO_INT_INFO,
crate::casts::CAST_POSSIBLE_TRUNCATION_INFO,
crate::casts::CAST_POSSIBLE_WRAP_INFO,
crate::casts::CAST_PRECISION_LOSS_INFO,
crate::casts::CAST_PTR_ALIGNMENT_INFO,
crate::casts::CAST_REF_TO_MUT_INFO,
crate::casts::CAST_SIGN_LOSS_INFO,
crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO,
crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO,
crate::casts::CHAR_LIT_AS_U8_INFO,
crate::casts::FN_TO_NUMERIC_CAST_INFO,
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
crate::casts::PTR_AS_PTR_INFO,
crate::casts::UNNECESSARY_CAST_INFO,
crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
crate::collapsible_if::COLLAPSIBLE_IF_INFO,
crate::comparison_chain::COMPARISON_CHAIN_INFO,
crate::copies::BRANCHES_SHARING_CODE_INFO,
crate::copies::IFS_SAME_COND_INFO,
crate::copies::IF_SAME_THEN_ELSE_INFO,
crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO,
crate::copy_iterator::COPY_ITERATOR_INFO,
crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO,
crate::create_dir::CREATE_DIR_INFO,
crate::dbg_macro::DBG_MACRO_INFO,
crate::default::DEFAULT_TRAIT_ACCESS_INFO,
crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO,
crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO,
crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO,
crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO,
crate::dereference::EXPLICIT_AUTO_DEREF_INFO,
crate::dereference::EXPLICIT_DEREF_METHODS_INFO,
crate::dereference::NEEDLESS_BORROW_INFO,
crate::dereference::REF_BINDING_TO_REFERENCE_INFO,
crate::derivable_impls::DERIVABLE_IMPLS_INFO,
crate::derive::DERIVE_HASH_XOR_EQ_INFO,
crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO,
crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO,
crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO,
crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO,
crate::disallowed_macros::DISALLOWED_MACROS_INFO,
crate::disallowed_methods::DISALLOWED_METHODS_INFO,
crate::disallowed_names::DISALLOWED_NAMES_INFO,
crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
crate::disallowed_types::DISALLOWED_TYPES_INFO,
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
crate::doc::DOC_MARKDOWN_INFO,
crate::doc::MISSING_ERRORS_DOC_INFO,
crate::doc::MISSING_PANICS_DOC_INFO,
crate::doc::MISSING_SAFETY_DOC_INFO,
crate::doc::NEEDLESS_DOCTEST_MAIN_INFO,
crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
crate::double_parens::DOUBLE_PARENS_INFO,
crate::drop_forget_ref::DROP_COPY_INFO,
crate::drop_forget_ref::DROP_NON_DROP_INFO,
crate::drop_forget_ref::DROP_REF_INFO,
crate::drop_forget_ref::FORGET_COPY_INFO,
crate::drop_forget_ref::FORGET_NON_DROP_INFO,
crate::drop_forget_ref::FORGET_REF_INFO,
crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO,
crate::duplicate_mod::DUPLICATE_MOD_INFO,
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
crate::empty_drop::EMPTY_DROP_INFO,
crate::empty_enum::EMPTY_ENUM_INFO,
crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
crate::entry::MAP_ENTRY_INFO,
crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO,
crate::enum_variants::ENUM_VARIANT_NAMES_INFO,
crate::enum_variants::MODULE_INCEPTION_INFO,
crate::enum_variants::MODULE_NAME_REPETITIONS_INFO,
crate::equatable_if_let::EQUATABLE_IF_LET_INFO,
crate::escape::BOXED_LOCAL_INFO,
crate::eta_reduction::REDUNDANT_CLOSURE_INFO,
crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO,
crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO,
crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO,
crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO,
crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
crate::exit::EXIT_INFO,
crate::explicit_write::EXPLICIT_WRITE_INFO,
crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO,
crate::float_literal::EXCESSIVE_PRECISION_INFO,
crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,
crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO,
crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
crate::format::USELESS_FORMAT_INFO,
crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
crate::format_args::UNINLINED_FORMAT_ARGS_INFO,
crate::format_args::UNUSED_FORMAT_SPECS_INFO,
crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO,
crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO,
crate::format_push_string::FORMAT_PUSH_STRING_INFO,
crate::formatting::POSSIBLE_MISSING_COMMA_INFO,
crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO,
crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO,
crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO,
crate::from_over_into::FROM_OVER_INTO_INFO,
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
crate::functions::DOUBLE_MUST_USE_INFO,
crate::functions::MUST_USE_CANDIDATE_INFO,
crate::functions::MUST_USE_UNIT_INFO,
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
crate::functions::RESULT_LARGE_ERR_INFO,
crate::functions::RESULT_UNIT_ERR_INFO,
crate::functions::TOO_MANY_ARGUMENTS_INFO,
crate::functions::TOO_MANY_LINES_INFO,
crate::future_not_send::FUTURE_NOT_SEND_INFO,
crate::if_let_mutex::IF_LET_MUTEX_INFO,
crate::if_not_else::IF_NOT_ELSE_INFO,
crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO,
crate::implicit_hasher::IMPLICIT_HASHER_INFO,
crate::implicit_return::IMPLICIT_RETURN_INFO,
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
crate::indexing_slicing::INDEXING_SLICING_INFO,
crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
crate::infinite_iter::INFINITE_ITER_INFO,
crate::infinite_iter::MAYBE_INFINITE_ITER_INFO,
crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO,
crate::inherent_to_string::INHERENT_TO_STRING_INFO,
crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO,
crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO,
crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO,
crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO,
crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO,
crate::int_plus_one::INT_PLUS_ONE_INFO,
crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO,
crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO,
crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO,
crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
crate::len_zero::COMPARISON_TO_EMPTY_INFO,
crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
crate::len_zero::LEN_ZERO_INFO,
crate::let_if_seq::USELESS_LET_IF_SEQ_INFO,
crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO,
crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO,
crate::literal_representation::LARGE_DIGIT_GROUPS_INFO,
crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO,
crate::literal_representation::UNREADABLE_LITERAL_INFO,
crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
crate::loops::EMPTY_LOOP_INFO,
crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,
crate::loops::EXPLICIT_ITER_LOOP_INFO,
crate::loops::FOR_KV_MAP_INFO,
crate::loops::ITER_NEXT_LOOP_INFO,
crate::loops::MANUAL_FIND_INFO,
crate::loops::MANUAL_FLATTEN_INFO,
crate::loops::MANUAL_MEMCPY_INFO,
crate::loops::MISSING_SPIN_LOOP_INFO,
crate::loops::MUT_RANGE_BOUND_INFO,
crate::loops::NEEDLESS_RANGE_LOOP_INFO,
crate::loops::NEVER_LOOP_INFO,
crate::loops::SAME_ITEM_PUSH_INFO,
crate::loops::SINGLE_ELEMENT_LOOP_INFO,
crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
crate::loops::WHILE_LET_LOOP_INFO,
crate::loops::WHILE_LET_ON_ITERATOR_INFO,
crate::macro_use::MACRO_USE_IMPORTS_INFO,
crate::main_recursion::MAIN_RECURSION_INFO,
crate::manual_assert::MANUAL_ASSERT_INFO,
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
crate::manual_bits::MANUAL_BITS_INFO,
crate::manual_clamp::MANUAL_CLAMP_INFO,
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
crate::manual_retain::MANUAL_RETAIN_INFO,
crate::manual_string_new::MANUAL_STRING_NEW_INFO,
crate::manual_strip::MANUAL_STRIP_INFO,
crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO,
crate::match_result_ok::MATCH_RESULT_OK_INFO,
crate::matches::COLLAPSIBLE_MATCH_INFO,
crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO,
crate::matches::MANUAL_FILTER_INFO,
crate::matches::MANUAL_MAP_INFO,
crate::matches::MANUAL_UNWRAP_OR_INFO,
crate::matches::MATCH_AS_REF_INFO,
crate::matches::MATCH_BOOL_INFO,
crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO,
crate::matches::MATCH_ON_VEC_ITEMS_INFO,
crate::matches::MATCH_OVERLAPPING_ARM_INFO,
crate::matches::MATCH_REF_PATS_INFO,
crate::matches::MATCH_SAME_ARMS_INFO,
crate::matches::MATCH_SINGLE_BINDING_INFO,
crate::matches::MATCH_STR_CASE_MISMATCH_INFO,
crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO,
crate::matches::MATCH_WILD_ERR_ARM_INFO,
crate::matches::NEEDLESS_MATCH_INFO,
crate::matches::REDUNDANT_PATTERN_MATCHING_INFO,
crate::matches::REST_PAT_IN_FULLY_BOUND_STRUCTS_INFO,
crate::matches::SIGNIFICANT_DROP_IN_SCRUTINEE_INFO,
crate::matches::SINGLE_MATCH_INFO,
crate::matches::SINGLE_MATCH_ELSE_INFO,
crate::matches::TRY_ERR_INFO,
crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO,
crate::matches::WILDCARD_IN_OR_PATTERNS_INFO,
crate::mem_forget::MEM_FORGET_INFO,
crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO,
crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO,
crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO,
crate::methods::BIND_INSTEAD_OF_MAP_INFO,
crate::methods::BYTES_COUNT_TO_LEN_INFO,
crate::methods::BYTES_NTH_INFO,
crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO,
crate::methods::CHARS_LAST_CMP_INFO,
crate::methods::CHARS_NEXT_CMP_INFO,
crate::methods::CLONED_INSTEAD_OF_COPIED_INFO,
crate::methods::CLONE_DOUBLE_REF_INFO,
crate::methods::CLONE_ON_COPY_INFO,
crate::methods::CLONE_ON_REF_PTR_INFO,
crate::methods::COLLAPSIBLE_STR_REPLACE_INFO,
crate::methods::ERR_EXPECT_INFO,
crate::methods::EXPECT_FUN_CALL_INFO,
crate::methods::EXPECT_USED_INFO,
crate::methods::EXTEND_WITH_DRAIN_INFO,
crate::methods::FILETYPE_IS_FILE_INFO,
crate::methods::FILTER_MAP_IDENTITY_INFO,
crate::methods::FILTER_MAP_NEXT_INFO,
crate::methods::FILTER_NEXT_INFO,
crate::methods::FLAT_MAP_IDENTITY_INFO,
crate::methods::FLAT_MAP_OPTION_INFO,
crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO,
crate::methods::GET_FIRST_INFO,
crate::methods::GET_LAST_WITH_LEN_INFO,
crate::methods::GET_UNWRAP_INFO,
crate::methods::IMPLICIT_CLONE_INFO,
crate::methods::INEFFICIENT_TO_STRING_INFO,
crate::methods::INSPECT_FOR_EACH_INFO,
crate::methods::INTO_ITER_ON_REF_INFO,
crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
crate::methods::ITER_CLONED_COLLECT_INFO,
crate::methods::ITER_COUNT_INFO,
crate::methods::ITER_KV_MAP_INFO,
crate::methods::ITER_NEXT_SLICE_INFO,
crate::methods::ITER_NTH_INFO,
crate::methods::ITER_NTH_ZERO_INFO,
crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO,
crate::methods::ITER_ON_SINGLE_ITEMS_INFO,
crate::methods::ITER_OVEREAGER_CLONED_INFO,
crate::methods::ITER_SKIP_NEXT_INFO,
crate::methods::ITER_WITH_DRAIN_INFO,
crate::methods::MANUAL_FILTER_MAP_INFO,
crate::methods::MANUAL_FIND_MAP_INFO,
crate::methods::MANUAL_OK_OR_INFO,
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
crate::methods::MANUAL_SPLIT_ONCE_INFO,
crate::methods::MANUAL_STR_REPEAT_INFO,
crate::methods::MAP_CLONE_INFO,
crate::methods::MAP_COLLECT_RESULT_UNIT_INFO,
crate::methods::MAP_ERR_IGNORE_INFO,
crate::methods::MAP_FLATTEN_INFO,
crate::methods::MAP_IDENTITY_INFO,
crate::methods::MAP_UNWRAP_OR_INFO,
crate::methods::MUT_MUTEX_LOCK_INFO,
crate::methods::NAIVE_BYTECOUNT_INFO,
crate::methods::NEEDLESS_COLLECT_INFO,
crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
crate::methods::NEEDLESS_OPTION_TAKE_INFO,
crate::methods::NEEDLESS_SPLITN_INFO,
crate::methods::NEW_RET_NO_SELF_INFO,
crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO,
crate::methods::NO_EFFECT_REPLACE_INFO,
crate::methods::OBFUSCATED_IF_ELSE_INFO,
crate::methods::OK_EXPECT_INFO,
crate::methods::OPTION_AS_REF_DEREF_INFO,
crate::methods::OPTION_FILTER_MAP_INFO,
crate::methods::OPTION_MAP_OR_NONE_INFO,
crate::methods::OR_FUN_CALL_INFO,
crate::methods::OR_THEN_UNWRAP_INFO,
crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
crate::methods::REPEAT_ONCE_INFO,
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
crate::methods::SEARCH_IS_SOME_INFO,
crate::methods::SEEK_FROM_CURRENT_INFO,
crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
crate::methods::SINGLE_CHAR_PATTERN_INFO,
crate::methods::SKIP_WHILE_NEXT_INFO,
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
crate::methods::STRING_EXTEND_CHARS_INFO,
crate::methods::SUSPICIOUS_MAP_INFO,
crate::methods::SUSPICIOUS_SPLITN_INFO,
crate::methods::SUSPICIOUS_TO_OWNED_INFO,
crate::methods::UNINIT_ASSUMED_INIT_INFO,
crate::methods::UNIT_HASH_INFO,
crate::methods::UNNECESSARY_FILTER_MAP_INFO,
crate::methods::UNNECESSARY_FIND_MAP_INFO,
crate::methods::UNNECESSARY_FOLD_INFO,
crate::methods::UNNECESSARY_JOIN_INFO,
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
crate::methods::UNNECESSARY_SORT_BY_INFO,
crate::methods::UNNECESSARY_TO_OWNED_INFO,
crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO,
crate::methods::UNWRAP_USED_INFO,
crate::methods::USELESS_ASREF_INFO,
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
crate::methods::VERBOSE_FILE_READS_INFO,
crate::methods::WRONG_SELF_CONVENTION_INFO,
crate::methods::ZST_OFFSET_INFO,
crate::minmax::MIN_MAX_INFO,
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
crate::misc::TOPLEVEL_REF_ARG_INFO,
crate::misc::USED_UNDERSCORE_BINDING_INFO,
crate::misc::ZERO_PTR_INFO,
crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
crate::misc_early::DOUBLE_NEG_INFO,
crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO,
crate::misc_early::REDUNDANT_PATTERN_INFO,
crate::misc_early::SEPARATED_LITERAL_SUFFIX_INFO,
crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO,
crate::misc_early::UNNEEDED_WILDCARD_PATTERN_INFO,
crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO,
crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO,
crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO,
crate::mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION_INFO,
crate::module_style::MOD_MODULE_FILES_INFO,
crate::module_style::SELF_NAMED_MODULE_FILES_INFO,
crate::multi_assignments::MULTI_ASSIGNMENTS_INFO,
crate::mut_key::MUTABLE_KEY_TYPE_INFO,
crate::mut_mut::MUT_MUT_INFO,
crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO,
crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO,
crate::mutex_atomic::MUTEX_ATOMIC_INFO,
crate::mutex_atomic::MUTEX_INTEGER_INFO,
crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
crate::needless_bool::BOOL_COMPARISON_INFO,
crate::needless_bool::NEEDLESS_BOOL_INFO,
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO,
crate::needless_update::NEEDLESS_UPDATE_INFO,
crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO,
crate::neg_multiply::NEG_MULTIPLY_INFO,
crate::new_without_default::NEW_WITHOUT_DEFAULT_INFO,
crate::no_effect::NO_EFFECT_INFO,
crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO,
crate::no_effect::UNNECESSARY_OPERATION_INFO,
crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO,
crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO,
crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO,
crate::non_expressive_names::MANY_SINGLE_CHAR_NAMES_INFO,
crate::non_expressive_names::SIMILAR_NAMES_INFO,
crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO,
crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO,
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
crate::octal_escapes::OCTAL_ESCAPES_INFO,
crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO,
crate::operators::ABSURD_EXTREME_COMPARISONS_INFO,
crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO,
crate::operators::ASSIGN_OP_PATTERN_INFO,
crate::operators::BAD_BIT_MASK_INFO,
crate::operators::CMP_NAN_INFO,
crate::operators::CMP_OWNED_INFO,
crate::operators::DOUBLE_COMPARISONS_INFO,
crate::operators::DURATION_SUBSEC_INFO,
crate::operators::EQ_OP_INFO,
crate::operators::ERASING_OP_INFO,
crate::operators::FLOAT_ARITHMETIC_INFO,
crate::operators::FLOAT_CMP_INFO,
crate::operators::FLOAT_CMP_CONST_INFO,
crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO,
crate::operators::IDENTITY_OP_INFO,
crate::operators::INEFFECTIVE_BIT_MASK_INFO,
crate::operators::INTEGER_ARITHMETIC_INFO,
crate::operators::INTEGER_DIVISION_INFO,
crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
crate::operators::MODULO_ARITHMETIC_INFO,
crate::operators::MODULO_ONE_INFO,
crate::operators::NEEDLESS_BITWISE_BOOL_INFO,
crate::operators::OP_REF_INFO,
crate::operators::PTR_EQ_INFO,
crate::operators::SELF_ASSIGNMENT_INFO,
crate::operators::VERBOSE_BIT_MASK_INFO,
crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO,
crate::option_if_let_else::OPTION_IF_LET_ELSE_INFO,
crate::overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL_INFO,
crate::panic_in_result_fn::PANIC_IN_RESULT_FN_INFO,
crate::panic_unimplemented::PANIC_INFO,
crate::panic_unimplemented::TODO_INFO,
crate::panic_unimplemented::UNIMPLEMENTED_INFO,
crate::panic_unimplemented::UNREACHABLE_INFO,
crate::partial_pub_fields::PARTIAL_PUB_FIELDS_INFO,
crate::partialeq_ne_impl::PARTIALEQ_NE_IMPL_INFO,
crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO,
crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO,
crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO,
crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
crate::precedence::PRECEDENCE_INFO,
crate::ptr::CMP_NULL_INFO,
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
crate::ptr::MUT_FROM_REF_INFO,
crate::ptr::PTR_ARG_INFO,
crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
crate::pub_use::PUB_USE_INFO,
crate::question_mark::QUESTION_MARK_INFO,
crate::ranges::MANUAL_RANGE_CONTAINS_INFO,
crate::ranges::RANGE_MINUS_ONE_INFO,
crate::ranges::RANGE_PLUS_ONE_INFO,
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
crate::redundant_clone::REDUNDANT_CLONE_INFO,
crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
crate::redundant_else::REDUNDANT_ELSE_INFO,
crate::redundant_field_names::REDUNDANT_FIELD_NAMES_INFO,
crate::redundant_pub_crate::REDUNDANT_PUB_CRATE_INFO,
crate::redundant_slicing::DEREF_BY_SLICING_INFO,
crate::redundant_slicing::REDUNDANT_SLICING_INFO,
crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO,
crate::ref_option_ref::REF_OPTION_REF_INFO,
crate::reference::DEREF_ADDROF_INFO,
crate::regex::INVALID_REGEX_INFO,
crate::regex::TRIVIAL_REGEX_INFO,
crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
crate::returns::LET_AND_RETURN_INFO,
crate::returns::NEEDLESS_RETURN_INFO,
crate::same_name_method::SAME_NAME_METHOD_INFO,
crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO,
crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO,
crate::serde_api::SERDE_API_MISUSE_INFO,
crate::shadow::SHADOW_REUSE_INFO,
crate::shadow::SHADOW_SAME_INFO,
crate::shadow::SHADOW_UNRELATED_INFO,
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO,
crate::strings::STRING_ADD_INFO,
crate::strings::STRING_ADD_ASSIGN_INFO,
crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO,
crate::strings::STRING_LIT_AS_BYTES_INFO,
crate::strings::STRING_SLICE_INFO,
crate::strings::STRING_TO_STRING_INFO,
crate::strings::STR_TO_STRING_INFO,
crate::strings::TRIM_SPLIT_WHITESPACE_INFO,
crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO,
crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,
crate::suspicious_xor_used_as_pow::SUSPICIOUS_XOR_USED_AS_POW_INFO,
crate::swap::ALMOST_SWAPPED_INFO,
crate::swap::MANUAL_SWAP_INFO,
crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO,
crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO,
crate::transmute::CROSSPOINTER_TRANSMUTE_INFO,
crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO,
crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO,
crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO,
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,
crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO,
crate::transmute::TRANSMUTING_NULL_INFO,
crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO,
crate::transmute::USELESS_TRANSMUTE_INFO,
crate::transmute::WRONG_TRANSMUTE_INFO,
crate::types::BORROWED_BOX_INFO,
crate::types::BOX_COLLECTION_INFO,
crate::types::LINKEDLIST_INFO,
crate::types::OPTION_OPTION_INFO,
crate::types::RC_BUFFER_INFO,
crate::types::RC_MUTEX_INFO,
crate::types::REDUNDANT_ALLOCATION_INFO,
crate::types::TYPE_COMPLEXITY_INFO,
crate::types::VEC_BOX_INFO,
crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO,
crate::unicode::INVISIBLE_CHARACTERS_INFO,
crate::unicode::NON_ASCII_LITERAL_INFO,
crate::unicode::UNICODE_NOT_NFC_INFO,
crate::uninit_vec::UNINIT_VEC_INFO,
crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO,
crate::unit_types::LET_UNIT_VALUE_INFO,
crate::unit_types::UNIT_ARG_INFO,
crate::unit_types::UNIT_CMP_INFO,
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
crate::unused_async::UNUSED_ASYNC_INFO,
crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO,
crate::unused_peekable::UNUSED_PEEKABLE_INFO,
crate::unused_rounding::UNUSED_ROUNDING_INFO,
crate::unused_self::UNUSED_SELF_INFO,
crate::unused_unit::UNUSED_UNIT_INFO,
crate::unwrap::PANICKING_UNWRAP_INFO,
crate::unwrap::UNNECESSARY_UNWRAP_INFO,
crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO,
crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO,
crate::use_self::USE_SELF_INFO,
crate::useless_conversion::USELESS_CONVERSION_INFO,
crate::vec::USELESS_VEC_INFO,
crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO,
crate::wildcard_imports::ENUM_GLOB_USE_INFO,
crate::wildcard_imports::WILDCARD_IMPORTS_INFO,
crate::write::PRINTLN_EMPTY_STRING_INFO,
crate::write::PRINT_LITERAL_INFO,
crate::write::PRINT_STDERR_INFO,
crate::write::PRINT_STDOUT_INFO,
crate::write::PRINT_WITH_NEWLINE_INFO,
crate::write::USE_DEBUG_INFO,
crate::write::WRITELN_EMPTY_STRING_INFO,
crate::write::WRITE_LITERAL_INFO,
crate::write::WRITE_WITH_NEWLINE_INFO,
crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO,
crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO,
];

View file

@ -9,6 +9,7 @@ use clippy_utils::{
}; };
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{ use rustc_hir::{
@ -274,9 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
} }
let typeck = cx.typeck_results(); let typeck = cx.typeck_results();
let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else {
x
} else {
// The whole chain of reference operations has been seen // The whole chain of reference operations has been seen
if let Some((state, data)) = self.state.take() { if let Some((state, data)) = self.state.take() {
report(cx, expr, state, data); report(cx, expr, state, data);
@ -806,30 +805,39 @@ fn walk_parents<'tcx>(
.position(|arg| arg.hir_id == child_id) .position(|arg| arg.hir_id == child_id)
.zip(expr_sig(cx, func)) .zip(expr_sig(cx, func))
.and_then(|(i, sig)| { .and_then(|(i, sig)| {
sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty { sig.input_with_hir(i).map(|(hir_ty, ty)| {
// Type inference for closures can depend on how they're called. Only go by the explicit match hir_ty {
// types here. // Type inference for closures can depend on how they're called. Only go by the explicit
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), // types here.
None => { Some(hir_ty) => {
if let ty::Param(param_ty) = ty.skip_binder().kind() { binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars())
needless_borrow_impl_arg_position( },
cx, None => {
possible_borrowers, // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
parent, // `!call_is_qualified(func)` for https://github.com/rust-lang/rust-clippy/issues/9782
i, if e.hir_id == child_id
*param_ty, && !call_is_qualified(func)
e, && let ty::Param(param_ty) = ty.skip_binder().kind()
precedence, {
msrv, needless_borrow_impl_arg_position(
) cx,
} else { possible_borrowers,
ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) parent,
.position_for_arg() i,
} *param_ty,
}, e,
precedence,
msrv,
)
} else {
ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
.position_for_arg()
}
},
}
}) })
}), }),
ExprKind::MethodCall(_, receiver, args, _) => { ExprKind::MethodCall(method, receiver, args, _) => {
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
if receiver.hir_id == child_id { if receiver.hir_id == child_id {
// Check for calls to trait methods where the trait is implemented on a reference. // Check for calls to trait methods where the trait is implemented on a reference.
@ -867,7 +875,9 @@ fn walk_parents<'tcx>(
} }
args.iter().position(|arg| arg.hir_id == child_id).map(|i| { args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
if let ty::Param(param_ty) = ty.kind() { // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
// `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782
if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() {
needless_borrow_impl_arg_position( needless_borrow_impl_arg_position(
cx, cx,
possible_borrowers, possible_borrowers,
@ -1045,13 +1055,25 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
v.0 v.0
} }
fn call_is_qualified(expr: &Expr<'_>) -> bool {
if let ExprKind::Path(path) = &expr.kind {
match path {
QPath::Resolved(_, path) => path.segments.last().map_or(false, |segment| segment.args.is_some()),
QPath::TypeRelative(_, segment) => segment.args.is_some(),
QPath::LangItem(..) => false,
}
} else {
false
}
}
// Checks whether: // Checks whether:
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait` // * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
// * `e`'s type implements `Trait` and is copyable // * `e`'s type implements `Trait` and is copyable
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
// be moved, but it cannot be. // be moved, but it cannot be.
#[expect(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments, clippy::too_many_lines)]
fn needless_borrow_impl_arg_position<'tcx>( fn needless_borrow_impl_arg_position<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
@ -1113,6 +1135,16 @@ fn needless_borrow_impl_arg_position<'tcx>(
return Position::Other(precedence); return Position::Other(precedence);
} }
// See:
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
if projection_predicates
.iter()
.any(|projection_predicate| is_mixed_projection_predicate(cx, callee_def_id, projection_predicate))
{
return Position::Other(precedence);
}
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
// elements are modified each time `check_referent` is called. // elements are modified each time `check_referent` is called.
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
@ -1192,6 +1224,37 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
}) })
} }
fn is_mixed_projection_predicate<'tcx>(
cx: &LateContext<'tcx>,
callee_def_id: DefId,
projection_predicate: &ProjectionPredicate<'tcx>,
) -> bool {
let generics = cx.tcx.generics_of(callee_def_id);
// The predicate requires the projected type to equal a type parameter from the parent context.
if let Some(term_ty) = projection_predicate.term.ty()
&& let ty::Param(term_param_ty) = term_ty.kind()
&& (term_param_ty.index as usize) < generics.parent_count
{
// The inner-most self type is a type parameter from the current function.
let mut projection_ty = projection_predicate.projection_ty;
loop {
match projection_ty.self_ty().kind() {
ty::Projection(inner_projection_ty) => {
projection_ty = *inner_projection_ty;
}
ty::Param(param_ty) => {
return (param_ty.index as usize) >= generics.parent_count;
}
_ => {
return false;
}
}
}
} else {
false
}
}
fn referent_used_exactly_once<'tcx>( fn referent_used_exactly_once<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
@ -1203,6 +1266,8 @@ fn referent_used_exactly_once<'tcx>(
&& let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
&& !place.has_deref() && !place.has_deref()
// Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710)
&& TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none()
{ {
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
if possible_borrowers if possible_borrowers
@ -1320,6 +1385,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc
continue; continue;
}, },
ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
ty::Projection(_) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty),
ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => { ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
Position::ReborrowStable(precedence).into() Position::ReborrowStable(precedence).into()
}, },
@ -1346,11 +1412,9 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc
| ty::Closure(..) | ty::Closure(..)
| ty::Never | ty::Never
| ty::Tuple(_) | ty::Tuple(_)
| ty::Projection(_) => Position::DerefStable( | ty::Projection(_) => {
precedence, Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into()
ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds()), },
)
.into(),
}; };
} }
} }

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::macro_backtrace; use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -89,7 +88,7 @@ impl DisallowedMacros {
&format!("use of a disallowed macro `{}`", conf.path()), &format!("use of a disallowed macro `{}`", conf.path()),
|diag| { |diag| {
if let Some(reason) = conf.reason() { if let Some(reason) = conf.reason() {
diag.note(&format!("{reason} (from clippy.toml)")); diag.note(reason);
} }
}, },
); );
@ -104,7 +103,7 @@ impl LateLintPass<'_> for DisallowedMacros {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() { for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect(); let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.disallowed.insert(id, index); self.disallowed.insert(id, index);
} }
} }

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -79,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() { for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect(); let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.disallowed.insert(id, index); self.disallowed.insert(id, index);
} }
} }
@ -104,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
let msg = format!("use of a disallowed method `{}`", conf.path()); let msg = format!("use of a disallowed method `{}`", conf.path());
span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
if let Some(reason) = conf.reason() { if let Some(reason) = conf.reason() {
diag.note(&format!("{reason} (from clippy.toml)")); diag.note(reason);
} }
}); });
} }

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{Namespace, Res}; use rustc_hir::def::Res;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -53,8 +53,8 @@ declare_clippy_lint! {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DisallowedTypes { pub struct DisallowedTypes {
conf_disallowed: Vec<conf::DisallowedPath>, conf_disallowed: Vec<conf::DisallowedPath>,
def_ids: FxHashMap<DefId, Option<String>>, def_ids: FxHashMap<DefId, usize>,
prim_tys: FxHashMap<PrimTy, Option<String>>, prim_tys: FxHashMap<PrimTy, usize>,
} }
impl DisallowedTypes { impl DisallowedTypes {
@ -69,13 +69,13 @@ impl DisallowedTypes {
fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
match res { match res {
Res::Def(_, did) => { Res::Def(_, did) => {
if let Some(reason) = self.def_ids.get(did) { if let Some(&index) = self.def_ids.get(did) {
emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref()); emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]);
} }
}, },
Res::PrimTy(prim) => { Res::PrimTy(prim) => {
if let Some(reason) = self.prim_tys.get(prim) { if let Some(&index) = self.prim_tys.get(prim) {
emit(cx, prim.name_str(), span, reason.as_deref()); emit(cx, prim.name_str(), span, &self.conf_disallowed[index]);
} }
}, },
_ => {}, _ => {},
@ -87,17 +87,19 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for conf in &self.conf_disallowed { for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect(); let segs: Vec<_> = conf.path().split("::").collect();
let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)"));
match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { for res in clippy_utils::def_path_res(cx, &segs) {
Res::Def(_, id) => { match res {
self.def_ids.insert(id, reason); Res::Def(_, id) => {
}, self.def_ids.insert(id, index);
Res::PrimTy(ty) => { },
self.prim_tys.insert(ty, reason); Res::PrimTy(ty) => {
}, self.prim_tys.insert(ty, index);
_ => {}, },
_ => {},
}
} }
} }
} }
@ -119,14 +121,14 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
} }
} }
fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) {
span_lint_and_then( span_lint_and_then(
cx, cx,
DISALLOWED_TYPES, DISALLOWED_TYPES,
span, span,
&format!("`{name}` is not allowed according to config"), &format!("`{name}` is not allowed according to config"),
|diag| { |diag| {
if let Some(reason) = reason { if let Some(reason) = conf.reason() {
diag.note(reason); diag.note(reason);
} }
}, },

View file

@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::EmitterWriter; use rustc_errors::emitter::EmitterWriter;
use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle}; use rustc_errors::{Applicability, Handler, SuggestionStyle};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AnonConst, Expr}; use rustc_hir::{AnonConst, Expr};
@ -221,6 +221,42 @@ declare_clippy_lint! {
"possible typo for an intra-doc link" "possible typo for an intra-doc link"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for the doc comments of publicly visible
/// safe functions and traits and warns if there is a `# Safety` section.
///
/// ### Why is this bad?
/// Safe functions and traits are safe to implement and therefore do not
/// need to describe safety preconditions that users are required to uphold.
///
/// ### Examples
/// ```rust
///# type Universe = ();
/// /// # Safety
/// ///
/// /// This function should not be called before the horsemen are ready.
/// pub fn start_apocalypse_but_safely(u: &mut Universe) {
/// unimplemented!();
/// }
/// ```
///
/// The function is safe, so there shouldn't be any preconditions
/// that have to be explained for safety reasons.
///
/// ```rust
///# type Universe = ();
/// /// This function should really be documented
/// pub fn start_apocalypse(u: &mut Universe) {
/// unimplemented!();
/// }
/// ```
#[clippy::version = "1.66.0"]
pub UNNECESSARY_SAFETY_DOC,
style,
"`pub fn` or `pub trait` with `# Safety` docs"
}
#[expect(clippy::module_name_repetitions)] #[expect(clippy::module_name_repetitions)]
#[derive(Clone)] #[derive(Clone)]
pub struct DocMarkdown { pub struct DocMarkdown {
@ -243,7 +279,8 @@ impl_lint_pass!(DocMarkdown => [
MISSING_SAFETY_DOC, MISSING_SAFETY_DOC,
MISSING_ERRORS_DOC, MISSING_ERRORS_DOC,
MISSING_PANICS_DOC, MISSING_PANICS_DOC,
NEEDLESS_DOCTEST_MAIN NEEDLESS_DOCTEST_MAIN,
UNNECESSARY_SAFETY_DOC,
]); ]);
impl<'tcx> LateLintPass<'tcx> for DocMarkdown { impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
@ -254,7 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id()); let attrs = cx.tcx.hir().attrs(item.hir_id());
let headers = check_attrs(cx, &self.valid_idents, attrs); let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
match item.kind { match item.kind {
hir::ItemKind::Fn(ref sig, _, body_id) => { hir::ItemKind::Fn(ref sig, _, body_id) => {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
@ -265,29 +302,26 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None, panic_span: None,
}; };
fpu.visit_expr(body.value); fpu.visit_expr(body.value);
lint_for_missing_headers( lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
cx,
item.owner_id.def_id,
item.span,
sig,
headers,
Some(body_id),
fpu.panic_span,
);
} }
}, },
hir::ItemKind::Impl(impl_) => { hir::ItemKind::Impl(impl_) => {
self.in_trait_impl = impl_.of_trait.is_some(); self.in_trait_impl = impl_.of_trait.is_some();
}, },
hir::ItemKind::Trait(_, unsafety, ..) => { hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
if !headers.safety && unsafety == hir::Unsafety::Unsafe { (false, hir::Unsafety::Unsafe) => span_lint(
span_lint( cx,
cx, MISSING_SAFETY_DOC,
MISSING_SAFETY_DOC, cx.tcx.def_span(item.owner_id),
item.span, "docs for unsafe trait missing `# Safety` section",
"docs for unsafe trait missing `# Safety` section", ),
); (true, hir::Unsafety::Normal) => span_lint(
} cx,
UNNECESSARY_SAFETY_DOC,
cx.tcx.def_span(item.owner_id),
"docs for safe trait have unnecessary `# Safety` section",
),
_ => (),
}, },
_ => (), _ => (),
} }
@ -301,17 +335,17 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id()); let attrs = cx.tcx.hir().attrs(item.hir_id());
let headers = check_attrs(cx, &self.valid_idents, attrs); let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
if !in_external_macro(cx.tcx.sess, item.span) { if !in_external_macro(cx.tcx.sess, item.span) {
lint_for_missing_headers(cx, item.owner_id.def_id, item.span, sig, headers, None, None); lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, None, None);
} }
} }
} }
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id()); let attrs = cx.tcx.hir().attrs(item.hir_id());
let headers = check_attrs(cx, &self.valid_idents, attrs); let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
return; return;
} }
@ -323,23 +357,14 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None, panic_span: None,
}; };
fpu.visit_expr(body.value); fpu.visit_expr(body.value);
lint_for_missing_headers( lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
cx,
item.owner_id.def_id,
item.span,
sig,
headers,
Some(body_id),
fpu.panic_span,
);
} }
} }
} }
fn lint_for_missing_headers<'tcx>( fn lint_for_missing_headers(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
def_id: LocalDefId, def_id: LocalDefId,
span: impl Into<MultiSpan> + Copy,
sig: &hir::FnSig<'_>, sig: &hir::FnSig<'_>,
headers: DocHeaders, headers: DocHeaders,
body_id: Option<hir::BodyId>, body_id: Option<hir::BodyId>,
@ -359,13 +384,21 @@ fn lint_for_missing_headers<'tcx>(
return; return;
} }
if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { let span = cx.tcx.def_span(def_id);
span_lint( match (headers.safety, sig.header.unsafety) {
(false, hir::Unsafety::Unsafe) => span_lint(
cx, cx,
MISSING_SAFETY_DOC, MISSING_SAFETY_DOC,
span, span,
"unsafe function's docs miss `# Safety` section", "unsafe function's docs miss `# Safety` section",
); ),
(true, hir::Unsafety::Normal) => span_lint(
cx,
UNNECESSARY_SAFETY_DOC,
span,
"safe function's docs have unnecessary `# Safety` section",
),
_ => (),
} }
if !headers.panics && panic_span.is_some() { if !headers.panics && panic_span.is_some() {
span_lint_and_note( span_lint_and_note(
@ -467,7 +500,7 @@ struct DocHeaders {
panics: bool, panics: bool,
} }
fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders { fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
use pulldown_cmark::{BrokenLink, CowStr, Options}; use pulldown_cmark::{BrokenLink, CowStr, Options};
/// We don't want the parser to choke on intra doc links. Since we don't /// We don't want the parser to choke on intra doc links. Since we don't
/// actually care about rendering them, just pretend that all broken links are /// actually care about rendering them, just pretend that all broken links are
@ -488,11 +521,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
} else if attr.has_name(sym::doc) { } else if attr.has_name(sym::doc) {
// ignore mix of sugared and non-sugared doc // ignore mix of sugared and non-sugared doc
// don't trigger the safety or errors check // don't trigger the safety or errors check
return DocHeaders { return None;
safety: true,
errors: true,
panics: true,
};
} }
} }
@ -504,7 +533,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
} }
if doc.is_empty() { if doc.is_empty() {
return DocHeaders::default(); return Some(DocHeaders::default());
} }
let mut cb = fake_broken_link_callback; let mut cb = fake_broken_link_callback;
@ -527,7 +556,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
(previous, current) => Err(((previous, previous_range), (current, current_range))), (previous, current) => Err(((previous, previous_range), (current, current_range))),
} }
}); });
check_doc(cx, valid_idents, events, &spans) Some(check_doc(cx, valid_idents, events, &spans))
} }
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];

View file

@ -250,7 +250,7 @@ impl LateLintPass<'_> for EnumVariantNames {
let item_name = item.ident.name.as_str(); let item_name = item.ident.name.as_str();
let item_camel = to_camel_case(item_name); let item_camel = to_camel_case(item_name);
if !item.span.from_expansion() && is_present_in_source(cx, item.span) { if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { if let Some((mod_name, mod_camel)) = self.modules.last() {
// constants don't have surrounding modules // constants don't have surrounding modules
if !mod_camel.is_empty() { if !mod_camel.is_empty() {
if mod_name == &item.ident.name { if mod_name == &item.ident.name {

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_hir::{Expr, ExprKind, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -67,16 +66,14 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T
impl<'tcx> LateLintPass<'tcx> for PatternEquality { impl<'tcx> LateLintPass<'tcx> for PatternEquality {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! { if !in_external_macro(cx.sess(), expr.span)
if !in_external_macro(cx.sess(), expr.span); && let ExprKind::Let(let_expr) = expr.kind
if let ExprKind::Let(let_expr) = expr.kind; && unary_pattern(let_expr.pat) {
if unary_pattern(let_expr.pat);
let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat);
if is_structural_partial_eq(cx, exp_ty, pat_ty); let mut applicability = Applicability::MachineApplicable;
then {
let mut applicability = Applicability::MachineApplicable; if is_structural_partial_eq(cx, exp_ty, pat_ty) {
let pat_str = match let_expr.pat.kind { let pat_str = match let_expr.pat.kind {
PatKind::Struct(..) => format!( PatKind::Struct(..) => format!(
"({})", "({})",
@ -96,6 +93,20 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
), ),
applicability, applicability,
); );
} else {
span_lint_and_sugg(
cx,
EQUATABLE_IF_LET,
expr.span,
"this pattern matching can be expressed using `matches!`",
"try",
format!(
"matches!({}, {})",
snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
),
applicability,
);
} }
} }
} }

View file

@ -176,13 +176,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
} }
} }
fn fake_read( fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
&mut self,
_: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
_: FakeReadCause,
_: HirId,
) {
}
} }
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {

View file

@ -1,8 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind}; use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool};
use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, Span}; use rustc_span::Span;
use rustc_target::spec::abi::Abi;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -83,6 +86,12 @@ pub struct ExcessiveBools {
max_fn_params_bools: u64, max_fn_params_bools: u64,
} }
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
enum Kind {
Struct,
Fn,
}
impl ExcessiveBools { impl ExcessiveBools {
#[must_use] #[must_use]
pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
@ -92,21 +101,20 @@ impl ExcessiveBools {
} }
} }
fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) { fn too_many_bools<'tcx>(&self, tys: impl Iterator<Item = &'tcx Ty<'tcx>>, kind: Kind) -> bool {
match fn_sig.header.ext { if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() {
Extern::Implicit(_) | Extern::Explicit(_, _) => return, (if Kind::Fn == kind {
Extern::None => (), self.max_fn_params_bools
} else {
self.max_struct_bools
}) < bools
} else {
false
} }
}
let fn_sig_bools = fn_sig fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) {
.decl if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) {
.inputs
.iter()
.filter(|param| is_bool_ty(&param.ty))
.count()
.try_into()
.unwrap();
if self.max_fn_params_bools < fn_sig_bools {
span_lint_and_help( span_lint_and_help(
cx, cx,
FN_PARAMS_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS,
@ -121,56 +129,55 @@ impl ExcessiveBools {
impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
fn is_bool_ty(ty: &Ty) -> bool { impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
if let TyKind::Path(None, path) = &ty.kind { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let [name] = path.segments.as_slice() {
return name.ident.name == sym::bool;
}
}
false
}
impl EarlyLintPass for ExcessiveBools {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if item.span.from_expansion() { if item.span.from_expansion() {
return; return;
} }
match &item.kind { if let ItemKind::Struct(variant_data, _) = &item.kind {
ItemKind::Struct(variant_data, _) => { if has_repr_attr(cx, item.hir_id()) {
if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { return;
return; }
}
let struct_bools = variant_data if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) {
.fields() span_lint_and_help(
.iter() cx,
.filter(|field| is_bool_ty(&field.ty)) STRUCT_EXCESSIVE_BOOLS,
.count() item.span,
.try_into() &format!("more than {} bools in a struct", self.max_struct_bools),
.unwrap(); None,
if self.max_struct_bools < struct_bools { "consider using a state machine or refactoring bools into two-variant enums",
span_lint_and_help( );
cx, }
STRUCT_EXCESSIVE_BOOLS, }
item.span, }
&format!("more than {} bools in a struct", self.max_struct_bools),
None, fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) {
"consider using a state machine or refactoring bools into two-variant enums", // functions with a body are already checked by `check_fn`
); if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind
} && fn_sig.header.abi == Abi::Rust
}, {
ItemKind::Impl(box Impl { self.check_fn_sig(cx, fn_sig.decl, fn_sig.span);
of_trait: None, items, .. }
}) }
| ItemKind::Trait(box Trait { items, .. }) => {
for item in items { fn check_fn(
if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { &mut self,
self.check_fn_sig(cx, sig, item.span); cx: &LateContext<'tcx>,
} fn_kind: FnKind<'tcx>,
} fn_decl: &'tcx FnDecl<'tcx>,
}, _: &'tcx Body<'tcx>,
ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span), span: Span,
_ => (), hir_id: HirId,
) {
if let Some(fn_header) = fn_kind.header()
&& fn_header.abi == Abi::Rust
&& get_parent_as_impl(cx.tcx, hir_id)
.map_or(true,
|impl_item| impl_item.of_trait.is_none()
)
{
self.check_fn_sig(cx, fn_decl, span);
} }
} }
} }

View file

@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
} }
} }
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Expr, ImplItemKind}; use rustc_hir::{Expr, ImplItemKind};

View file

@ -0,0 +1,77 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_c_void;
use clippy_utils::{match_def_path, path_def_id, paths};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::RawPtr;
use rustc_middle::ty::TypeAndMut;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)`
///
/// ### Why is this bad?
/// When dealing with `c_void` raw pointers in FFI, it is easy to run into the pitfall of calling `from_raw` with the `c_void` pointer.
/// The type signature of `Box::from_raw` is `fn from_raw(raw: *mut T) -> Box<T>`, so if you pass a `*mut c_void` you will get a `Box<c_void>` (and similarly for `Rc`, `Arc` and `Weak`).
/// For this to be safe, `c_void` would need to have the same memory layout as the original type, which is often not the case.
///
/// ### Example
/// ```rust
/// # use std::ffi::c_void;
/// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
/// let _ = unsafe { Box::from_raw(ptr) };
/// ```
/// Use instead:
/// ```rust
/// # use std::ffi::c_void;
/// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
/// let _ = unsafe { Box::from_raw(ptr as *mut usize) };
/// ```
///
#[clippy::version = "1.66.0"]
pub FROM_RAW_WITH_VOID_PTR,
suspicious,
"creating a `Box` from a void raw pointer"
}
declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]);
impl LateLintPass<'_> for FromRawWithVoidPtr {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Call(box_from_raw, [arg]) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
&& seg.ident.name == sym!(from_raw)
&& let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
&& let arg_kind = cx.typeck_results().expr_ty(arg).kind()
&& let RawPtr(TypeAndMut { ty, .. }) = arg_kind
&& is_c_void(cx, *ty) {
let msg = format!("creating a `{type_str}` from a void raw pointer");
span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type");
}
}
}
/// Checks whether a `DefId` matches `Box`, `Rc`, `Arc`, or one of the `Weak` types.
/// Returns a static string slice with the name of the type, if one was found.
fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> {
// Box
if Some(def_id) == cx.tcx.lang_items().owned_box() {
return Some("Box");
}
if let Some(symbol) = cx.tcx.get_diagnostic_name(def_id) {
if symbol == sym::Arc {
return Some("Arc");
} else if symbol == sym::Rc {
return Some("Rc");
}
}
if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) {
Some("Weak")
} else {
None
}
}

View file

@ -254,7 +254,7 @@ declare_clippy_lint! {
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
#[clippy::version = "1.64.0"] #[clippy::version = "1.65.0"]
pub RESULT_LARGE_ERR, pub RESULT_LARGE_ERR,
perf, perf,
"function returning `Result` with large `Err` type" "function returning `Result` with large `Err` type"

View file

@ -50,7 +50,9 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
if let Some(attr) = attr { if let Some(attr) = attr {
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
} else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() } else if is_public
&& !is_proc_macro(cx.sess(), attrs)
&& trait_ref_of_method(cx, item.owner_id.def_id).is_none()
{ {
check_must_use_candidate( check_must_use_candidate(
cx, cx,
@ -175,7 +177,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet)
return false; // ignore `_` patterns return false; // ignore `_` patterns
} }
if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), pat.span, tys) is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), tys)
} else { } else {
false false
} }
@ -183,7 +185,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet)
static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
match *ty.kind() { match *ty.kind() {
// primitive types are never mutable // primitive types are never mutable
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
@ -192,12 +194,12 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m
|| KNOWN_WRAPPER_TYS || KNOWN_WRAPPER_TYS
.iter() .iter()
.any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
&& substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) && substs.types().any(|ty| is_mutable_ty(cx, ty, tys))
}, },
ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)),
ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys),
ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys)
}, },
// calling something constitutes a side effect, so return true on all callables // calling something constitutes a side effect, so return true on all callables
// also never calls need not be used, so return true for them, too // also never calls need not be used, so return true for them, too
@ -225,12 +227,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo
let mut tys = DefIdSet::default(); let mut tys = DefIdSet::default();
for arg in args { for arg in args {
if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
&& is_mutable_ty( && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys)
cx,
cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
arg.span,
&mut tys,
)
&& is_mutated_static(arg) && is_mutated_static(arg)
{ {
return ControlFlow::Break(()); return ControlFlow::Break(());
@ -243,12 +240,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo
let mut tys = DefIdSet::default(); let mut tys = DefIdSet::default();
for arg in std::iter::once(receiver).chain(args.iter()) { for arg in std::iter::once(receiver).chain(args.iter()) {
if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
&& is_mutable_ty( && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys)
cx,
cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
arg.span,
&mut tys,
)
&& is_mutated_static(arg) && is_mutated_static(arg)
{ {
return ControlFlow::Break(()); return ControlFlow::Break(());

View file

@ -2,12 +2,12 @@ use rustc_errors::Diagnostic;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Adt, Ty};
use rustc_span::{sym, Span}; use rustc_span::{sym, Span};
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::trait_ref_of_method; use clippy_utils::trait_ref_of_method;
use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item}; use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo};
use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
@ -84,17 +84,57 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S
} }
fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
let ty_size = approx_ty_size(cx, err_ty); if_chain! {
if ty_size >= large_err_threshold { if let Adt(adt, subst) = err_ty.kind();
span_lint_and_then( if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local();
cx, if let Some(hir::Node::Item(item)) = cx
RESULT_LARGE_ERR, .tcx
hir_ty_span, .hir()
"the `Err`-variant returned from this function is very large", .find_by_def_id(local_def_id);
|diag: &mut Diagnostic| { if let hir::ItemKind::Enum(ref def, _) = item.kind;
diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); then {
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); let variants_size = AdtVariantInfo::new(cx, *adt, subst);
}, if variants_size[0].size >= large_err_threshold {
); span_lint_and_then(
cx,
RESULT_LARGE_ERR,
hir_ty_span,
"the `Err`-variant returned from this function is very large",
|diag| {
diag.span_label(
def.variants[variants_size[0].ind].span,
format!("the largest variant contains at least {} bytes", variants_size[0].size),
);
for variant in &variants_size[1..] {
if variant.size >= large_err_threshold {
let variant_def = &def.variants[variant.ind];
diag.span_label(
variant_def.span,
format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size),
);
}
}
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
}
);
}
}
else {
let ty_size = approx_ty_size(cx, err_ty);
if ty_size >= large_err_threshold {
span_lint_and_then(
cx,
RESULT_LARGE_ERR,
hir_ty_span,
"the `Err`-variant returned from this function is very large",
|diag: &mut Diagnostic| {
diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
},
);
}
}
} }
} }

View file

@ -66,8 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
use rustc_span::BytePos; use rustc_span::BytePos;
fn suggestion<'tcx>( fn suggestion(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
diag: &mut Diagnostic, diag: &mut Diagnostic,
generics_span: Span, generics_span: Span,
generics_suggestion_span: Span, generics_suggestion_span: Span,

View file

@ -207,8 +207,8 @@ impl SliceLintInformation {
} }
} }
fn filter_lintable_slices<'a, 'tcx>( fn filter_lintable_slices<'tcx>(
cx: &'a LateContext<'tcx>, cx: &LateContext<'tcx>,
slice_lint_info: FxIndexMap<hir::HirId, SliceLintInformation>, slice_lint_info: FxIndexMap<hir::HirId, SliceLintInformation>,
max_suggested_slice: u64, max_suggested_slice: u64,
scope: &'tcx hir::Expr<'tcx>, scope: &'tcx hir::Expr<'tcx>,

View file

@ -171,11 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
/// Returns a tuple of options with the start and end (exclusive) values of /// Returns a tuple of options with the start and end (exclusive) values of
/// the range. If the start or end is not constant, None is returned. /// the range. If the start or end is not constant, None is returned.
fn to_const_range<'tcx>( fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) {
cx: &LateContext<'tcx>,
range: higher::Range<'_>,
array_size: u128,
) -> (Option<u128>, Option<u128>) {
let s = range let s = range
.start .start
.map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c));

View file

@ -0,0 +1,184 @@
use clippy_utils::{
diagnostics::{self, span_lint_and_sugg},
meets_msrv, msrvs, source,
sugg::Sugg,
ty,
};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{source_map::Spanned, sym};
declare_clippy_lint! {
/// ### What it does
/// Lints subtraction between `Instant::now()` and another `Instant`.
///
/// ### Why is this bad?
/// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
/// as `Instant` subtraction saturates.
///
/// `prev_instant.elapsed()` also more clearly signals intention.
///
/// ### Example
/// ```rust
/// use std::time::Instant;
/// let prev_instant = Instant::now();
/// let duration = Instant::now() - prev_instant;
/// ```
/// Use instead:
/// ```rust
/// use std::time::Instant;
/// let prev_instant = Instant::now();
/// let duration = prev_instant.elapsed();
/// ```
#[clippy::version = "1.65.0"]
pub MANUAL_INSTANT_ELAPSED,
pedantic,
"subtraction between `Instant::now()` and previous `Instant`"
}
declare_clippy_lint! {
/// ### What it does
/// Lints subtraction between an [`Instant`] and a [`Duration`].
///
/// ### Why is this bad?
/// Unchecked subtraction could cause underflow on certain platforms, leading to
/// unintentional panics.
///
/// ### Example
/// ```rust
/// # use std::time::{Instant, Duration};
/// let time_passed = Instant::now() - Duration::from_secs(5);
/// ```
///
/// Use instead:
/// ```rust
/// # use std::time::{Instant, Duration};
/// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
/// ```
///
/// [`Duration`]: std::time::Duration
/// [`Instant::now()`]: std::time::Instant::now;
#[clippy::version = "1.65.0"]
pub UNCHECKED_DURATION_SUBTRACTION,
suspicious,
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
}
pub struct InstantSubtraction {
msrv: Option<RustcVersion>,
}
impl InstantSubtraction {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]);
impl LateLintPass<'_> for InstantSubtraction {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Sub, ..
},
lhs,
rhs,
) = expr.kind
{
if_chain! {
if is_instant_now_call(cx, lhs);
if is_an_instant(cx, rhs);
if let Some(sugg) = Sugg::hir_opt(cx, rhs);
then {
print_manual_instant_elapsed_sugg(cx, expr, sugg)
} else {
if_chain! {
if !expr.span.from_expansion();
if meets_msrv(self.msrv, msrvs::TRY_FROM);
if is_an_instant(cx, lhs);
if is_a_duration(cx, rhs);
then {
print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr)
}
}
}
}
}
}
extract_msrv_attr!(LateContext);
}
fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
if let ExprKind::Call(fn_expr, []) = expr_block.kind
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
&& clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
{
true
} else {
false
}
}
fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
match expr_ty.kind() {
rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT),
_ => false,
}
}
fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration)
}
fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) {
span_lint_and_sugg(
cx,
MANUAL_INSTANT_ELAPSED,
expr.span,
"manual implementation of `Instant::elapsed`",
"try",
format!("{}.elapsed()", sugg.maybe_par()),
Applicability::MachineApplicable,
);
}
fn print_unchecked_duration_subtraction_sugg(
cx: &LateContext<'_>,
left_expr: &Expr<'_>,
right_expr: &Expr<'_>,
expr: &Expr<'_>,
) {
let mut applicability = Applicability::MachineApplicable;
let left_expr =
source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
let right_expr = source::snippet_with_applicability(
cx,
right_expr.span,
"std::time::Duration::from_secs(1)",
&mut applicability,
);
diagnostics::span_lint_and_sugg(
cx,
UNCHECKED_DURATION_SUBTRACTION,
expr.span,
"unchecked subtraction of a 'Duration' from an 'Instant'",
"try",
format!("{left_expr}.checked_sub({right_expr}).unwrap()"),
applicability,
);
}

View file

@ -63,58 +63,54 @@ impl IntPlusOne {
fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> { fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
match (binop, &lhs.kind, &rhs.kind) { match (binop, &lhs.kind, &rhs.kind) {
// case where `x - 1 >= ...` or `-1 + x >= ...` // case where `x - 1 >= ...` or `-1 + x >= ...`
(BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { (BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => {
match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
// `-1 + x` // `-1 + x`
(BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => {
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
}, },
// `x - 1` // `x - 1`
(BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
}, },
_ => None, _ => None,
} }
}, },
// case where `... >= y + 1` or `... >= 1 + y` // case where `... >= y + 1` or `... >= 1 + y`
(BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) (BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => {
if rhskind.node == BinOpKind::Add =>
{
match (&rhslhs.kind, &rhsrhs.kind) { match (&rhslhs.kind, &rhsrhs.kind) {
// `y + 1` and `1 + y` // `y + 1` and `1 + y`
(&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => {
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
}, },
(_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
}, },
_ => None, _ => None,
} }
}, },
// case where `x + 1 <= ...` or `1 + x <= ...` // case where `x + 1 <= ...` or `1 + x <= ...`
(BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) (BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => {
if lhskind.node == BinOpKind::Add =>
{
match (&lhslhs.kind, &lhsrhs.kind) { match (&lhslhs.kind, &lhsrhs.kind) {
// `1 + x` and `x + 1` // `1 + x` and `x + 1`
(&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => {
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
}, },
(_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
}, },
_ => None, _ => None,
} }
}, },
// case where `... >= y - 1` or `... >= -1 + y` // case where `... >= y - 1` or `... >= -1 + y`
(BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { (BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => {
match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
// `-1 + y` // `-1 + y`
(BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => {
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
}, },
// `y - 1` // `y - 1`
(BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
}, },
_ => None, _ => None,

View file

@ -38,7 +38,7 @@ declare_clippy_lint! {
declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(FullInt, FullInt)> {
if let ExprKind::Cast(cast_exp, _) = expr.kind { if let ExprKind::Cast(cast_exp, _) = expr.kind {
let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp);
let cast_ty = cx.typeck_results().expr_ty(expr); let cast_ty = cx.typeck_results().expr_ty(expr);

View file

@ -1,12 +1,15 @@
//! lint when there is a large size difference between variants on an enum //! lint when there is a large size difference between variants on an enum
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy}; use clippy_utils::{
diagnostics::span_lint_and_then,
ty::{approx_ty_size, is_copy, AdtVariantInfo},
};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty}; use rustc_middle::ty::{Adt, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
@ -72,49 +75,6 @@ impl LargeEnumVariant {
} }
} }
struct FieldInfo {
ind: usize,
size: u64,
}
struct VariantInfo {
ind: usize,
size: u64,
fields_size: Vec<FieldInfo>,
}
fn variants_size<'tcx>(
cx: &LateContext<'tcx>,
adt: AdtDef<'tcx>,
subst: &'tcx List<GenericArg<'tcx>>,
) -> Vec<VariantInfo> {
let mut variants_size = adt
.variants()
.iter()
.enumerate()
.map(|(i, variant)| {
let mut fields_size = variant
.fields
.iter()
.enumerate()
.map(|(i, f)| FieldInfo {
ind: i,
size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
})
.collect::<Vec<_>>();
fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
VariantInfo {
ind: i,
size: fields_size.iter().map(|info| info.size).sum(),
fields_size,
}
})
.collect::<Vec<_>>();
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
variants_size
}
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
@ -130,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
if adt.variants().len() <= 1 { if adt.variants().len() <= 1 {
return; return;
} }
let variants_size = variants_size(cx, *adt, subst); let variants_size = AdtVariantInfo::new(cx, *adt, subst);
let mut difference = variants_size[0].size - variants_size[1].size; let mut difference = variants_size[0].size - variants_size[1].size;
if difference > self.maximum_size_difference_allowed { if difference > self.maximum_size_difference_allowed {
@ -173,16 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
.fields_size .fields_size
.iter() .iter()
.rev() .rev()
.map_while(|val| { .map_while(|&(ind, size)| {
if difference > self.maximum_size_difference_allowed { if difference > self.maximum_size_difference_allowed {
difference = difference.saturating_sub(val.size); difference = difference.saturating_sub(size);
Some(( Some((
fields[val.ind].ty.span, fields[ind].ty.span,
format!( format!(
"Box<{}>", "Box<{}>",
snippet_with_applicability( snippet_with_applicability(
cx, cx,
fields[val.ind].ty.span, fields[ind].ty.span,
"..", "..",
&mut applicability &mut applicability
) )

View file

@ -366,8 +366,7 @@ fn check_for_is_empty<'tcx>(
} }
fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
{
// check if we are in an is_empty() method // check if we are in an is_empty() method
if let Some(name) = get_item_name(cx, method) { if let Some(name) = get_item_name(cx, method) {
if name.as_str() == "is_empty" { if name.as_str() == "is_empty" {

View file

@ -1,13 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type}; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
use clippy_utils::{is_must_use_func_call, paths}; use clippy_utils::{is_must_use_func_call, paths};
use if_chain::if_chain;
use rustc_hir::{Local, PatKind}; use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Symbol};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -30,13 +28,14 @@ declare_clippy_lint! {
#[clippy::version = "1.42.0"] #[clippy::version = "1.42.0"]
pub LET_UNDERSCORE_MUST_USE, pub LET_UNDERSCORE_MUST_USE,
restriction, restriction,
"non-binding let on a `#[must_use]` expression" "non-binding `let` on a `#[must_use]` expression"
} }
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for `let _ = sync_lock`. /// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in
/// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`. /// `parking_lot`. For `std` locks see the `rustc` lint
/// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock)
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// This statement immediately drops the lock instead of /// This statement immediately drops the lock instead of
@ -57,50 +56,41 @@ declare_clippy_lint! {
#[clippy::version = "1.43.0"] #[clippy::version = "1.43.0"]
pub LET_UNDERSCORE_LOCK, pub LET_UNDERSCORE_LOCK,
correctness, correctness,
"non-binding let on a synchronization lock" "non-binding `let` on a synchronization lock"
} }
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for `let _ = <expr>` /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
/// where expr has a type that implements `Drop`
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// This statement immediately drops the initializer /// Futures must be polled for work to be done. The original intention was most likely to await the future
/// expression instead of extending its lifetime to the end of the scope, which /// and ignore the resulting value.
/// is often not intended. To extend the expression's lifetime to the end of the
/// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
/// explicitly drop the expression, `std::mem::drop` conveys your intention
/// better and is less error-prone.
/// ///
/// ### Example /// ### Example
/// ```rust /// ```rust
/// # struct DroppableItem; /// async fn foo() -> Result<(), ()> {
/// { /// Ok(())
/// let _ = DroppableItem;
/// // ^ dropped here
/// /* more code */
/// } /// }
/// let _ = foo();
/// ``` /// ```
/// ///
/// Use instead: /// Use instead:
/// ```rust /// ```rust
/// # struct DroppableItem; /// # async fn context() {
/// { /// async fn foo() -> Result<(), ()> {
/// let _droppable = DroppableItem; /// Ok(())
/// /* more code */
/// // dropped at end of scope
/// } /// }
/// let _ = foo().await;
/// # }
/// ``` /// ```
#[clippy::version = "1.50.0"] #[clippy::version = "1.66"]
pub LET_UNDERSCORE_DROP, pub LET_UNDERSCORE_FUTURE,
pedantic, suspicious,
"non-binding let on a type that implements `Drop`" "non-binding `let` on a future"
} }
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]);
const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard];
const SYNC_GUARD_PATHS: [&[&str]; 3] = [ const SYNC_GUARD_PATHS: [&[&str]; 3] = [
&paths::PARKING_LOT_MUTEX_GUARD, &paths::PARKING_LOT_MUTEX_GUARD,
@ -110,64 +100,53 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [
impl<'tcx> LateLintPass<'tcx> for LetUnderscore { impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
if in_external_macro(cx.tcx.sess, local.span) { if !in_external_macro(cx.tcx.sess, local.span)
return; && let PatKind::Wild = local.pat.kind
} && let Some(init) = local.init
{
if_chain! { let init_ty = cx.typeck_results().expr_ty(init);
if let PatKind::Wild = local.pat.kind; let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
if let Some(init) = local.init; GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
then { GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
let init_ty = cx.typeck_results().expr_ty(init); });
let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { if contains_sync_guard {
GenericArgKind::Type(inner_ty) => { span_lint_and_help(
SYNC_GUARD_SYMS cx,
.iter() LET_UNDERSCORE_LOCK,
.any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym)) local.span,
|| SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) "non-binding `let` on a synchronization lock",
}, None,
"consider using an underscore-prefixed named \
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
});
if contains_sync_guard {
span_lint_and_help(
cx,
LET_UNDERSCORE_LOCK,
local.span,
"non-binding let on a synchronization lock",
None,
"consider using an underscore-prefixed named \
binding or dropping explicitly with `std::mem::drop`", binding or dropping explicitly with `std::mem::drop`",
); );
} else if init_ty.needs_drop(cx.tcx, cx.param_env) { } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
span_lint_and_help( && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) {
cx, span_lint_and_help(
LET_UNDERSCORE_DROP, cx,
local.span, LET_UNDERSCORE_FUTURE,
"non-binding `let` on a type that implements `Drop`", local.span,
None, "non-binding `let` on a future",
"consider using an underscore-prefixed named \ None,
binding or dropping explicitly with `std::mem::drop`", "consider awaiting the future or dropping explicitly with `std::mem::drop`"
); );
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
span_lint_and_help( span_lint_and_help(
cx, cx,
LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_MUST_USE,
local.span, local.span,
"non-binding let on an expression with `#[must_use]` type", "non-binding `let` on an expression with `#[must_use]` type",
None, None,
"consider explicitly using expression value", "consider explicitly using expression value",
); );
} else if is_must_use_func_call(cx, init) { } else if is_must_use_func_call(cx, init) {
span_lint_and_help( span_lint_and_help(
cx, cx,
LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_MUST_USE,
local.span, local.span,
"non-binding let on a result of a `#[must_use]` function", "non-binding `let` on a result of a `#[must_use]` function",
None, None,
"consider explicitly using function result", "consider explicitly using function result",
); );
}
} }
} }
} }

View file

@ -1,368 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
LintId::of(approx_const::APPROX_CONSTANT),
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(attrs::DEPRECATED_CFG_ATTR),
LintId::of(attrs::DEPRECATED_SEMVER),
LintId::of(attrs::MISMATCHED_TARGET_OS),
LintId::of(attrs::USELESS_ATTRIBUTE),
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
LintId::of(box_default::BOX_DEFAULT),
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_NAN_TO_INT),
LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(casts::UNNECESSARY_CAST),
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
LintId::of(collapsible_if::COLLAPSIBLE_IF),
LintId::of(comparison_chain::COMPARISON_CHAIN),
LintId::of(copies::IFS_SAME_COND),
LintId::of(copies::IF_SAME_THEN_ELSE),
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
LintId::of(dereference::EXPLICIT_AUTO_DEREF),
LintId::of(dereference::NEEDLESS_BORROW),
LintId::of(derivable_impls::DERIVABLE_IMPLS),
LintId::of(derive::DERIVE_HASH_XOR_EQ),
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(disallowed_macros::DISALLOWED_MACROS),
LintId::of(disallowed_methods::DISALLOWED_METHODS),
LintId::of(disallowed_names::DISALLOWED_NAMES),
LintId::of(disallowed_types::DISALLOWED_TYPES),
LintId::of(doc::MISSING_SAFETY_DOC),
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(double_parens::DOUBLE_PARENS),
LintId::of(drop_forget_ref::DROP_COPY),
LintId::of(drop_forget_ref::DROP_NON_DROP),
LintId::of(drop_forget_ref::DROP_REF),
LintId::of(drop_forget_ref::FORGET_COPY),
LintId::of(drop_forget_ref::FORGET_NON_DROP),
LintId::of(drop_forget_ref::FORGET_REF),
LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
LintId::of(duplicate_mod::DUPLICATE_MOD),
LintId::of(entry::MAP_ENTRY),
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
LintId::of(enum_variants::ENUM_VARIANT_NAMES),
LintId::of(enum_variants::MODULE_INCEPTION),
LintId::of(escape::BOXED_LOCAL),
LintId::of(eta_reduction::REDUNDANT_CLOSURE),
LintId::of(explicit_write::EXPLICIT_WRITE),
LintId::of(float_literal::EXCESSIVE_PRECISION),
LintId::of(format::USELESS_FORMAT),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
LintId::of(format_args::UNUSED_FORMAT_SPECS),
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(from_over_into::FROM_OVER_INTO),
LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(functions::RESULT_LARGE_ERR),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(functions::TOO_MANY_ARGUMENTS),
LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD),
LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
LintId::of(int_plus_one::INT_PLUS_ONE),
LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(len_zero::LEN_ZERO),
LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
LintId::of(lifetimes::NEEDLESS_LIFETIMES),
LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(loops::EMPTY_LOOP),
LintId::of(loops::EXPLICIT_COUNTER_LOOP),
LintId::of(loops::FOR_KV_MAP),
LintId::of(loops::ITER_NEXT_LOOP),
LintId::of(loops::MANUAL_FIND),
LintId::of(loops::MANUAL_FLATTEN),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::MISSING_SPIN_LOOP),
LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(loops::NEEDLESS_RANGE_LOOP),
LintId::of(loops::NEVER_LOOP),
LintId::of(loops::SAME_ITEM_PUSH),
LintId::of(loops::SINGLE_ELEMENT_LOOP),
LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
LintId::of(loops::WHILE_LET_LOOP),
LintId::of(loops::WHILE_LET_ON_ITERATOR),
LintId::of(main_recursion::MAIN_RECURSION),
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_clamp::MANUAL_CLAMP),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
LintId::of(manual_retain::MANUAL_RETAIN),
LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::COLLAPSIBLE_MATCH),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MANUAL_FILTER),
LintId::of(matches::MANUAL_MAP),
LintId::of(matches::MANUAL_UNWRAP_OR),
LintId::of(matches::MATCH_AS_REF),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(matches::MATCH_OVERLAPPING_ARM),
LintId::of(matches::MATCH_REF_PATS),
LintId::of(matches::MATCH_SINGLE_BINDING),
LintId::of(matches::MATCH_STR_CASE_MISMATCH),
LintId::of(matches::NEEDLESS_MATCH),
LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
LintId::of(matches::SINGLE_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::CLONE_ON_COPY),
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::ERR_EXPECT),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT),
LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::GET_FIRST),
LintId::of(methods::GET_LAST_WITH_LEN),
LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_COUNT),
LintId::of(methods::ITER_KV_MAP),
LintId::of(methods::ITER_NEXT_SLICE),
LintId::of(methods::ITER_NTH),
LintId::of(methods::ITER_NTH_ZERO),
LintId::of(methods::ITER_OVEREAGER_CLONED),
LintId::of(methods::ITER_SKIP_NEXT),
LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MANUAL_SPLIT_ONCE),
LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
LintId::of(methods::NEEDLESS_OPTION_TAKE),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::OPTION_MAP_OR_NONE),
LintId::of(methods::OR_FUN_CALL),
LintId::of(methods::OR_THEN_UNWRAP),
LintId::of(methods::RANGE_ZIP_WITH_LEN),
LintId::of(methods::REPEAT_ONCE),
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
LintId::of(methods::SINGLE_CHAR_ADD_STR),
LintId::of(methods::SINGLE_CHAR_PATTERN),
LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::STRING_EXTEND_CHARS),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::SUSPICIOUS_TO_OWNED),
LintId::of(methods::UNINIT_ASSUMED_INIT),
LintId::of(methods::UNIT_HASH),
LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_FOLD),
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::UNNECESSARY_TO_OWNED),
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
LintId::of(methods::USELESS_ASREF),
LintId::of(methods::VEC_RESIZE_TO_ZERO),
LintId::of(methods::WRONG_SELF_CONVENTION),
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc::TOPLEVEL_REF_ARG),
LintId::of(misc::ZERO_PTR),
LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
LintId::of(misc_early::DOUBLE_NEG),
LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
LintId::of(misc_early::REDUNDANT_PATTERN),
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON),
LintId::of(needless_bool::NEEDLESS_BOOL),
LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(needless_update::NEEDLESS_UPDATE),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(neg_multiply::NEG_MULTIPLY),
LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION),
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::ASSIGN_OP_PATTERN),
LintId::of(operators::BAD_BIT_MASK),
LintId::of(operators::CMP_NAN),
LintId::of(operators::CMP_OWNED),
LintId::of(operators::DOUBLE_COMPARISONS),
LintId::of(operators::DURATION_SUBSEC),
LintId::of(operators::EQ_OP),
LintId::of(operators::ERASING_OP),
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(operators::IDENTITY_OP),
LintId::of(operators::INEFFECTIVE_BIT_MASK),
LintId::of(operators::MISREFACTORED_ASSIGN_OP),
LintId::of(operators::MODULO_ONE),
LintId::of(operators::OP_REF),
LintId::of(operators::PTR_EQ),
LintId::of(operators::SELF_ASSIGNMENT),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
LintId::of(precedence::PRECEDENCE),
LintId::of(ptr::CMP_NULL),
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
LintId::of(ptr::MUT_FROM_REF),
LintId::of(ptr::PTR_ARG),
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(question_mark::QUESTION_MARK),
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(reference::DEREF_ADDROF),
LintId::of(regex::INVALID_REGEX),
LintId::of(returns::LET_AND_RETURN),
LintId::of(returns::NEEDLESS_RETURN),
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
LintId::of(strings::TRIM_SPLIT_WHITESPACE),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap::ALMOST_SWAPPED),
LintId::of(swap::MANUAL_SWAP),
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(types::BORROWED_BOX),
LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::LET_UNIT_VALUE),
LintId::of(unit_types::UNIT_ARG),
LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(unwrap::PANICKING_UNWRAP),
LintId::of(unwrap::UNNECESSARY_UNWRAP),
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
LintId::of(write::PRINTLN_EMPTY_STRING),
LintId::of(write::PRINT_LITERAL),
LintId::of(write::PRINT_WITH_NEWLINE),
LintId::of(write::WRITELN_EMPTY_STRING),
LintId::of(write::WRITE_LITERAL),
LintId::of(write::WRITE_WITH_NEWLINE),
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
])

View file

@ -1,11 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
LintId::of(cargo::CARGO_COMMON_METADATA),
LintId::of(cargo::MULTIPLE_CRATE_VERSIONS),
LintId::of(cargo::NEGATIVE_FEATURE_NAMES),
LintId::of(cargo::REDUNDANT_FEATURE_NAMES),
LintId::of(cargo::WILDCARD_DEPENDENCIES),
])

View file

@ -1,111 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
LintId::of(attrs::DEPRECATED_CFG_ATTR),
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::UNNECESSARY_CAST),
LintId::of(dereference::EXPLICIT_AUTO_DEREF),
LintId::of(derivable_impls::DERIVABLE_IMPLS),
LintId::of(double_parens::DOUBLE_PARENS),
LintId::of(explicit_write::EXPLICIT_WRITE),
LintId::of(format::USELESS_FORMAT),
LintId::of(format_args::UNUSED_FORMAT_SPECS),
LintId::of(functions::TOO_MANY_ARGUMENTS),
LintId::of(int_plus_one::INT_PLUS_ONE),
LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
LintId::of(lifetimes::NEEDLESS_LIFETIMES),
LintId::of(loops::EXPLICIT_COUNTER_LOOP),
LintId::of(loops::MANUAL_FIND),
LintId::of(loops::MANUAL_FLATTEN),
LintId::of(loops::SINGLE_ELEMENT_LOOP),
LintId::of(loops::WHILE_LET_LOOP),
LintId::of(manual_clamp::MANUAL_CLAMP),
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::MANUAL_FILTER),
LintId::of(matches::MANUAL_UNWRAP_OR),
LintId::of(matches::MATCH_AS_REF),
LintId::of(matches::MATCH_SINGLE_BINDING),
LintId::of(matches::NEEDLESS_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::CLONE_ON_COPY),
LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT),
LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::GET_LAST_WITH_LEN),
LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::ITER_COUNT),
LintId::of(methods::ITER_KV_MAP),
LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MANUAL_SPLIT_ONCE),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
LintId::of(methods::NEEDLESS_OPTION_TAKE),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::OR_THEN_UNWRAP),
LintId::of(methods::RANGE_ZIP_WITH_LEN),
LintId::of(methods::REPEAT_ONCE),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::USELESS_ASREF),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON),
LintId::of(needless_bool::NEEDLESS_BOOL),
LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(needless_update::NEEDLESS_UPDATE),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::DOUBLE_COMPARISONS),
LintId::of(operators::DURATION_SUBSEC),
LintId::of(operators::IDENTITY_OP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(precedence::PRECEDENCE),
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(reference::DEREF_ADDROF),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(swap::MANUAL_SWAP),
LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(types::BORROWED_BOX),
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX),
LintId::of(unit_types::UNIT_ARG),
LintId::of(unwrap::UNNECESSARY_UNWRAP),
LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
])

View file

@ -1,78 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
LintId::of(approx_const::APPROX_CONSTANT),
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
LintId::of(attrs::DEPRECATED_SEMVER),
LintId::of(attrs::MISMATCHED_TARGET_OS),
LintId::of(attrs::USELESS_ATTRIBUTE),
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
LintId::of(copies::IFS_SAME_COND),
LintId::of(copies::IF_SAME_THEN_ELSE),
LintId::of(derive::DERIVE_HASH_XOR_EQ),
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(drop_forget_ref::DROP_COPY),
LintId::of(drop_forget_ref::DROP_REF),
LintId::of(drop_forget_ref::FORGET_COPY),
LintId::of(drop_forget_ref::FORGET_REF),
LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
LintId::of(loops::ITER_NEXT_LOOP),
LintId::of(loops::NEVER_LOOP),
LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
LintId::of(matches::MATCH_STR_CASE_MISMATCH),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::UNINIT_ASSUMED_INIT),
LintId::of(methods::UNIT_HASH),
LintId::of(methods::VEC_RESIZE_TO_ZERO),
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::BAD_BIT_MASK),
LintId::of(operators::CMP_NAN),
LintId::of(operators::EQ_OP),
LintId::of(operators::ERASING_OP),
LintId::of(operators::INEFFECTIVE_BIT_MASK),
LintId::of(operators::MODULO_ONE),
LintId::of(operators::SELF_ASSIGNMENT),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
LintId::of(ptr::MUT_FROM_REF),
LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
LintId::of(regex::INVALID_REGEX),
LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(swap::ALMOST_SWAPPED),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(unwrap::PANICKING_UNWRAP),
])

View file

@ -1,22 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL),
LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS),
LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS),
LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE),
LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL),
LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR),
LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS),
LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON),
LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT),
LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS),
LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL),
LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA),
LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE),
LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH),
])

View file

@ -1,620 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_lints(&[
#[cfg(feature = "internal")]
utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL,
#[cfg(feature = "internal")]
utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
#[cfg(feature = "internal")]
utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS,
#[cfg(feature = "internal")]
utils::internal_lints::if_chain_style::IF_CHAIN_STYLE,
#[cfg(feature = "internal")]
utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL,
#[cfg(feature = "internal")]
utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR,
#[cfg(feature = "internal")]
utils::internal_lints::invalid_paths::INVALID_PATHS,
#[cfg(feature = "internal")]
utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON,
#[cfg(feature = "internal")]
utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT,
#[cfg(feature = "internal")]
utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE,
#[cfg(feature = "internal")]
utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS,
#[cfg(feature = "internal")]
utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE,
#[cfg(feature = "internal")]
utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL,
#[cfg(feature = "internal")]
utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA,
#[cfg(feature = "internal")]
utils::internal_lints::produce_ice::PRODUCE_ICE,
#[cfg(feature = "internal")]
utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH,
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
approx_const::APPROX_CONSTANT,
as_conversions::AS_CONVERSIONS,
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES,
async_yields_async::ASYNC_YIELDS_ASYNC,
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
attrs::DEPRECATED_CFG_ATTR,
attrs::DEPRECATED_SEMVER,
attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
attrs::INLINE_ALWAYS,
attrs::MISMATCHED_TARGET_OS,
attrs::USELESS_ATTRIBUTE,
await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
await_holding_invalid::AWAIT_HOLDING_LOCK,
await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
bool_to_int_with_if::BOOL_TO_INT_WITH_IF,
booleans::NONMINIMAL_BOOL,
booleans::OVERLY_COMPLEX_BOOL_EXPR,
borrow_deref_ref::BORROW_DEREF_REF,
box_default::BOX_DEFAULT,
cargo::CARGO_COMMON_METADATA,
cargo::MULTIPLE_CRATE_VERSIONS,
cargo::NEGATIVE_FEATURE_NAMES,
cargo::REDUNDANT_FEATURE_NAMES,
cargo::WILDCARD_DEPENDENCIES,
casts::AS_PTR_CAST_MUT,
casts::AS_UNDERSCORE,
casts::BORROW_AS_PTR,
casts::CAST_ABS_TO_UNSIGNED,
casts::CAST_ENUM_CONSTRUCTOR,
casts::CAST_ENUM_TRUNCATION,
casts::CAST_LOSSLESS,
casts::CAST_NAN_TO_INT,
casts::CAST_POSSIBLE_TRUNCATION,
casts::CAST_POSSIBLE_WRAP,
casts::CAST_PRECISION_LOSS,
casts::CAST_PTR_ALIGNMENT,
casts::CAST_REF_TO_MUT,
casts::CAST_SIGN_LOSS,
casts::CAST_SLICE_DIFFERENT_SIZES,
casts::CAST_SLICE_FROM_RAW_PARTS,
casts::CHAR_LIT_AS_U8,
casts::FN_TO_NUMERIC_CAST,
casts::FN_TO_NUMERIC_CAST_ANY,
casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
casts::PTR_AS_PTR,
casts::UNNECESSARY_CAST,
checked_conversions::CHECKED_CONVERSIONS,
cognitive_complexity::COGNITIVE_COMPLEXITY,
collapsible_if::COLLAPSIBLE_ELSE_IF,
collapsible_if::COLLAPSIBLE_IF,
comparison_chain::COMPARISON_CHAIN,
copies::BRANCHES_SHARING_CODE,
copies::IFS_SAME_COND,
copies::IF_SAME_THEN_ELSE,
copies::SAME_FUNCTIONS_IN_IF_CONDITION,
copy_iterator::COPY_ITERATOR,
crate_in_macro_def::CRATE_IN_MACRO_DEF,
create_dir::CREATE_DIR,
dbg_macro::DBG_MACRO,
default::DEFAULT_TRAIT_ACCESS,
default::FIELD_REASSIGN_WITH_DEFAULT,
default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
default_union_representation::DEFAULT_UNION_REPRESENTATION,
dereference::EXPLICIT_AUTO_DEREF,
dereference::EXPLICIT_DEREF_METHODS,
dereference::NEEDLESS_BORROW,
dereference::REF_BINDING_TO_REFERENCE,
derivable_impls::DERIVABLE_IMPLS,
derive::DERIVE_HASH_XOR_EQ,
derive::DERIVE_ORD_XOR_PARTIAL_ORD,
derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
derive::EXPL_IMPL_CLONE_ON_COPY,
derive::UNSAFE_DERIVE_DESERIALIZE,
disallowed_macros::DISALLOWED_MACROS,
disallowed_methods::DISALLOWED_METHODS,
disallowed_names::DISALLOWED_NAMES,
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
disallowed_types::DISALLOWED_TYPES,
doc::DOC_LINK_WITH_QUOTES,
doc::DOC_MARKDOWN,
doc::MISSING_ERRORS_DOC,
doc::MISSING_PANICS_DOC,
doc::MISSING_SAFETY_DOC,
doc::NEEDLESS_DOCTEST_MAIN,
double_parens::DOUBLE_PARENS,
drop_forget_ref::DROP_COPY,
drop_forget_ref::DROP_NON_DROP,
drop_forget_ref::DROP_REF,
drop_forget_ref::FORGET_COPY,
drop_forget_ref::FORGET_NON_DROP,
drop_forget_ref::FORGET_REF,
drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
duplicate_mod::DUPLICATE_MOD,
else_if_without_else::ELSE_IF_WITHOUT_ELSE,
empty_drop::EMPTY_DROP,
empty_enum::EMPTY_ENUM,
empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
entry::MAP_ENTRY,
enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
enum_variants::ENUM_VARIANT_NAMES,
enum_variants::MODULE_INCEPTION,
enum_variants::MODULE_NAME_REPETITIONS,
equatable_if_let::EQUATABLE_IF_LET,
escape::BOXED_LOCAL,
eta_reduction::REDUNDANT_CLOSURE,
eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
excessive_bools::STRUCT_EXCESSIVE_BOOLS,
exhaustive_items::EXHAUSTIVE_ENUMS,
exhaustive_items::EXHAUSTIVE_STRUCTS,
exit::EXIT,
explicit_write::EXPLICIT_WRITE,
fallible_impl_from::FALLIBLE_IMPL_FROM,
float_literal::EXCESSIVE_PRECISION,
float_literal::LOSSY_FLOAT_LITERAL,
floating_point_arithmetic::IMPRECISE_FLOPS,
floating_point_arithmetic::SUBOPTIMAL_FLOPS,
format::USELESS_FORMAT,
format_args::FORMAT_IN_FORMAT_ARGS,
format_args::TO_STRING_IN_FORMAT_ARGS,
format_args::UNINLINED_FORMAT_ARGS,
format_args::UNUSED_FORMAT_SPECS,
format_impl::PRINT_IN_FORMAT_IMPL,
format_impl::RECURSIVE_FORMAT_IMPL,
format_push_string::FORMAT_PUSH_STRING,
formatting::POSSIBLE_MISSING_COMMA,
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
formatting::SUSPICIOUS_ELSE_FORMATTING,
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
from_over_into::FROM_OVER_INTO,
from_str_radix_10::FROM_STR_RADIX_10,
functions::DOUBLE_MUST_USE,
functions::MUST_USE_CANDIDATE,
functions::MUST_USE_UNIT,
functions::NOT_UNSAFE_PTR_ARG_DEREF,
functions::RESULT_LARGE_ERR,
functions::RESULT_UNIT_ERR,
functions::TOO_MANY_ARGUMENTS,
functions::TOO_MANY_LINES,
future_not_send::FUTURE_NOT_SEND,
if_let_mutex::IF_LET_MUTEX,
if_not_else::IF_NOT_ELSE,
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
implicit_hasher::IMPLICIT_HASHER,
implicit_return::IMPLICIT_RETURN,
implicit_saturating_add::IMPLICIT_SATURATING_ADD,
implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
index_refutable_slice::INDEX_REFUTABLE_SLICE,
indexing_slicing::INDEXING_SLICING,
indexing_slicing::OUT_OF_BOUNDS_INDEXING,
infinite_iter::INFINITE_ITER,
infinite_iter::MAYBE_INFINITE_ITER,
inherent_impl::MULTIPLE_INHERENT_IMPL,
inherent_to_string::INHERENT_TO_STRING,
inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
init_numbered_fields::INIT_NUMBERED_FIELDS,
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
int_plus_one::INT_PLUS_ONE,
invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
items_after_statements::ITEMS_AFTER_STATEMENTS,
iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
large_const_arrays::LARGE_CONST_ARRAYS,
large_enum_variant::LARGE_ENUM_VARIANT,
large_include_file::LARGE_INCLUDE_FILE,
large_stack_arrays::LARGE_STACK_ARRAYS,
len_zero::COMPARISON_TO_EMPTY,
len_zero::LEN_WITHOUT_IS_EMPTY,
len_zero::LEN_ZERO,
let_if_seq::USELESS_LET_IF_SEQ,
let_underscore::LET_UNDERSCORE_DROP,
let_underscore::LET_UNDERSCORE_LOCK,
let_underscore::LET_UNDERSCORE_MUST_USE,
lifetimes::EXTRA_UNUSED_LIFETIMES,
lifetimes::NEEDLESS_LIFETIMES,
literal_representation::DECIMAL_LITERAL_REPRESENTATION,
literal_representation::INCONSISTENT_DIGIT_GROUPING,
literal_representation::LARGE_DIGIT_GROUPS,
literal_representation::MISTYPED_LITERAL_SUFFIXES,
literal_representation::UNREADABLE_LITERAL,
literal_representation::UNUSUAL_BYTE_GROUPINGS,
loops::EMPTY_LOOP,
loops::EXPLICIT_COUNTER_LOOP,
loops::EXPLICIT_INTO_ITER_LOOP,
loops::EXPLICIT_ITER_LOOP,
loops::FOR_KV_MAP,
loops::ITER_NEXT_LOOP,
loops::MANUAL_FIND,
loops::MANUAL_FLATTEN,
loops::MANUAL_MEMCPY,
loops::MISSING_SPIN_LOOP,
loops::MUT_RANGE_BOUND,
loops::NEEDLESS_COLLECT,
loops::NEEDLESS_RANGE_LOOP,
loops::NEVER_LOOP,
loops::SAME_ITEM_PUSH,
loops::SINGLE_ELEMENT_LOOP,
loops::WHILE_IMMUTABLE_CONDITION,
loops::WHILE_LET_LOOP,
loops::WHILE_LET_ON_ITERATOR,
macro_use::MACRO_USE_IMPORTS,
main_recursion::MAIN_RECURSION,
manual_assert::MANUAL_ASSERT,
manual_async_fn::MANUAL_ASYNC_FN,
manual_bits::MANUAL_BITS,
manual_clamp::MANUAL_CLAMP,
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
manual_rem_euclid::MANUAL_REM_EUCLID,
manual_retain::MANUAL_RETAIN,
manual_string_new::MANUAL_STRING_NEW,
manual_strip::MANUAL_STRIP,
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_result_ok::MATCH_RESULT_OK,
matches::COLLAPSIBLE_MATCH,
matches::INFALLIBLE_DESTRUCTURING_MATCH,
matches::MANUAL_FILTER,
matches::MANUAL_MAP,
matches::MANUAL_UNWRAP_OR,
matches::MATCH_AS_REF,
matches::MATCH_BOOL,
matches::MATCH_LIKE_MATCHES_MACRO,
matches::MATCH_ON_VEC_ITEMS,
matches::MATCH_OVERLAPPING_ARM,
matches::MATCH_REF_PATS,
matches::MATCH_SAME_ARMS,
matches::MATCH_SINGLE_BINDING,
matches::MATCH_STR_CASE_MISMATCH,
matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
matches::MATCH_WILD_ERR_ARM,
matches::NEEDLESS_MATCH,
matches::REDUNDANT_PATTERN_MATCHING,
matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
matches::SINGLE_MATCH,
matches::SINGLE_MATCH_ELSE,
matches::TRY_ERR,
matches::WILDCARD_ENUM_MATCH_ARM,
matches::WILDCARD_IN_OR_PATTERNS,
mem_forget::MEM_FORGET,
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
mem_replace::MEM_REPLACE_WITH_DEFAULT,
mem_replace::MEM_REPLACE_WITH_UNINIT,
methods::BIND_INSTEAD_OF_MAP,
methods::BYTES_COUNT_TO_LEN,
methods::BYTES_NTH,
methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
methods::CHARS_LAST_CMP,
methods::CHARS_NEXT_CMP,
methods::CLONED_INSTEAD_OF_COPIED,
methods::CLONE_DOUBLE_REF,
methods::CLONE_ON_COPY,
methods::CLONE_ON_REF_PTR,
methods::COLLAPSIBLE_STR_REPLACE,
methods::ERR_EXPECT,
methods::EXPECT_FUN_CALL,
methods::EXPECT_USED,
methods::EXTEND_WITH_DRAIN,
methods::FILETYPE_IS_FILE,
methods::FILTER_MAP_IDENTITY,
methods::FILTER_MAP_NEXT,
methods::FILTER_NEXT,
methods::FLAT_MAP_IDENTITY,
methods::FLAT_MAP_OPTION,
methods::FROM_ITER_INSTEAD_OF_COLLECT,
methods::GET_FIRST,
methods::GET_LAST_WITH_LEN,
methods::GET_UNWRAP,
methods::IMPLICIT_CLONE,
methods::INEFFICIENT_TO_STRING,
methods::INSPECT_FOR_EACH,
methods::INTO_ITER_ON_REF,
methods::IS_DIGIT_ASCII_RADIX,
methods::ITERATOR_STEP_BY_ZERO,
methods::ITER_CLONED_COLLECT,
methods::ITER_COUNT,
methods::ITER_KV_MAP,
methods::ITER_NEXT_SLICE,
methods::ITER_NTH,
methods::ITER_NTH_ZERO,
methods::ITER_ON_EMPTY_COLLECTIONS,
methods::ITER_ON_SINGLE_ITEMS,
methods::ITER_OVEREAGER_CLONED,
methods::ITER_SKIP_NEXT,
methods::ITER_WITH_DRAIN,
methods::MANUAL_FILTER_MAP,
methods::MANUAL_FIND_MAP,
methods::MANUAL_OK_OR,
methods::MANUAL_SATURATING_ARITHMETIC,
methods::MANUAL_SPLIT_ONCE,
methods::MANUAL_STR_REPEAT,
methods::MAP_CLONE,
methods::MAP_COLLECT_RESULT_UNIT,
methods::MAP_ERR_IGNORE,
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
methods::MUT_MUTEX_LOCK,
methods::NAIVE_BYTECOUNT,
methods::NEEDLESS_OPTION_AS_DEREF,
methods::NEEDLESS_OPTION_TAKE,
methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF,
methods::NONSENSICAL_OPEN_OPTIONS,
methods::NO_EFFECT_REPLACE,
methods::OBFUSCATED_IF_ELSE,
methods::OK_EXPECT,
methods::OPTION_AS_REF_DEREF,
methods::OPTION_FILTER_MAP,
methods::OPTION_MAP_OR_NONE,
methods::OR_FUN_CALL,
methods::OR_THEN_UNWRAP,
methods::PATH_BUF_PUSH_OVERWRITE,
methods::RANGE_ZIP_WITH_LEN,
methods::REPEAT_ONCE,
methods::RESULT_MAP_OR_INTO_OPTION,
methods::SEARCH_IS_SOME,
methods::SHOULD_IMPLEMENT_TRAIT,
methods::SINGLE_CHAR_ADD_STR,
methods::SINGLE_CHAR_PATTERN,
methods::SKIP_WHILE_NEXT,
methods::STABLE_SORT_PRIMITIVE,
methods::STRING_EXTEND_CHARS,
methods::SUSPICIOUS_MAP,
methods::SUSPICIOUS_SPLITN,
methods::SUSPICIOUS_TO_OWNED,
methods::UNINIT_ASSUMED_INIT,
methods::UNIT_HASH,
methods::UNNECESSARY_FILTER_MAP,
methods::UNNECESSARY_FIND_MAP,
methods::UNNECESSARY_FOLD,
methods::UNNECESSARY_JOIN,
methods::UNNECESSARY_LAZY_EVALUATIONS,
methods::UNNECESSARY_SORT_BY,
methods::UNNECESSARY_TO_OWNED,
methods::UNWRAP_OR_ELSE_DEFAULT,
methods::UNWRAP_USED,
methods::USELESS_ASREF,
methods::VEC_RESIZE_TO_ZERO,
methods::VERBOSE_FILE_READS,
methods::WRONG_SELF_CONVENTION,
methods::ZST_OFFSET,
minmax::MIN_MAX,
misc::SHORT_CIRCUIT_STATEMENT,
misc::TOPLEVEL_REF_ARG,
misc::USED_UNDERSCORE_BINDING,
misc::ZERO_PTR,
misc_early::BUILTIN_TYPE_SHADOW,
misc_early::DOUBLE_NEG,
misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
misc_early::MIXED_CASE_HEX_LITERALS,
misc_early::REDUNDANT_PATTERN,
misc_early::SEPARATED_LITERAL_SUFFIX,
misc_early::UNNEEDED_FIELD_PATTERN,
misc_early::UNNEEDED_WILDCARD_PATTERN,
misc_early::UNSEPARATED_LITERAL_SUFFIX,
misc_early::ZERO_PREFIXED_LITERAL,
mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
missing_const_for_fn::MISSING_CONST_FOR_FN,
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
missing_trait_methods::MISSING_TRAIT_METHODS,
mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION,
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
module_style::MOD_MODULE_FILES,
module_style::SELF_NAMED_MODULE_FILES,
multi_assignments::MULTI_ASSIGNMENTS,
mut_key::MUTABLE_KEY_TYPE,
mut_mut::MUT_MUT,
mut_reference::UNNECESSARY_MUT_PASSED,
mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
mutex_atomic::MUTEX_ATOMIC,
mutex_atomic::MUTEX_INTEGER,
needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
needless_bool::BOOL_COMPARISON,
needless_bool::NEEDLESS_BOOL,
needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
needless_continue::NEEDLESS_CONTINUE,
needless_for_each::NEEDLESS_FOR_EACH,
needless_late_init::NEEDLESS_LATE_INIT,
needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS,
needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
needless_question_mark::NEEDLESS_QUESTION_MARK,
needless_update::NEEDLESS_UPDATE,
neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
neg_multiply::NEG_MULTIPLY,
new_without_default::NEW_WITHOUT_DEFAULT,
no_effect::NO_EFFECT,
no_effect::NO_EFFECT_UNDERSCORE_BINDING,
no_effect::UNNECESSARY_OPERATION,
non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
non_expressive_names::MANY_SINGLE_CHAR_NAMES,
non_expressive_names::SIMILAR_NAMES,
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
octal_escapes::OCTAL_ESCAPES,
only_used_in_recursion::ONLY_USED_IN_RECURSION,
operators::ABSURD_EXTREME_COMPARISONS,
operators::ARITHMETIC_SIDE_EFFECTS,
operators::ASSIGN_OP_PATTERN,
operators::BAD_BIT_MASK,
operators::CMP_NAN,
operators::CMP_OWNED,
operators::DOUBLE_COMPARISONS,
operators::DURATION_SUBSEC,
operators::EQ_OP,
operators::ERASING_OP,
operators::FLOAT_ARITHMETIC,
operators::FLOAT_CMP,
operators::FLOAT_CMP_CONST,
operators::FLOAT_EQUALITY_WITHOUT_ABS,
operators::IDENTITY_OP,
operators::INEFFECTIVE_BIT_MASK,
operators::INTEGER_ARITHMETIC,
operators::INTEGER_DIVISION,
operators::MISREFACTORED_ASSIGN_OP,
operators::MODULO_ARITHMETIC,
operators::MODULO_ONE,
operators::NEEDLESS_BITWISE_BOOL,
operators::OP_REF,
operators::PTR_EQ,
operators::SELF_ASSIGNMENT,
operators::VERBOSE_BIT_MASK,
option_env_unwrap::OPTION_ENV_UNWRAP,
option_if_let_else::OPTION_IF_LET_ELSE,
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
panic_in_result_fn::PANIC_IN_RESULT_FN,
panic_unimplemented::PANIC,
panic_unimplemented::TODO,
panic_unimplemented::UNIMPLEMENTED,
panic_unimplemented::UNREACHABLE,
partial_pub_fields::PARTIAL_PUB_FIELDS,
partialeq_ne_impl::PARTIALEQ_NE_IMPL,
partialeq_to_none::PARTIALEQ_TO_NONE,
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
precedence::PRECEDENCE,
ptr::CMP_NULL,
ptr::INVALID_NULL_PTR_USAGE,
ptr::MUT_FROM_REF,
ptr::PTR_ARG,
ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
pub_use::PUB_USE,
question_mark::QUESTION_MARK,
ranges::MANUAL_RANGE_CONTAINS,
ranges::RANGE_MINUS_ONE,
ranges::RANGE_PLUS_ONE,
ranges::REVERSED_EMPTY_RANGES,
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
redundant_clone::REDUNDANT_CLONE,
redundant_closure_call::REDUNDANT_CLOSURE_CALL,
redundant_else::REDUNDANT_ELSE,
redundant_field_names::REDUNDANT_FIELD_NAMES,
redundant_pub_crate::REDUNDANT_PUB_CRATE,
redundant_slicing::DEREF_BY_SLICING,
redundant_slicing::REDUNDANT_SLICING,
redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
ref_option_ref::REF_OPTION_REF,
reference::DEREF_ADDROF,
regex::INVALID_REGEX,
regex::TRIVIAL_REGEX,
return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
returns::LET_AND_RETURN,
returns::NEEDLESS_RETURN,
same_name_method::SAME_NAME_METHOD,
self_named_constructors::SELF_NAMED_CONSTRUCTORS,
semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
serde_api::SERDE_API_MISUSE,
shadow::SHADOW_REUSE,
shadow::SHADOW_SAME,
shadow::SHADOW_UNRELATED,
single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
std_instead_of_core::STD_INSTEAD_OF_ALLOC,
std_instead_of_core::STD_INSTEAD_OF_CORE,
strings::STRING_ADD,
strings::STRING_ADD_ASSIGN,
strings::STRING_FROM_UTF8_AS_BYTES,
strings::STRING_LIT_AS_BYTES,
strings::STRING_SLICE,
strings::STRING_TO_STRING,
strings::STR_TO_STRING,
strings::TRIM_SPLIT_WHITESPACE,
strlen_on_c_strings::STRLEN_ON_C_STRINGS,
suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
swap::ALMOST_SWAPPED,
swap::MANUAL_SWAP,
swap_ptr_to_ref::SWAP_PTR_TO_REF,
tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
temporary_assignment::TEMPORARY_ASSIGNMENT,
to_digit_is_some::TO_DIGIT_IS_SOME,
trailing_empty_array::TRAILING_EMPTY_ARRAY,
trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
transmute::CROSSPOINTER_TRANSMUTE,
transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
transmute::TRANSMUTE_BYTES_TO_STR,
transmute::TRANSMUTE_FLOAT_TO_INT,
transmute::TRANSMUTE_INT_TO_BOOL,
transmute::TRANSMUTE_INT_TO_CHAR,
transmute::TRANSMUTE_INT_TO_FLOAT,
transmute::TRANSMUTE_NUM_TO_BYTES,
transmute::TRANSMUTE_PTR_TO_PTR,
transmute::TRANSMUTE_PTR_TO_REF,
transmute::TRANSMUTE_UNDEFINED_REPR,
transmute::TRANSMUTING_NULL,
transmute::UNSOUND_COLLECTION_TRANSMUTE,
transmute::USELESS_TRANSMUTE,
transmute::WRONG_TRANSMUTE,
types::BORROWED_BOX,
types::BOX_COLLECTION,
types::LINKEDLIST,
types::OPTION_OPTION,
types::RC_BUFFER,
types::RC_MUTEX,
types::REDUNDANT_ALLOCATION,
types::TYPE_COMPLEXITY,
types::VEC_BOX,
undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
unicode::INVISIBLE_CHARACTERS,
unicode::NON_ASCII_LITERAL,
unicode::UNICODE_NOT_NFC,
uninit_vec::UNINIT_VEC,
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
unit_types::LET_UNIT_VALUE,
unit_types::UNIT_ARG,
unit_types::UNIT_CMP,
unnamed_address::FN_ADDRESS_COMPARISONS,
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
unnecessary_wraps::UNNECESSARY_WRAPS,
unnested_or_patterns::UNNESTED_OR_PATTERNS,
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
unused_async::UNUSED_ASYNC,
unused_io_amount::UNUSED_IO_AMOUNT,
unused_peekable::UNUSED_PEEKABLE,
unused_rounding::UNUSED_ROUNDING,
unused_self::UNUSED_SELF,
unused_unit::UNUSED_UNIT,
unwrap::PANICKING_UNWRAP,
unwrap::UNNECESSARY_UNWRAP,
unwrap_in_result::UNWRAP_IN_RESULT,
upper_case_acronyms::UPPER_CASE_ACRONYMS,
use_self::USE_SELF,
useless_conversion::USELESS_CONVERSION,
vec::USELESS_VEC,
vec_init_then_push::VEC_INIT_THEN_PUSH,
wildcard_imports::ENUM_GLOB_USE,
wildcard_imports::WILDCARD_IMPORTS,
write::PRINTLN_EMPTY_STRING,
write::PRINT_LITERAL,
write::PRINT_STDERR,
write::PRINT_STDOUT,
write::PRINT_WITH_NEWLINE,
write::USE_DEBUG,
write::WRITELN_EMPTY_STRING,
write::WRITE_LITERAL,
write::WRITE_WITH_NEWLINE,
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
])

View file

@ -1,39 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(casts::AS_PTR_CAST_MUT),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(copies::BRANCHES_SHARING_CODE),
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
LintId::of(equatable_if_let::EQUATABLE_IF_LET),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
LintId::of(future_not_send::FUTURE_NOT_SEND),
LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
LintId::of(methods::ITER_ON_SINGLE_ITEMS),
LintId::of(methods::ITER_WITH_DRAIN),
LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(mutex_atomic::MUTEX_INTEGER),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
LintId::of(regex::TRIVIAL_REGEX),
LintId::of(strings::STRING_LIT_AS_BYTES),
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
LintId::of(unused_peekable::UNUSED_PEEKABLE),
LintId::of(unused_rounding::UNUSED_ROUNDING),
LintId::of(use_self::USE_SELF),
])

View file

@ -1,104 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(attrs::INLINE_ALWAYS),
LintId::of(casts::BORROW_AS_PTR),
LintId::of(casts::CAST_LOSSLESS),
LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
LintId::of(casts::CAST_POSSIBLE_WRAP),
LintId::of(casts::CAST_PRECISION_LOSS),
LintId::of(casts::CAST_PTR_ALIGNMENT),
LintId::of(casts::CAST_SIGN_LOSS),
LintId::of(casts::PTR_AS_PTR),
LintId::of(checked_conversions::CHECKED_CONVERSIONS),
LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
LintId::of(copy_iterator::COPY_ITERATOR),
LintId::of(default::DEFAULT_TRAIT_ACCESS),
LintId::of(dereference::EXPLICIT_DEREF_METHODS),
LintId::of(dereference::REF_BINDING_TO_REFERENCE),
LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
LintId::of(doc::DOC_LINK_WITH_QUOTES),
LintId::of(doc::DOC_MARKDOWN),
LintId::of(doc::MISSING_ERRORS_DOC),
LintId::of(doc::MISSING_PANICS_DOC),
LintId::of(empty_enum::EMPTY_ENUM),
LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
LintId::of(format_args::UNINLINED_FORMAT_ARGS),
LintId::of(functions::MUST_USE_CANDIDATE),
LintId::of(functions::TOO_MANY_LINES),
LintId::of(if_not_else::IF_NOT_ELSE),
LintId::of(implicit_hasher::IMPLICIT_HASHER),
LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
LintId::of(let_underscore::LET_UNDERSCORE_DROP),
LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
LintId::of(literal_representation::UNREADABLE_LITERAL),
LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
LintId::of(loops::EXPLICIT_ITER_LOOP),
LintId::of(macro_use::MACRO_USE_IMPORTS),
LintId::of(manual_assert::MANUAL_ASSERT),
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
LintId::of(manual_string_new::MANUAL_STRING_NEW),
LintId::of(matches::MATCH_BOOL),
LintId::of(matches::MATCH_ON_VEC_ITEMS),
LintId::of(matches::MATCH_SAME_ARMS),
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
LintId::of(matches::MATCH_WILD_ERR_ARM),
LintId::of(matches::SINGLE_MATCH_ELSE),
LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
LintId::of(methods::FILTER_MAP_NEXT),
LintId::of(methods::FLAT_MAP_OPTION),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MANUAL_OK_OR),
LintId::of(methods::MAP_UNWRAP_OR),
LintId::of(methods::NAIVE_BYTECOUNT),
LintId::of(methods::STABLE_SORT_PRIMITIVE),
LintId::of(methods::UNNECESSARY_JOIN),
LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
LintId::of(mut_mut::MUT_MUT),
LintId::of(needless_continue::NEEDLESS_CONTINUE),
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_expressive_names::SIMILAR_NAMES),
LintId::of(operators::FLOAT_CMP),
LintId::of(operators::NEEDLESS_BITWISE_BOOL),
LintId::of(operators::VERBOSE_BIT_MASK),
LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(ranges::RANGE_MINUS_ONE),
LintId::of(ranges::RANGE_PLUS_ONE),
LintId::of(redundant_else::REDUNDANT_ELSE),
LintId::of(ref_option_ref::REF_OPTION_REF),
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
LintId::of(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
LintId::of(types::LINKEDLIST),
LintId::of(types::OPTION_OPTION),
LintId::of(unicode::UNICODE_NOT_NFC),
LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
LintId::of(unused_async::UNUSED_ASYNC),
LintId::of(unused_self::UNUSED_SELF),
LintId::of(wildcard_imports::ENUM_GLOB_USE),
LintId::of(wildcard_imports::WILDCARD_IMPORTS),
LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
])

View file

@ -1,34 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
LintId::of(box_default::BOX_DEFAULT),
LintId::of(entry::MAP_ENTRY),
LintId::of(escape::BOXED_LOCAL),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
LintId::of(functions::RESULT_LARGE_ERR),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::MISSING_SPIN_LOOP),
LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(manual_retain::MANUAL_RETAIN),
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::ITER_NTH),
LintId::of(methods::ITER_OVEREAGER_CLONED),
LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::OR_FUN_CALL),
LintId::of(methods::SINGLE_CHAR_PATTERN),
LintId::of(methods::UNNECESSARY_TO_OWNED),
LintId::of(operators::CMP_OWNED),
LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
])

View file

@ -1,90 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(as_conversions::AS_CONVERSIONS),
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
LintId::of(casts::AS_UNDERSCORE),
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO),
LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(empty_drop::EMPTY_DROP),
LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
LintId::of(exit::EXIT),
LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
LintId::of(format_push_string::FORMAT_PUSH_STRING),
LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
LintId::of(implicit_return::IMPLICIT_RETURN),
LintId::of(indexing_slicing::INDEXING_SLICING),
LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
LintId::of(matches::TRY_ERR),
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
LintId::of(mem_forget::MEM_FORGET),
LintId::of(methods::CLONE_ON_REF_PTR),
LintId::of(methods::EXPECT_USED),
LintId::of(methods::FILETYPE_IS_FILE),
LintId::of(methods::GET_UNWRAP),
LintId::of(methods::MAP_ERR_IGNORE),
LintId::of(methods::UNWRAP_USED),
LintId::of(methods::VERBOSE_FILE_READS),
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS),
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
LintId::of(module_style::MOD_MODULE_FILES),
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
LintId::of(operators::ARITHMETIC_SIDE_EFFECTS),
LintId::of(operators::FLOAT_ARITHMETIC),
LintId::of(operators::FLOAT_CMP_CONST),
LintId::of(operators::INTEGER_ARITHMETIC),
LintId::of(operators::INTEGER_DIVISION),
LintId::of(operators::MODULO_ARITHMETIC),
LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
LintId::of(panic_unimplemented::PANIC),
LintId::of(panic_unimplemented::TODO),
LintId::of(panic_unimplemented::UNIMPLEMENTED),
LintId::of(panic_unimplemented::UNREACHABLE),
LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS),
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
LintId::of(pub_use::PUB_USE),
LintId::of(redundant_slicing::DEREF_BY_SLICING),
LintId::of(same_name_method::SAME_NAME_METHOD),
LintId::of(shadow::SHADOW_REUSE),
LintId::of(shadow::SHADOW_SAME),
LintId::of(shadow::SHADOW_UNRELATED),
LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE),
LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC),
LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE),
LintId::of(strings::STRING_ADD),
LintId::of(strings::STRING_SLICE),
LintId::of(strings::STRING_TO_STRING),
LintId::of(strings::STR_TO_STRING),
LintId::of(types::RC_BUFFER),
LintId::of(types::RC_MUTEX),
LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
LintId::of(unicode::NON_ASCII_LITERAL),
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
LintId::of(write::PRINT_STDERR),
LintId::of(write::PRINT_STDOUT),
LintId::of(write::USE_DEBUG),
])

View file

@ -1,131 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
LintId::of(collapsible_if::COLLAPSIBLE_IF),
LintId::of(comparison_chain::COMPARISON_CHAIN),
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
LintId::of(dereference::NEEDLESS_BORROW),
LintId::of(disallowed_macros::DISALLOWED_MACROS),
LintId::of(disallowed_methods::DISALLOWED_METHODS),
LintId::of(disallowed_names::DISALLOWED_NAMES),
LintId::of(disallowed_types::DISALLOWED_TYPES),
LintId::of(doc::MISSING_SAFETY_DOC),
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(enum_variants::ENUM_VARIANT_NAMES),
LintId::of(enum_variants::MODULE_INCEPTION),
LintId::of(eta_reduction::REDUNDANT_CLOSURE),
LintId::of(float_literal::EXCESSIVE_PRECISION),
LintId::of(from_over_into::FROM_OVER_INTO),
LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD),
LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(len_zero::LEN_ZERO),
LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(loops::FOR_KV_MAP),
LintId::of(loops::NEEDLESS_RANGE_LOOP),
LintId::of(loops::SAME_ITEM_PUSH),
LintId::of(loops::WHILE_LET_ON_ITERATOR),
LintId::of(main_recursion::MAIN_RECURSION),
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::COLLAPSIBLE_MATCH),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MANUAL_MAP),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(matches::MATCH_OVERLAPPING_ARM),
LintId::of(matches::MATCH_REF_PATS),
LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
LintId::of(matches::SINGLE_MATCH),
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::ERR_EXPECT),
LintId::of(methods::GET_FIRST),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_NEXT_SLICE),
LintId::of(methods::ITER_NTH_ZERO),
LintId::of(methods::ITER_SKIP_NEXT),
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_MAP_OR_NONE),
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
LintId::of(methods::SINGLE_CHAR_ADD_STR),
LintId::of(methods::STRING_EXTEND_CHARS),
LintId::of(methods::UNNECESSARY_FOLD),
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
LintId::of(methods::WRONG_SELF_CONVENTION),
LintId::of(misc::TOPLEVEL_REF_ARG),
LintId::of(misc::ZERO_PTR),
LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
LintId::of(misc_early::DOUBLE_NEG),
LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
LintId::of(misc_early::REDUNDANT_PATTERN),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
LintId::of(neg_multiply::NEG_MULTIPLY),
LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(operators::ASSIGN_OP_PATTERN),
LintId::of(operators::OP_REF),
LintId::of(operators::PTR_EQ),
LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
LintId::of(ptr::CMP_NULL),
LintId::of(ptr::PTR_ARG),
LintId::of(question_mark::QUESTION_MARK),
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(returns::LET_AND_RETURN),
LintId::of(returns::NEEDLESS_RETURN),
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(strings::TRIM_SPLIT_WHITESPACE),
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
LintId::of(unit_types::LET_UNIT_VALUE),
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(write::PRINTLN_EMPTY_STRING),
LintId::of(write::PRINT_LITERAL),
LintId::of(write::PRINT_WITH_NEWLINE),
LintId::of(write::WRITELN_EMPTY_STRING),
LintId::of(write::WRITE_LITERAL),
LintId::of(write::WRITE_WITH_NEWLINE),
])

View file

@ -1,38 +0,0 @@
// This file was generated by `cargo dev update_lints`.
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_NAN_TO_INT),
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(drop_forget_ref::DROP_NON_DROP),
LintId::of(drop_forget_ref::FORGET_NON_DROP),
LintId::of(duplicate_mod::DUPLICATE_MOD),
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(loops::EMPTY_LOOP),
LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::SUSPICIOUS_TO_OWNED),
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(operators::MISREFACTORED_ASSIGN_OP),
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
])

View file

@ -32,8 +32,8 @@ extern crate rustc_driver;
extern crate rustc_errors; extern crate rustc_errors;
extern crate rustc_hir; extern crate rustc_hir;
extern crate rustc_hir_analysis; extern crate rustc_hir_analysis;
extern crate rustc_hir_typeck;
extern crate rustc_hir_pretty; extern crate rustc_hir_pretty;
extern crate rustc_hir_typeck;
extern crate rustc_index; extern crate rustc_index;
extern crate rustc_infer; extern crate rustc_infer;
extern crate rustc_lexer; extern crate rustc_lexer;
@ -47,122 +47,24 @@ extern crate rustc_trait_selection;
#[macro_use] #[macro_use]
extern crate clippy_utils; extern crate clippy_utils;
#[macro_use]
extern crate declare_clippy_lint;
use std::io;
use std::path::PathBuf;
use clippy_utils::parse_msrv; use clippy_utils::parse_msrv;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_lint::LintId; use rustc_lint::{Lint, LintId};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::Session; use rustc_session::Session;
/// Macro used to declare a Clippy lint.
///
/// Every lint declaration consists of 4 parts:
///
/// 1. The documentation, which is used for the website
/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
/// 4. The `description` that contains a short explanation on what's wrong with code where the
/// lint is triggered.
///
/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
/// enabled by default. As said in the README.md of this repository, if the lint level mapping
/// changes, please update README.md.
///
/// # Example
///
/// ```
/// #![feature(rustc_private)]
/// extern crate rustc_session;
/// use rustc_session::declare_tool_lint;
/// use clippy_lints::declare_clippy_lint;
///
/// declare_clippy_lint! {
/// /// ### What it does
/// /// Checks for ... (describe what the lint matches).
/// ///
/// /// ### Why is this bad?
/// /// Supply the reason for linting the code.
/// ///
/// /// ### Example
/// /// ```rust
/// /// Insert a short example of code that triggers the lint
/// /// ```
/// ///
/// /// Use instead:
/// /// ```rust
/// /// Insert a short example of improved code that doesn't trigger the lint
/// /// ```
/// pub LINT_NAME,
/// pedantic,
/// "description"
/// }
/// ```
/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
#[macro_export]
macro_rules! declare_clippy_lint {
{ $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
}
};
}
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
pub mod deprecated_lints; pub mod deprecated_lints;
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils; mod utils;
mod declared_lints;
mod renamed_lints; mod renamed_lints;
// begin lints modules, do not remove this comment, its used in `update_lints` // begin lints modules, do not remove this comment, its used in `update_lints`
@ -231,6 +133,7 @@ mod format_impl;
mod format_push_string; mod format_push_string;
mod formatting; mod formatting;
mod from_over_into; mod from_over_into;
mod from_raw_with_void_ptr;
mod from_str_radix_10; mod from_str_radix_10;
mod functions; mod functions;
mod future_not_send; mod future_not_send;
@ -249,6 +152,7 @@ mod inherent_impl;
mod inherent_to_string; mod inherent_to_string;
mod init_numbered_fields; mod init_numbered_fields;
mod inline_fn_without_body; mod inline_fn_without_body;
mod instant_subtraction;
mod int_plus_one; mod int_plus_one;
mod invalid_upcast_comparisons; mod invalid_upcast_comparisons;
mod invalid_utf8_in_unchecked; mod invalid_utf8_in_unchecked;
@ -270,7 +174,8 @@ mod manual_assert;
mod manual_async_fn; mod manual_async_fn;
mod manual_bits; mod manual_bits;
mod manual_clamp; mod manual_clamp;
mod manual_instant_elapsed; mod manual_is_ascii_check;
mod manual_let_else;
mod manual_non_exhaustive; mod manual_non_exhaustive;
mod manual_rem_euclid; mod manual_rem_euclid;
mod manual_retain; mod manual_retain;
@ -365,6 +270,7 @@ mod strings;
mod strlen_on_c_strings; mod strlen_on_c_strings;
mod suspicious_operation_groupings; mod suspicious_operation_groupings;
mod suspicious_trait_impl; mod suspicious_trait_impl;
mod suspicious_xor_used_as_pow;
mod swap; mod swap;
mod swap_ptr_to_ref; mod swap_ptr_to_ref;
mod tabs_in_doc_comments; mod tabs_in_doc_comments;
@ -404,8 +310,8 @@ mod zero_div_zero;
mod zero_sized_map_values; mod zero_sized_map_values;
// end lints modules, do not remove this comment, its used in `update_lints` // end lints modules, do not remove this comment, its used in `update_lints`
pub use crate::utils::conf::Conf;
use crate::utils::conf::{format_error, TryConf}; use crate::utils::conf::{format_error, TryConf};
pub use crate::utils::conf::{lookup_conf_file, Conf};
/// Register all pre expansion lints /// Register all pre expansion lints
/// ///
@ -462,8 +368,8 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn read_conf(sess: &Session) -> Conf { pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf {
let file_name = match utils::conf::lookup_conf_file() { let file_name = match path {
Ok(Some(path)) => path, Ok(Some(path)) => path,
Ok(None) => return Conf::default(), Ok(None) => return Conf::default(),
Err(error) => { Err(error) => {
@ -473,7 +379,7 @@ pub fn read_conf(sess: &Session) -> Conf {
}, },
}; };
let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); let TryConf { conf, errors, warnings } = utils::conf::read(file_name);
// all conf errors are non-fatal, we just use the default conf in case of error // all conf errors are non-fatal, we just use the default conf in case of error
for error in errors { for error in errors {
sess.err(format!( sess.err(format!(
@ -495,31 +401,121 @@ pub fn read_conf(sess: &Session) -> Conf {
conf conf
} }
#[derive(Default)]
struct RegistrationGroups {
all: Vec<LintId>,
cargo: Vec<LintId>,
complexity: Vec<LintId>,
correctness: Vec<LintId>,
nursery: Vec<LintId>,
pedantic: Vec<LintId>,
perf: Vec<LintId>,
restriction: Vec<LintId>,
style: Vec<LintId>,
suspicious: Vec<LintId>,
#[cfg(feature = "internal")]
internal: Vec<LintId>,
}
impl RegistrationGroups {
#[rustfmt::skip]
fn register(self, store: &mut rustc_lint::LintStore) {
store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness);
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction);
store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
#[cfg(feature = "internal")]
store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal);
}
}
#[derive(Copy, Clone)]
pub(crate) enum LintCategory {
Cargo,
Complexity,
Correctness,
Nursery,
Pedantic,
Perf,
Restriction,
Style,
Suspicious,
#[cfg(feature = "internal")]
Internal,
}
#[allow(clippy::enum_glob_use)]
use LintCategory::*;
impl LintCategory {
fn is_all(self) -> bool {
matches!(self, Correctness | Suspicious | Style | Complexity | Perf)
}
fn group(self, groups: &mut RegistrationGroups) -> &mut Vec<LintId> {
match self {
Cargo => &mut groups.cargo,
Complexity => &mut groups.complexity,
Correctness => &mut groups.correctness,
Nursery => &mut groups.nursery,
Pedantic => &mut groups.pedantic,
Perf => &mut groups.perf,
Restriction => &mut groups.restriction,
Style => &mut groups.style,
Suspicious => &mut groups.suspicious,
#[cfg(feature = "internal")]
Internal => &mut groups.internal,
}
}
}
pub(crate) struct LintInfo {
/// Double reference to maintain pointer equality
lint: &'static &'static Lint,
category: LintCategory,
explanation: &'static str,
}
pub fn explain(name: &str) {
let target = format!("clippy::{}", name.to_ascii_uppercase());
match declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
Some(info) => print!("{}", info.explanation),
None => println!("unknown lint: {name}"),
}
}
fn register_categories(store: &mut rustc_lint::LintStore) {
let mut groups = RegistrationGroups::default();
for LintInfo { lint, category, .. } in declared_lints::LINTS {
if category.is_all() {
groups.all.push(LintId::of(lint));
}
category.group(&mut groups).push(LintId::of(lint));
}
let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect();
store.register_lints(&lints);
groups.register(store);
}
/// Register all lints and lint groups with the rustc plugin registry /// Register all lints and lint groups with the rustc plugin registry
/// ///
/// Used in `./src/driver.rs`. /// Used in `./src/driver.rs`.
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
register_removed_non_tool_lints(store); register_removed_non_tool_lints(store);
register_categories(store);
include!("lib.deprecated.rs"); include!("lib.deprecated.rs");
include!("lib.register_lints.rs");
include!("lib.register_restriction.rs");
include!("lib.register_pedantic.rs");
#[cfg(feature = "internal")]
include!("lib.register_internal.rs");
include!("lib.register_all.rs");
include!("lib.register_style.rs");
include!("lib.register_complexity.rs");
include!("lib.register_correctness.rs");
include!("lib.register_suspicious.rs");
include!("lib.register_perf.rs");
include!("lib.register_cargo.rs");
include!("lib.register_nursery.rs");
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
{ {
if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
@ -614,6 +610,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
)) ))
}); });
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv))); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
let matches_for_let_else = conf.matches_for_let_else;
store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else)));
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
@ -735,7 +733,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
let max_trait_bounds = conf.max_trait_bounds; let max_trait_bounds = conf.max_trait_bounds;
store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds))); store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
store.register_late_pass(|_| Box::new(mut_key::MutableKeyType)); let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(reference::DerefAddrOf));
store.register_early_pass(|| Box::new(double_parens::DoubleParens)); store.register_early_pass(|| Box::new(double_parens::DoubleParens));
store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new())); store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new()));
@ -794,10 +793,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
store.register_early_pass(|| Box::new(as_conversions::AsConversions)); store.register_early_pass(|| Box::new(as_conversions::AsConversions));
store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default());
let max_fn_params_bools = conf.max_fn_params_bools; let max_fn_params_bools = conf.max_fn_params_bools;
let max_struct_bools = conf.max_struct_bools; let max_struct_bools = conf.max_struct_bools;
store.register_early_pass(move || { store.register_late_pass(move |_| {
Box::new(excessive_bools::ExcessiveBools::new( Box::new(excessive_bools::ExcessiveBools::new(
max_struct_bools, max_struct_bools,
max_fn_params_bools, max_fn_params_bools,
@ -879,13 +878,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()); store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
let allow_dbg_in_tests = conf.allow_dbg_in_tests; let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let allow_print_in_tests = conf.allow_print_in_tests;
store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish; let cargo_ignore_publish = conf.cargo_ignore_publish;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(cargo::Cargo { Box::new(cargo::Cargo {
ignore_publish: cargo_ignore_publish, ignore_publish: cargo_ignore_publish,
}) })
}); });
store.register_late_pass(|_| Box::<write::Write>::default());
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
@ -908,7 +908,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
@ -919,6 +919,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd));
store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields));
store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
// add lints here, do not remove this comment, it's used in `new_lint` // add lints here, do not remove this comment, it's used in `new_lint`
} }

View file

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::trait_ref_of_method; use clippy_utils::trait_ref_of_method;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
@ -152,6 +152,7 @@ fn check_fn_inner<'tcx>(
.params .params
.iter() .iter()
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
for typ in types { for typ in types {
for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
if pred.origin == PredicateOrigin::WhereClause { if pred.origin == PredicateOrigin::WhereClause {
@ -188,15 +189,30 @@ fn check_fn_inner<'tcx>(
} }
} }
} }
if could_use_elision(cx, decl, body, trait_sig, generics.params) {
span_lint( if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) {
let lts = elidable_lts
.iter()
// In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
// `Node::GenericParam`.
.filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident())
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ");
span_lint_and_then(
cx, cx,
NEEDLESS_LIFETIMES, NEEDLESS_LIFETIMES,
span.with_hi(decl.output.span().hi()), span.with_hi(decl.output.span().hi()),
"explicit lifetimes given in parameter types where they could be elided \ &format!("the following explicit lifetimes could be elided: {lts}"),
(or replaced with `'_` if needed by type declaration)", |diag| {
if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) {
diag.span_help(span, "replace with `'_` in generic arguments such as here");
}
},
); );
} }
if report_extra_lifetimes { if report_extra_lifetimes {
self::report_extra_lifetimes(cx, decl, generics); self::report_extra_lifetimes(cx, decl, generics);
} }
@ -227,7 +243,7 @@ fn could_use_elision<'tcx>(
body: Option<BodyId>, body: Option<BodyId>,
trait_sig: Option<&[Ident]>, trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>], named_generics: &'tcx [GenericParam<'_>],
) -> bool { ) -> Option<Vec<(LocalDefId, Option<Span>)>> {
// There are two scenarios where elision works: // There are two scenarios where elision works:
// * no output references, all input references have different LT // * no output references, all input references have different LT
// * output references, exactly one input reference with same LT // * output references, exactly one input reference with same LT
@ -254,7 +270,7 @@ fn could_use_elision<'tcx>(
} }
if input_visitor.abort() || output_visitor.abort() { if input_visitor.abort() || output_visitor.abort() {
return false; return None;
} }
let input_lts = input_visitor.lts; let input_lts = input_visitor.lts;
@ -262,7 +278,7 @@ fn could_use_elision<'tcx>(
if let Some(trait_sig) = trait_sig { if let Some(trait_sig) = trait_sig {
if explicit_self_type(cx, func, trait_sig.first().copied()) { if explicit_self_type(cx, func, trait_sig.first().copied()) {
return false; return None;
} }
} }
@ -271,7 +287,7 @@ fn could_use_elision<'tcx>(
let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
if explicit_self_type(cx, func, first_ident) { if explicit_self_type(cx, func, first_ident) {
return false; return None;
} }
let mut checker = BodyLifetimeChecker { let mut checker = BodyLifetimeChecker {
@ -279,14 +295,14 @@ fn could_use_elision<'tcx>(
}; };
checker.visit_expr(body.value); checker.visit_expr(body.value);
if checker.lifetimes_used_in_body { if checker.lifetimes_used_in_body {
return false; return None;
} }
} }
// check for lifetimes from higher scopes // check for lifetimes from higher scopes
for lt in input_lts.iter().chain(output_lts.iter()) { for lt in input_lts.iter().chain(output_lts.iter()) {
if !allowed_lts.contains(lt) { if !allowed_lts.contains(lt) {
return false; return None;
} }
} }
@ -302,48 +318,45 @@ fn could_use_elision<'tcx>(
for lt in input_visitor.nested_elision_site_lts { for lt in input_visitor.nested_elision_site_lts {
if let RefLt::Named(def_id) = lt { if let RefLt::Named(def_id) = lt {
if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
return false; return None;
} }
} }
} }
for lt in output_visitor.nested_elision_site_lts { for lt in output_visitor.nested_elision_site_lts {
if let RefLt::Named(def_id) = lt { if let RefLt::Named(def_id) = lt {
if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
return false; return None;
} }
} }
} }
} }
// no input lifetimes? easy case! // A lifetime can be newly elided if:
if input_lts.is_empty() { // - It occurs only once among the inputs.
false // - If there are multiple input lifetimes, then the newly elided lifetime does not occur among the
} else if output_lts.is_empty() { // outputs (because eliding such an lifetime would create an ambiguity).
// no output lifetimes, check distinctness of input lifetimes let elidable_lts = named_lifetime_occurrences(&input_lts)
.into_iter()
// only unnamed and static, ok .filter_map(|(def_id, occurrences)| {
let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static); if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) {
if unnamed_and_static { Some((
return false; def_id,
} input_visitor
// we have no output reference, so we only need all distinct lifetimes .lifetime_generic_arg_spans
input_lts.len() == unique_lifetimes(&input_lts) .get(&def_id)
} else { .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id))
// we have output references, so we need one input reference, .copied(),
// and all output lifetimes must be the same ))
if unique_lifetimes(&output_lts) > 1 { } else {
return false; None
}
if input_lts.len() == 1 {
match (&input_lts[0], &output_lts[0]) {
(&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
(&RefLt::Named(_), &RefLt::Unnamed) => true,
_ => false, /* already elided, different named lifetimes
* or something static going on */
} }
} else { })
false .collect::<Vec<_>>();
}
if elidable_lts.is_empty() {
None
} else {
Some(elidable_lts)
} }
} }
@ -359,16 +372,31 @@ fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxH
allowed_lts allowed_lts
} }
/// Number of unique lifetimes in the given vector. /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve
/// relative order.
#[must_use] #[must_use]
fn unique_lifetimes(lts: &[RefLt]) -> usize { fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> {
lts.iter().collect::<FxHashSet<_>>().len() let mut occurrences = Vec::new();
for lt in lts {
if let &RefLt::Named(curr_def_id) = lt {
if let Some(pair) = occurrences
.iter_mut()
.find(|(prev_def_id, _)| *prev_def_id == curr_def_id)
{
pair.1 += 1;
} else {
occurrences.push((curr_def_id, 1));
}
}
}
occurrences
} }
/// A visitor usable for `rustc_front::visit::walk_ty()`. /// A visitor usable for `rustc_front::visit::walk_ty()`.
struct RefVisitor<'a, 'tcx> { struct RefVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
lts: Vec<RefLt>, lts: Vec<RefLt>,
lifetime_generic_arg_spans: FxHashMap<LocalDefId, Span>,
nested_elision_site_lts: Vec<RefLt>, nested_elision_site_lts: Vec<RefLt>,
unelided_trait_object_lifetime: bool, unelided_trait_object_lifetime: bool,
} }
@ -378,6 +406,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
Self { Self {
cx, cx,
lts: Vec::new(), lts: Vec::new(),
lifetime_generic_arg_spans: FxHashMap::default(),
nested_elision_site_lts: Vec::new(), nested_elision_site_lts: Vec::new(),
unelided_trait_object_lifetime: false, unelided_trait_object_lifetime: false,
} }
@ -467,6 +496,22 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
_ => walk_ty(self, ty), _ => walk_ty(self, ty),
} }
} }
fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) {
if let GenericArg::Lifetime(l) = generic_arg
&& let LifetimeName::Param(def_id, _) = l.name
{
self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.span);
}
// Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands.
// walk_generic_arg(self, generic_arg);
match generic_arg {
GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
GenericArg::Type(ty) => self.visit_ty(ty),
GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
GenericArg::Infer(inf) => self.visit_infer(inf),
}
}
} }
/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to

View file

@ -9,7 +9,6 @@ mod manual_flatten;
mod manual_memcpy; mod manual_memcpy;
mod missing_spin_loop; mod missing_spin_loop;
mod mut_range_bound; mod mut_range_bound;
mod needless_collect;
mod needless_range_loop; mod needless_range_loop;
mod never_loop; mod never_loop;
mod same_item_push; mod same_item_push;
@ -205,28 +204,6 @@ declare_clippy_lint! {
"`loop { if let { ... } else break }`, which can be written as a `while let` loop" "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for functions collecting an iterator when collect
/// is not needed.
///
/// ### Why is this bad?
/// `collect` causes the allocation of a new data structure,
/// when this allocation may not be needed.
///
/// ### Example
/// ```rust
/// # let iterator = vec![1].into_iter();
/// let len = iterator.clone().collect::<Vec<_>>().len();
/// // should be
/// let len = iterator.count();
/// ```
#[clippy::version = "1.30.0"]
pub NEEDLESS_COLLECT,
perf,
"collecting an iterator when collect is not needed"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks `for` loops over slices with an explicit counter /// Checks `for` loops over slices with an explicit counter
@ -605,7 +582,6 @@ declare_lint_pass!(Loops => [
EXPLICIT_INTO_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP,
ITER_NEXT_LOOP, ITER_NEXT_LOOP,
WHILE_LET_LOOP, WHILE_LET_LOOP,
NEEDLESS_COLLECT,
EXPLICIT_COUNTER_LOOP, EXPLICIT_COUNTER_LOOP,
EMPTY_LOOP, EMPTY_LOOP,
WHILE_LET_ON_ITERATOR, WHILE_LET_ON_ITERATOR,
@ -667,8 +643,6 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
while_immutable_condition::check(cx, condition, body); while_immutable_condition::check(cx, condition, body);
missing_spin_loop::check(cx, condition, body); missing_spin_loop::check(cx, condition, body);
} }
needless_collect::check(expr, cx);
} }
} }

View file

@ -52,8 +52,8 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId>
None None
} }
fn check_for_mutation<'tcx>( fn check_for_mutation(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
body: &Expr<'_>, body: &Expr<'_>,
bound_id_start: Option<HirId>, bound_id_start: Option<HirId>,
bound_id_end: Option<HirId>, bound_id_end: Option<HirId>,
@ -113,13 +113,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
} }
} }
fn fake_read( fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
&mut self,
_: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
_: FakeReadCause,
_: HirId,
) {
}
} }
impl MutatePairDelegate<'_, '_> { impl MutatePairDelegate<'_, '_> {

View file

@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::ForLoop; use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::Span; use rustc_span::Span;
use std::iter::{once, Iterator}; use std::iter::{once, Iterator};
@ -16,7 +16,7 @@ pub(super) fn check(
span: Span, span: Span,
for_loop: Option<&ForLoop<'_>>, for_loop: Option<&ForLoop<'_>>,
) { ) {
match never_loop_block(block, loop_id) { match never_loop_block(block, &mut Vec::new(), loop_id) {
NeverLoopResult::AlwaysBreak => { NeverLoopResult::AlwaysBreak => {
span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
if let Some(ForLoop { if let Some(ForLoop {
@ -92,35 +92,34 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
} }
} }
fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
let mut iter = block let iter = block
.stmts .stmts
.iter() .iter()
.filter_map(stmt_to_expr) .filter_map(stmt_to_expr)
.chain(block.expr.map(|expr| (expr, None))); .chain(block.expr.map(|expr| (expr, None)));
never_loop_expr_seq(&mut iter, main_loop_id)
}
fn never_loop_expr_seq<'a, T: Iterator<Item = (&'a Expr<'a>, Option<&'a Block<'a>>)>>( iter.map(|(e, els)| {
es: &mut T, let e = never_loop_expr(e, ignore_ids, main_loop_id);
main_loop_id: HirId, // els is an else block in a let...else binding
) -> NeverLoopResult { els.map_or(e, |els| {
es.map(|(e, els)| { combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id))
let e = never_loop_expr(e, main_loop_id); })
els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id)))
}) })
.fold(NeverLoopResult::Otherwise, combine_seq) .fold(NeverLoopResult::Otherwise, combine_seq)
} }
fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> {
match stmt.kind { match stmt.kind {
StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)),
// add the let...else expression (if present)
StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Local(local) => local.init.map(|init| (init, local.els)),
StmtKind::Item(..) => None, StmtKind::Item(..) => None,
} }
} }
fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { #[allow(clippy::too_many_lines)]
fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
match expr.kind { match expr.kind {
ExprKind::Box(e) ExprKind::Box(e)
| ExprKind::Unary(_, e) | ExprKind::Unary(_, e)
@ -129,47 +128,56 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
| ExprKind::Field(e, _) | ExprKind::Field(e, _)
| ExprKind::AddrOf(_, _, e) | ExprKind::AddrOf(_, _, e)
| ExprKind::Repeat(e, _) | ExprKind::Repeat(e, _)
| ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id),
ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id), ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id),
ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id), ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id),
ExprKind::MethodCall(_, receiver, es, _) => { ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id) &mut std::iter::once(receiver).chain(es.iter()),
}, ignore_ids,
main_loop_id,
),
ExprKind::Struct(_, fields, base) => { ExprKind::Struct(_, fields, base) => {
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id); let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
if let Some(base) = base { if let Some(base) = base {
combine_both(fields, never_loop_expr(base, main_loop_id)) combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id))
} else { } else {
fields fields
} }
}, },
ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
ExprKind::Binary(_, e1, e2) ExprKind::Binary(_, e1, e2)
| ExprKind::Assign(e1, e2, _) | ExprKind::Assign(e1, e2, _)
| ExprKind::AssignOp(_, e1, e2) | ExprKind::AssignOp(_, e1, e2)
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
ExprKind::Loop(b, _, _, _) => { ExprKind::Loop(b, _, _, _) => {
// Break can come from the inner loop so remove them. // Break can come from the inner loop so remove them.
absorb_break(never_loop_block(b, main_loop_id)) absorb_break(never_loop_block(b, ignore_ids, main_loop_id))
}, },
ExprKind::If(e, e2, e3) => { ExprKind::If(e, e2, e3) => {
let e1 = never_loop_expr(e, main_loop_id); let e1 = never_loop_expr(e, ignore_ids, main_loop_id);
let e2 = never_loop_expr(e2, main_loop_id); let e2 = never_loop_expr(e2, ignore_ids, main_loop_id);
let e3 = e3 let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
.as_ref() never_loop_expr(e, ignore_ids, main_loop_id)
.map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); });
combine_seq(e1, combine_branches(e2, e3)) combine_seq(e1, combine_branches(e2, e3))
}, },
ExprKind::Match(e, arms, _) => { ExprKind::Match(e, arms, _) => {
let e = never_loop_expr(e, main_loop_id); let e = never_loop_expr(e, ignore_ids, main_loop_id);
if arms.is_empty() { if arms.is_empty() {
e e
} else { } else {
let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
combine_seq(e, arms) combine_seq(e, arms)
} }
}, },
ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), ExprKind::Block(b, l) => {
if l.is_some() {
ignore_ids.push(b.hir_id);
}
let ret = never_loop_block(b, ignore_ids, main_loop_id);
ignore_ids.pop();
ret
},
ExprKind::Continue(d) => { ExprKind::Continue(d) => {
let id = d let id = d
.target_id .target_id
@ -180,20 +188,32 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
NeverLoopResult::AlwaysBreak NeverLoopResult::AlwaysBreak
} }
}, },
// checks if break targets a block instead of a loop
ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
.map_or(NeverLoopResult::Otherwise, |e| {
combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise)
}),
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) combine_seq(
never_loop_expr(e, ignore_ids, main_loop_id),
NeverLoopResult::AlwaysBreak,
)
}), }),
ExprKind::InlineAsm(asm) => asm ExprKind::InlineAsm(asm) => asm
.operands .operands
.iter() .iter()
.map(|(o, _)| match o { .map(|(o, _)| match o {
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
never_loop_expr(expr, main_loop_id) never_loop_expr(expr, ignore_ids, main_loop_id)
}, },
InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id), InlineAsmOperand::Out { expr, .. } => {
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id)
never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id)
}, },
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
&mut once(*in_expr).chain(out_expr.iter().copied()),
ignore_ids,
main_loop_id,
),
InlineAsmOperand::Const { .. } InlineAsmOperand::Const { .. }
| InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymFn { .. }
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
@ -208,13 +228,21 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
} }
} }
fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
es.map(|e| never_loop_expr(e, main_loop_id)) es: &mut T,
ignore_ids: &mut Vec<HirId>,
main_loop_id: HirId,
) -> NeverLoopResult {
es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
.fold(NeverLoopResult::Otherwise, combine_both) .fold(NeverLoopResult::Otherwise, combine_both)
} }
fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
e.map(|e| never_loop_expr(e, main_loop_id)) e: &mut T,
ignore_ids: &mut Vec<HirId>,
main_loop_id: HirId,
) -> NeverLoopResult {
e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
.fold(NeverLoopResult::AlwaysBreak, combine_branches) .fold(NeverLoopResult::AlwaysBreak, combine_branches)
} }

View file

@ -1,69 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
declare_clippy_lint! {
/// ### What it does
/// Lints subtraction between `Instant::now()` and another `Instant`.
///
/// ### Why is this bad?
/// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
/// as `Instant` subtraction saturates.
///
/// `prev_instant.elapsed()` also more clearly signals intention.
///
/// ### Example
/// ```rust
/// use std::time::Instant;
/// let prev_instant = Instant::now();
/// let duration = Instant::now() - prev_instant;
/// ```
/// Use instead:
/// ```rust
/// use std::time::Instant;
/// let prev_instant = Instant::now();
/// let duration = prev_instant.elapsed();
/// ```
#[clippy::version = "1.64.0"]
pub MANUAL_INSTANT_ELAPSED,
pedantic,
"subtraction between `Instant::now()` and previous `Instant`"
}
declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]);
impl LateLintPass<'_> for ManualInstantElapsed {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
&& check_instant_now_call(cx, lhs)
&& let ty_resolved = cx.typeck_results().expr_ty(rhs)
&& let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
&& clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
&& let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
{
span_lint_and_sugg(
cx,
MANUAL_INSTANT_ELAPSED,
expr.span,
"manual implementation of `Instant::elapsed`",
"try",
format!("{}.elapsed()", sugg.maybe_par()),
Applicability::MachineApplicable,
);
}
}
}
fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
if let ExprKind::Call(fn_expr, []) = expr_block.kind
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
&& clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
{
true
} else {
false
}
}

View file

@ -0,0 +1,158 @@
use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{def_id::DefId, sym};
use clippy_utils::{
diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet,
};
declare_clippy_lint! {
/// ### What it does
/// Suggests to use dedicated built-in methods,
/// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range
///
/// ### Why is this bad?
/// Using the built-in functions is more readable and makes it
/// clear that it's not a specific subset of characters, but all
/// ASCII (lowercase|uppercase|digit) characters.
/// ### Example
/// ```rust
/// fn main() {
/// assert!(matches!('x', 'a'..='z'));
/// assert!(matches!(b'X', b'A'..=b'Z'));
/// assert!(matches!('2', '0'..='9'));
/// assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
/// }
/// ```
/// Use instead:
/// ```rust
/// fn main() {
/// assert!('x'.is_ascii_lowercase());
/// assert!(b'X'.is_ascii_uppercase());
/// assert!('2'.is_ascii_digit());
/// assert!('x'.is_ascii_alphabetic());
/// }
/// ```
#[clippy::version = "1.66.0"]
pub MANUAL_IS_ASCII_CHECK,
style,
"use dedicated method to check ascii range"
}
impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
pub struct ManualIsAsciiCheck {
msrv: Option<RustcVersion>,
}
impl ManualIsAsciiCheck {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
#[derive(Debug, PartialEq)]
enum CharRange {
/// 'a'..='z' | b'a'..=b'z'
LowerChar,
/// 'A'..='Z' | b'A'..=b'Z'
UpperChar,
/// AsciiLower | AsciiUpper
FullChar,
/// '0..=9'
Digit,
Otherwise,
}
impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) {
return;
}
if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) {
return;
}
let Some(macro_call) = root_macro_call(expr.span) else { return };
if is_matches_macro(cx, macro_call.def_id) {
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
let range = check_pat(&arm.pat.kind);
if let Some(sugg) = match range {
CharRange::UpperChar => Some("is_ascii_uppercase"),
CharRange::LowerChar => Some("is_ascii_lowercase"),
CharRange::FullChar => Some("is_ascii_alphabetic"),
CharRange::Digit => Some("is_ascii_digit"),
CharRange::Otherwise => None,
} {
let default_snip = "..";
// `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
// macro span, so we check applicability manually by comparing `recv` is not default.
let recv = snippet(cx, recv.span, default_snip);
let applicability = if recv == default_snip {
Applicability::HasPlaceholders
} else {
Applicability::MachineApplicable
};
span_lint_and_sugg(
cx,
MANUAL_IS_ASCII_CHECK,
macro_call.span,
"manual check for common ascii range",
"try",
format!("{recv}.{sugg}()"),
applicability,
);
}
}
}
}
extract_msrv_attr!(LateContext);
}
fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
match pat_kind {
PatKind::Or(pats) => {
let ranges = pats.iter().map(|p| check_pat(&p.kind)).collect::<Vec<_>>();
if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) {
CharRange::FullChar
} else {
CharRange::Otherwise
}
},
PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end),
_ => CharRange::Otherwise,
}
}
fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
if let ExprKind::Lit(start_lit) = &start.kind
&& let ExprKind::Lit(end_lit) = &end.kind {
match (&start_lit.node, &end_lit.node) {
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
(Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit,
_ => CharRange::Otherwise,
}
} else {
CharRange::Otherwise
}
}
fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
return sym::matches_macro == name;
}
false
}

View file

@ -0,0 +1,297 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::{meets_msrv, msrvs, peel_blocks};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::sym;
use rustc_span::Span;
use serde::Deserialize;
use std::ops::ControlFlow;
declare_clippy_lint! {
/// ### What it does
///
/// Warn of cases where `let...else` could be used
///
/// ### Why is this bad?
///
/// `let...else` provides a standard construct for this pattern
/// that people can easily recognize. It's also more compact.
///
/// ### Example
///
/// ```rust
/// # let w = Some(0);
/// let v = if let Some(v) = w { v } else { return };
/// ```
///
/// Could be written:
///
/// ```rust
/// # #![feature(let_else)]
/// # fn main () {
/// # let w = Some(0);
/// let Some(v) = w else { return };
/// # }
/// ```
#[clippy::version = "1.67.0"]
pub MANUAL_LET_ELSE,
pedantic,
"manual implementation of a let...else statement"
}
pub struct ManualLetElse {
msrv: Option<RustcVersion>,
matches_behaviour: MatchLintBehaviour,
}
impl ManualLetElse {
#[must_use]
pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self {
Self {
msrv,
matches_behaviour,
}
}
}
impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]);
impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
let if_let_or_match = if_chain! {
if meets_msrv(self.msrv, msrvs::LET_ELSE);
if !in_external_macro(cx.sess(), stmt.span);
if let StmtKind::Local(local) = stmt.kind;
if let Some(init) = local.init;
if local.els.is_none();
if local.ty.is_none();
if init.span.ctxt() == stmt.span.ctxt();
if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init);
then {
if_let_or_match
} else {
return;
}
};
match if_let_or_match {
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! {
if expr_is_simple_identity(let_pat, if_then);
if let Some(if_else) = if_else;
if expr_diverges(cx, if_else);
then {
emit_manual_let_else(cx, stmt.span, if_let_expr, let_pat, if_else);
}
},
IfLetOrMatch::Match(match_expr, arms, source) => {
if self.matches_behaviour == MatchLintBehaviour::Never {
return;
}
if source != MatchSource::Normal {
return;
}
// Any other number than two arms doesn't (neccessarily)
// have a trivial mapping to let else.
if arms.len() != 2 {
return;
}
// Guards don't give us an easy mapping either
if arms.iter().any(|arm| arm.guard.is_some()) {
return;
}
let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
let diverging_arm_opt = arms
.iter()
.enumerate()
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
let Some((idx, diverging_arm)) = diverging_arm_opt else { return; };
let pat_arm = &arms[1 - idx];
if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) {
return;
}
emit_manual_let_else(cx, stmt.span, match_expr, pat_arm.pat, diverging_arm.body);
},
}
}
extract_msrv_attr!(LateContext);
}
fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: &Pat<'_>, else_body: &Expr<'_>) {
span_lint_and_then(
cx,
MANUAL_LET_ELSE,
span,
"this could be rewritten as `let...else`",
|diag| {
// This is far from perfect, for example there needs to be:
// * mut additions for the bindings
// * renamings of the bindings
// * unused binding collision detection with existing ones
// * putting patterns with at the top level | inside ()
// for this to be machine applicable.
let app = Applicability::HasPlaceholders;
if let Some(sn_pat) = snippet_opt(cx, pat.span) &&
let Some(sn_expr) = snippet_opt(cx, expr.span) &&
let Some(sn_else) = snippet_opt(cx, else_body.span)
{
let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
sn_else
} else {
format!("{{ {sn_else} }}")
};
let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
diag.span_suggestion(span, "consider writing", sugg, app);
}
},
);
}
fn expr_diverges(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) {
return ty.is_never();
}
false
}
// We can't just call is_never on expr and be done, because the type system
// sometimes coerces the ! type to something different before we can get
// our hands on it. So instead, we do a manual search. We do fall back to
// is_never in some places when there is no better alternative.
for_each_expr(expr, |ex| {
match ex.kind {
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()),
ExprKind::Call(call, _) => {
if is_never(cx, ex) || is_never(cx, call) {
return ControlFlow::Break(());
}
ControlFlow::Continue(Descend::Yes)
},
ExprKind::MethodCall(..) => {
if is_never(cx, ex) {
return ControlFlow::Break(());
}
ControlFlow::Continue(Descend::Yes)
},
ExprKind::If(if_expr, if_then, if_else) => {
let else_diverges = if_else.map_or(false, |ex| expr_diverges(cx, ex));
let diverges = expr_diverges(cx, if_expr) || (else_diverges && expr_diverges(cx, if_then));
if diverges {
return ControlFlow::Break(());
}
ControlFlow::Continue(Descend::No)
},
ExprKind::Match(match_expr, match_arms, _) => {
let diverges = expr_diverges(cx, match_expr)
|| match_arms.iter().all(|arm| {
let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(cx, g.body()));
guard_diverges || expr_diverges(cx, arm.body)
});
if diverges {
return ControlFlow::Break(());
}
ControlFlow::Continue(Descend::No)
},
// Don't continue into loops or labeled blocks, as they are breakable,
// and we'd have to start checking labels.
ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No),
// Default: descend
_ => ControlFlow::Continue(Descend::Yes),
}
})
.is_some()
}
fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool {
// Check whether the pattern contains any bindings, as the
// binding might potentially be used in the body.
// TODO: only look for *used* bindings.
let mut has_bindings = false;
pat.each_binding_or_first(&mut |_, _, _, _| has_bindings = true);
if has_bindings {
return false;
}
// If we shouldn't check the types, exit early.
if !check_types {
return true;
}
// Check whether any possibly "unknown" patterns are included,
// because users might not know which values some enum has.
// Well-known enums are excepted, as we assume people know them.
// We do a deep check, to be able to disallow Err(En::Foo(_))
// for usage of the En::Foo variant, as we disallow En::Foo(_),
// but we allow Err(_).
let typeck_results = cx.typeck_results();
let mut has_disallowed = false;
pat.walk_always(|pat| {
// Only do the check if the type is "spelled out" in the pattern
if !matches!(
pat.kind,
PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..)
) {
return;
};
let ty = typeck_results.pat_ty(pat);
// Option and Result are allowed, everything else isn't.
if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) {
has_disallowed = true;
}
});
!has_disallowed
}
/// Checks if the passed block is a simple identity referring to bindings created by the pattern
fn expr_is_simple_identity(pat: &'_ Pat<'_>, expr: &'_ Expr<'_>) -> bool {
// We support patterns with multiple bindings and tuples, like:
// let ... = if let (Some(foo), bar) = g() { (foo, bar) } else { ... }
let peeled = peel_blocks(expr);
let paths = match peeled.kind {
ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs,
ExprKind::Path(_) => std::slice::from_ref(peeled),
_ => return false,
};
let mut pat_bindings = FxHashSet::default();
pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| {
pat_bindings.insert(ident);
});
if pat_bindings.len() < paths.len() {
return false;
}
for path in paths {
if_chain! {
if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind;
if let [path_seg] = path.segments;
then {
if !pat_bindings.remove(&path_seg.ident) {
return false;
}
} else {
return false;
}
}
}
true
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)]
pub enum MatchLintBehaviour {
AllTypes,
WellKnownTypes,
Never,
}

View file

@ -119,7 +119,7 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
/// semicolons, which causes problems when generating a suggestion. Given an /// semicolons, which causes problems when generating a suggestion. Given an
/// expression that evaluates to '()' or '!', recursively remove useless braces /// expression that evaluates to '()' or '!', recursively remove useless braces
/// and semi-colons until is suitable for including in the suggestion template /// and semi-colons until is suitable for including in the suggestion template
fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<Span> { fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Span> {
if !is_unit_expression(cx, expr) { if !is_unit_expression(cx, expr) {
return None; return None;
} }

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath}; use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use super::INFALLIBLE_DESTRUCTURING_MATCH; use super::INFALLIBLE_DESTRUCTURING_MATCH;
@ -16,7 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
if let PatKind::TupleStruct( if let PatKind::TupleStruct(
QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
if args.len() == 1; if args.len() == 1;
if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind;
let body = peel_blocks(arms[0].body); let body = peel_blocks(arms[0].body);
if path_to_local_id(body, arg); if path_to_local_id(body, arg);
@ -30,8 +30,9 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
Consider using `let`", Consider using `let`",
"try this", "try this",
format!( format!(
"let {}({}) = {};", "let {}({}{}) = {};",
snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
if binding.0 == ByRef::Yes { "ref " } else { "" },
snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
snippet_with_applicability(cx, target.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability),
), ),

View file

@ -62,7 +62,7 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
// <expr> // <expr>
// } // }
// Returns true if <expr> resolves to `Some(x)`, `false` otherwise // Returns true if <expr> resolves to `Some(x)`, `false` otherwise
fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool {
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
// there can be not statements in the block as they would be removed when switching to `.filter` // there can be not statements in the block as they would be removed when switching to `.filter`
if let ExprKind::Call(callee, [arg]) = inner_expr.kind { if let ExprKind::Call(callee, [arg]) = inner_expr.kind {

View file

@ -83,8 +83,8 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t
/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that /// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that
/// may have a surprising lifetime. /// may have a surprising lifetime.
fn has_significant_drop_in_scrutinee<'tcx, 'a>( fn has_significant_drop_in_scrutinee<'tcx>(
cx: &'a LateContext<'tcx>, cx: &LateContext<'tcx>,
scrutinee: &'tcx Expr<'tcx>, scrutinee: &'tcx Expr<'tcx>,
source: MatchSource, source: MatchSource,
) -> Option<(Vec<FoundSigDrop>, &'static str)> { ) -> Option<(Vec<FoundSigDrop>, &'static str)> {
@ -226,7 +226,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
/// This will try to set the current suggestion (so it can be moved into the suggestions vec /// This will try to set the current suggestion (so it can be moved into the suggestions vec
/// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
/// an opportunity to look for another type in the chain that will be trivially copyable. /// an opportunity to look for another type in the chain that will be trivially copyable.
/// However, if we are at the the end of the chain, we want to accept whatever is there. (The /// However, if we are at the end of the chain, we want to accept whatever is there. (The
/// suggestion won't actually be output, but the diagnostic message will be output, so the user /// suggestion won't actually be output, but the diagnostic message will be output, so the user
/// can determine the best way to handle the lint.) /// can determine the best way to handle the lint.)
fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) {
@ -377,7 +377,7 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
} }
} }
fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> { fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
let mut helper = ArmSigDropHelper::new(cx); let mut helper = ArmSigDropHelper::new(cx);
for arm in arms { for arm in arms {
helper.visit_expr(arm.body); helper.visit_expr(arm.body);

View file

@ -153,7 +153,7 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) ->
} }
/// Returns `true` if the given type is an enum we know won't be expanded in the future /// Returns `true` if the given type is an enum we know won't be expanded in the future
fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
// list of candidate `Enum`s we know will never get any more members // list of candidate `Enum`s we know will never get any more members
let candidates = [sym::Cow, sym::Option, sym::Result]; let candidates = [sym::Cow, sym::Option, sym::Result];

View file

@ -9,8 +9,8 @@ use rustc_lint::LateContext;
use rustc_lint::Lint; use rustc_lint::Lint;
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
pub(super) fn check<'tcx>( pub(super) fn check(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
info: &crate::methods::BinaryExprInfo<'_>, info: &crate::methods::BinaryExprInfo<'_>,
chain_methods: &[&str], chain_methods: &[&str],
lint: &'static Lint, lint: &'static Lint,

View file

@ -4,7 +4,7 @@ use rustc_lint::LateContext;
use super::CHARS_LAST_CMP; use super::CHARS_LAST_CMP;
/// Checks for the `CHARS_LAST_CMP` lint. /// Checks for the `CHARS_LAST_CMP` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
true true
} else { } else {

View file

@ -4,7 +4,7 @@ use rustc_lint::LateContext;
use super::CHARS_LAST_CMP; use super::CHARS_LAST_CMP;
/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
true true
} else { } else {

View file

@ -3,6 +3,6 @@ use rustc_lint::LateContext;
use super::CHARS_NEXT_CMP; use super::CHARS_NEXT_CMP;
/// Checks for the `CHARS_NEXT_CMP` lint. /// Checks for the `CHARS_NEXT_CMP` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
} }

View file

@ -3,6 +3,6 @@ use rustc_lint::LateContext;
use super::CHARS_NEXT_CMP; use super::CHARS_NEXT_CMP;
/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. /// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
} }

View file

@ -23,7 +23,7 @@ pub(super) fn check<'tcx>(
// If the parent node's `to` argument is the same as the `to` argument // If the parent node's `to` argument is the same as the `to` argument
// of the last replace call in the current chain, don't lint as it was already linted // of the last replace call in the current chain, don't lint as it was already linted
if let Some(parent) = get_parent_expr(cx, expr) if let Some(parent) = get_parent_expr(cx, expr)
&& let Some(("replace", _, [current_from, current_to], _)) = method_call(parent) && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent)
&& eq_expr_value(cx, to, current_to) && eq_expr_value(cx, to, current_to)
&& from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
{ {
@ -48,7 +48,7 @@ fn collect_replace_calls<'tcx>(
let mut from_args = VecDeque::new(); let mut from_args = VecDeque::new();
let _: Option<()> = for_each_expr(expr, |e| { let _: Option<()> = for_each_expr(expr, |e| {
if let Some(("replace", _, [from, to], _)) = method_call(e) { if let Some(("replace", _, [from, to], _, _)) = method_call(e) {
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
methods.push_front(e); methods.push_front(e);
from_args.push_front(from); from_args.push_front(from);
@ -78,7 +78,7 @@ fn check_consecutive_replace_calls<'tcx>(
.collect(); .collect();
let app = Applicability::MachineApplicable; let app = Applicability::MachineApplicable;
let earliest_replace_call = replace_methods.methods.front().unwrap(); let earliest_replace_call = replace_methods.methods.front().unwrap();
if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) { if let Some((_, _, [..], span_lo, _)) = method_call(earliest_replace_call) {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
COLLAPSIBLE_STR_REPLACE, COLLAPSIBLE_STR_REPLACE,

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_in_test_function; use clippy_utils::is_in_cfg_test;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -18,16 +18,16 @@ pub(super) fn check(
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
Some((EXPECT_USED, "an Option", "None", "")) Some((EXPECT_USED, "an `Option`", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
} else { } else {
None None
}; };
let method = if is_err { "expect_err" } else { "expect" }; let method = if is_err { "expect_err" } else { "expect" };
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) {
return; return;
} }
@ -36,7 +36,7 @@ pub(super) fn check(
cx, cx,
lint, lint,
expr.span, expr.span,
&format!("used `{method}()` on `{kind}` value"), &format!("used `{method}()` on {kind} value"),
None, None,
&format!("if this value is {none_prefix}`{none_value}`, it will panic"), &format!("if this value is {none_prefix}`{none_value}`, it will panic"),
); );

View file

@ -17,7 +17,7 @@ use super::MANUAL_FILTER_MAP;
use super::MANUAL_FIND_MAP; use super::MANUAL_FIND_MAP;
use super::OPTION_FILTER_MAP; use super::OPTION_FILTER_MAP;
fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
match &expr.kind { match &expr.kind {
hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
hir::ExprKind::Path(QPath::Resolved(_, segments)) => { hir::ExprKind::Path(QPath::Resolved(_, segments)) => {
@ -46,7 +46,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy
} }
} }
fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool {
is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
} }
@ -66,8 +66,8 @@ fn is_filter_some_map_unwrap(
/// lint use of `filter().map()` or `find().map()` for `Iterators` /// lint use of `filter().map()` or `find().map()` for `Iterators`
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(super) fn check<'tcx>( pub(super) fn check(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
expr: &hir::Expr<'_>, expr: &hir::Expr<'_>,
filter_recv: &hir::Expr<'_>, filter_recv: &hir::Expr<'_>,
filter_arg: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>,

View file

@ -12,8 +12,8 @@ use rustc_span::symbol::{Symbol, sym};
use super::INEFFICIENT_TO_STRING; use super::INEFFICIENT_TO_STRING;
/// Checks for the `INEFFICIENT_TO_STRING` lint /// Checks for the `INEFFICIENT_TO_STRING` lint
pub fn check<'tcx>( pub fn check(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
expr: &hir::Expr<'_>, expr: &hir::Expr<'_>,
method_name: Symbol, method_name: Symbol,
receiver: &hir::Expr<'_>, receiver: &hir::Expr<'_>,

View file

@ -10,7 +10,7 @@ use rustc_span::sym;
use super::ITER_NTH_ZERO; use super::ITER_NTH_ZERO;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
if_chain! { if_chain! {
if is_trait_method(cx, expr, sym::Iterator); if is_trait_method(cx, expr, sym::Iterator);
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg);

View file

@ -25,7 +25,7 @@ impl IterType {
} }
} }
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
let item = match recv.kind { let item = match recv.kind {
ExprKind::Array([]) => None, ExprKind::Array([]) => None,
ExprKind::Array([e]) => Some(e), ExprKind::Array([e]) => Some(e),

View file

@ -67,7 +67,7 @@ enum MinMax {
Max, Max,
} }
fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> { fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
// `T::max_value()` `T::min_value()` inherent methods // `T::max_value()` `T::min_value()` inherent methods
if_chain! { if_chain! {
if let hir::ExprKind::Call(func, args) = &expr.kind; if let hir::ExprKind::Call(func, args) = &expr.kind;

View file

@ -59,10 +59,8 @@ pub(super) fn check(
if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind;
if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat);
if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String);
if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id);
if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id);
if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id);
if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id);
if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg);
let ctxt = collect_expr.span.ctxt(); let ctxt = collect_expr.span.ctxt();

View file

@ -15,11 +15,11 @@ use rustc_span::{sym, Span};
use super::MAP_CLONE; use super::MAP_CLONE;
pub(super) fn check<'tcx>( pub(super) fn check(
cx: &LateContext<'_>, cx: &LateContext<'_>,
e: &hir::Expr<'_>, e: &hir::Expr<'_>,
recv: &hir::Expr<'_>, recv: &hir::Expr<'_>,
arg: &'tcx hir::Expr<'_>, arg: &hir::Expr<'_>,
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
) { ) {
if_chain! { if_chain! {

View file

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_trait_method;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain; use if_chain::if_chain;
@ -11,18 +10,10 @@ use rustc_span::symbol::sym;
use super::MAP_COLLECT_RESULT_UNIT; use super::MAP_COLLECT_RESULT_UNIT;
pub(super) fn check( pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) {
cx: &LateContext<'_>, // return of collect `Result<(),_>`
expr: &hir::Expr<'_>, let collect_ret_ty = cx.typeck_results().expr_ty(expr);
iter: &hir::Expr<'_>,
map_fn: &hir::Expr<'_>,
collect_recv: &hir::Expr<'_>,
) {
if_chain! { if_chain! {
// called on Iterator
if is_trait_method(cx, collect_recv, sym::Iterator);
// 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 is_type_diagnostic_item(cx, collect_ret_ty, sym::Result);
if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let ty::Adt(_, substs) = collect_ret_ty.kind();
if let Some(result_t) = substs.types().next(); if let Some(result_t) = substs.types().next();

View file

@ -6,7 +6,7 @@ use rustc_span::sym;
use super::MAP_ERR_IGNORE; use super::MAP_ERR_IGNORE;
pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) { 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) if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
&& is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)

View file

@ -54,6 +54,7 @@ mod map_flatten;
mod map_identity; mod map_identity;
mod map_unwrap_or; mod map_unwrap_or;
mod mut_mutex_lock; mod mut_mutex_lock;
mod needless_collect;
mod needless_option_as_deref; mod needless_option_as_deref;
mod needless_option_take; mod needless_option_take;
mod no_effect_replace; mod no_effect_replace;
@ -69,6 +70,8 @@ mod path_buf_push_overwrite;
mod range_zip_with_len; mod range_zip_with_len;
mod repeat_once; mod repeat_once;
mod search_is_some; mod search_is_some;
mod seek_from_current;
mod seek_to_start_instead_of_rewind;
mod single_char_add_str; mod single_char_add_str;
mod single_char_insert_string; mod single_char_insert_string;
mod single_char_pattern; mod single_char_pattern;
@ -101,12 +104,11 @@ mod zst_offset;
use bind_instead_of_map::BindInsteadOfMap; use bind_instead_of_map::BindInsteadOfMap;
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
use rustc_hir_analysis::hir_ty_to_ty; use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
@ -156,9 +158,9 @@ declare_clippy_lint! {
/// ``` /// ```
/// Use instead: /// Use instead:
/// ```rust /// ```rust
/// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l");
/// ``` /// ```
#[clippy::version = "1.64.0"] #[clippy::version = "1.65.0"]
pub COLLAPSIBLE_STR_REPLACE, pub COLLAPSIBLE_STR_REPLACE,
perf, perf,
"collapse consecutive calls to str::replace (2 or more) into a single call" "collapse consecutive calls to str::replace (2 or more) into a single call"
@ -829,32 +831,30 @@ declare_clippy_lint! {
/// etc. instead. /// etc. instead.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// The function will always be called and potentially /// The function will always be called. This is only bad if it allocates or
/// allocate an object acting as the default. /// does some non-trivial amount of work.
/// ///
/// ### Known problems /// ### Known problems
/// If the function has side-effects, not calling it will /// If the function has side-effects, not calling it will change the
/// change the semantic of the program, but you shouldn't rely on that anyway. /// semantic of the program, but you shouldn't rely on that.
///
/// The lint also cannot figure out whether the function you call is
/// actually expensive to call or not.
/// ///
/// ### Example /// ### Example
/// ```rust /// ```rust
/// # let foo = Some(String::new()); /// # let foo = Some(String::new());
/// foo.unwrap_or(String::new()); /// foo.unwrap_or(String::from("empty"));
/// ``` /// ```
/// ///
/// Use instead: /// Use instead:
/// ```rust /// ```rust
/// # let foo = Some(String::new()); /// # let foo = Some(String::new());
/// foo.unwrap_or_else(String::new); /// foo.unwrap_or_else(|| String::from("empty"));
///
/// // or
///
/// # let foo = Some(String::new());
/// foo.unwrap_or_default();
/// ``` /// ```
#[clippy::version = "pre 1.29.0"] #[clippy::version = "pre 1.29.0"]
pub OR_FUN_CALL, pub OR_FUN_CALL,
perf, nursery,
"using any `*or` method with a function call, which suggests `*or_else`" "using any `*or` method with a function call, which suggests `*or_else`"
} }
@ -1728,7 +1728,7 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). /// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str).
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// Readability, this can be written more concisely as /// Readability, this can be written more concisely as
@ -2094,8 +2094,7 @@ declare_clippy_lint! {
/// let s = "Hello world!"; /// let s = "Hello world!";
/// let cow = Cow::Borrowed(s); /// let cow = Cow::Borrowed(s);
/// ///
/// let data = cow.into_owned(); /// let _data: String = cow.into_owned();
/// assert!(matches!(data, String))
/// ``` /// ```
#[clippy::version = "1.65.0"] #[clippy::version = "1.65.0"]
pub SUSPICIOUS_TO_OWNED, pub SUSPICIOUS_TO_OWNED,
@ -2426,7 +2425,7 @@ declare_clippy_lint! {
/// ### Known problems /// ### Known problems
/// ///
/// The type of the resulting iterator might become incompatible with its usage /// The type of the resulting iterator might become incompatible with its usage
#[clippy::version = "1.64.0"] #[clippy::version = "1.65.0"]
pub ITER_ON_SINGLE_ITEMS, pub ITER_ON_SINGLE_ITEMS,
nursery, nursery,
"Iterator for array of length 1" "Iterator for array of length 1"
@ -2458,7 +2457,7 @@ declare_clippy_lint! {
/// ### Known problems /// ### Known problems
/// ///
/// The type of the resulting iterator might become incompatible with its usage /// The type of the resulting iterator might become incompatible with its usage
#[clippy::version = "1.64.0"] #[clippy::version = "1.65.0"]
pub ITER_ON_EMPTY_COLLECTIONS, pub ITER_ON_EMPTY_COLLECTIONS,
nursery, nursery,
"Iterator for empty array" "Iterator for empty array"
@ -3066,6 +3065,102 @@ declare_clippy_lint! {
"iterating on map using `iter` when `keys` or `values` would do" "iterating on map using `iter` when `keys` or `values` would do"
} }
declare_clippy_lint! {
/// ### What it does
///
/// Checks an argument of `seek` method of `Seek` trait
/// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead.
///
/// ### Why is this bad?
///
/// Readability. Use dedicated method.
///
/// ### Example
///
/// ```rust,no_run
/// use std::fs::File;
/// use std::io::{self, Write, Seek, SeekFrom};
///
/// fn main() -> io::Result<()> {
/// let mut f = File::create("foo.txt")?;
/// f.write_all(b"Hello")?;
/// eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?);
///
/// Ok(())
/// }
/// ```
/// Use instead:
/// ```rust,no_run
/// use std::fs::File;
/// use std::io::{self, Write, Seek, SeekFrom};
///
/// fn main() -> io::Result<()> {
/// let mut f = File::create("foo.txt")?;
/// f.write_all(b"Hello")?;
/// eprintln!("Written {} bytes", f.stream_position()?);
///
/// Ok(())
/// }
/// ```
#[clippy::version = "1.66.0"]
pub SEEK_FROM_CURRENT,
complexity,
"use dedicated method for seek from current position"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for jumps to the start of a stream that implements `Seek`
/// and uses the `seek` method providing `Start` as parameter.
///
/// ### Why is this bad?
///
/// Readability. There is a specific method that was implemented for
/// this exact scenario.
///
/// ### Example
/// ```rust
/// # use std::io;
/// fn foo<T: io::Seek>(t: &mut T) {
/// t.seek(io::SeekFrom::Start(0));
/// }
/// ```
/// Use instead:
/// ```rust
/// # use std::io;
/// fn foo<T: io::Seek>(t: &mut T) {
/// t.rewind();
/// }
/// ```
#[clippy::version = "1.66.0"]
pub SEEK_TO_START_INSTEAD_OF_REWIND,
complexity,
"jumping to the start of stream using `seek` method"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for functions collecting an iterator when collect
/// is not needed.
///
/// ### Why is this bad?
/// `collect` causes the allocation of a new data structure,
/// when this allocation may not be needed.
///
/// ### Example
/// ```rust
/// # let iterator = vec![1].into_iter();
/// let len = iterator.clone().collect::<Vec<_>>().len();
/// // should be
/// let len = iterator.count();
/// ```
#[clippy::version = "1.30.0"]
pub NEEDLESS_COLLECT,
nursery,
"collecting an iterator when collect is not needed"
}
pub struct Methods { pub struct Methods {
avoid_breaking_exported_api: bool, avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
@ -3190,16 +3285,19 @@ impl_lint_pass!(Methods => [
VEC_RESIZE_TO_ZERO, VEC_RESIZE_TO_ZERO,
VERBOSE_FILE_READS, VERBOSE_FILE_READS,
ITER_KV_MAP, ITER_KV_MAP,
SEEK_FROM_CURRENT,
SEEK_TO_START_INSTEAD_OF_REWIND,
NEEDLESS_COLLECT,
]); ]);
/// Extracts a method call name, args, and `Span` of the method name. /// Extracts a method call name, args, and `Span` of the method name.
fn method_call<'tcx>( fn method_call<'tcx>(
recv: &'tcx hir::Expr<'tcx>, recv: &'tcx hir::Expr<'tcx>,
) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> { ) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> {
if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind {
if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() {
let name = path.ident.name.as_str(); let name = path.ident.name.as_str();
return Some((name, receiver, args, path.ident.span)); return Some((name, receiver, args, path.ident.span, call_span));
} }
} }
None None
@ -3316,36 +3414,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
let ret_ty = return_ty(cx, impl_item.hir_id()); let ret_ty = return_ty(cx, impl_item.hir_id());
// walk the return type and check for Self (this does not check associated types) if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
if let Some(self_adt) = self_ty.ty_adt_def() {
if contains_adt_constructor(ret_ty, self_adt) {
return;
}
} else if ret_ty.contains(self_ty) {
return; return;
} }
// if return type is impl trait, check the associated types
if let ty::Opaque(def_id, _) = *ret_ty.kind() {
// one of the associated types must be Self
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
let assoc_ty = match projection_predicate.term.unpack() {
ty::TermKind::Ty(ty) => ty,
ty::TermKind::Const(_c) => continue,
};
// walk the associated type and check for Self
if let Some(self_adt) = self_ty.ty_adt_def() {
if contains_adt_constructor(assoc_ty, self_adt) {
return;
}
} else if assoc_ty.contains(self_ty) {
return;
}
}
}
}
if name == "new" && ret_ty != self_ty { if name == "new" && ret_ty != self_ty {
span_lint( span_lint(
cx, cx,
@ -3411,7 +3483,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
impl Methods { impl Methods {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some((name, recv, args, span)) = method_call(expr) { if let Some((name, recv, args, span, call_span)) = method_call(expr) {
match (name, args) { match (name, args) {
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
zst_offset::check(cx, expr, recv); zst_offset::check(cx, expr, recv);
@ -3430,28 +3502,31 @@ impl Methods {
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
("collect", []) => match method_call(recv) { ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
Some((name @ ("cloned" | "copied"), recv2, [], _)) => { needless_collect::check(cx, span, expr, recv, call_span);
iter_cloned_collect::check(cx, name, expr, recv2); match method_call(recv) {
}, Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => {
Some(("map", m_recv, [m_arg], _)) => { iter_cloned_collect::check(cx, name, expr, recv2);
map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); },
}, Some(("map", m_recv, [m_arg], _, _)) => {
Some(("take", take_self_arg, [take_arg], _)) => { map_collect_result_unit::check(cx, expr, m_recv, m_arg);
if meets_msrv(self.msrv, msrvs::STR_REPEAT) { },
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); Some(("take", take_self_arg, [take_arg], _, _)) => {
} if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
}, manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
_ => {}, }
},
_ => {},
}
}, },
("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => { Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => {
iter_count::check(cx, expr, recv2, name2); iter_count::check(cx, expr, recv2, name2);
}, },
Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg), Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg),
Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg), Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg),
Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
_ => {}, _ => {},
}, },
("drain", [arg]) => { ("drain", [arg]) => {
@ -3463,8 +3538,8 @@ impl Methods {
} }
}, },
("expect", [_]) => match method_call(recv) { ("expect", [_]) => match method_call(recv) {
Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv), Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
}, },
("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
@ -3484,13 +3559,13 @@ impl Methods {
flat_map_option::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span);
}, },
("flatten", []) => match method_call(recv) { ("flatten", []) => match method_call(recv) {
Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
_ => {}, _ => {},
}, },
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
("for_each", [_]) => { ("for_each", [_]) => {
if let Some(("inspect", _, [_], span2)) = method_call(recv) { if let Some(("inspect", _, [_], span2, _)) = method_call(recv) {
inspect_for_each::check(cx, expr, span2); inspect_for_each::check(cx, expr, span2);
} }
}, },
@ -3510,12 +3585,12 @@ impl Methods {
iter_on_single_or_empty_collections::check(cx, expr, name, recv); iter_on_single_or_empty_collections::check(cx, expr, name, recv);
}, },
("join", [join_arg]) => { ("join", [join_arg]) => {
if let Some(("collect", _, _, span)) = method_call(recv) { if let Some(("collect", _, _, span, _)) = method_call(recv) {
unnecessary_join::check(cx, expr, recv, join_arg, span); unnecessary_join::check(cx, expr, recv, join_arg, span);
} }
}, },
("last", []) | ("skip", [_]) => { ("last", []) | ("skip", [_]) => {
if let Some((name2, recv2, args2, _span2)) = method_call(recv) { if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) { if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
} }
@ -3527,13 +3602,13 @@ impl Methods {
(name @ ("map" | "map_err"), [m_arg]) => { (name @ ("map" | "map_err"), [m_arg]) => {
if name == "map" { if name == "map" {
map_clone::check(cx, expr, recv, m_arg, self.msrv); map_clone::check(cx, expr, recv, m_arg, self.msrv);
if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) { if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) {
iter_kv_map::check(cx, map_name, expr, recv2, m_arg); iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
} }
} else { } else {
map_err_ignore::check(cx, expr, m_arg); map_err_ignore::check(cx, expr, m_arg);
} }
if let Some((name, recv2, args, span2)) = method_call(recv) { if let Some((name, recv2, args, span2,_)) = method_call(recv) {
match (name, args) { match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
@ -3553,7 +3628,7 @@ impl Methods {
manual_ok_or::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map);
}, },
("next", []) => { ("next", []) => {
if let Some((name2, recv2, args2, _)) = method_call(recv) { if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
match (name2, args2) { match (name2, args2) {
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
@ -3566,10 +3641,10 @@ impl Methods {
} }
}, },
("nth", [n_arg]) => match method_call(recv) { ("nth", [n_arg]) => match method_call(recv) {
Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
_ => iter_nth_zero::check(cx, expr, recv, n_arg), _ => iter_nth_zero::check(cx, expr, recv, n_arg),
}, },
("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
@ -3604,6 +3679,14 @@ impl Methods {
("resize", [count_arg, default_arg]) => { ("resize", [count_arg, default_arg]) => {
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
}, },
("seek", [arg]) => {
if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
seek_from_current::check(cx, expr, recv, arg);
}
if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
}
},
("sort", []) => { ("sort", []) => {
stable_sort_primitive::check(cx, expr, recv); stable_sort_primitive::check(cx, expr, recv);
}, },
@ -3626,7 +3709,7 @@ impl Methods {
}, },
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", [_arg]) => { ("take", [_arg]) => {
if let Some((name2, recv2, args2, _span2)) = method_call(recv) { if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) { if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
} }
@ -3649,13 +3732,13 @@ impl Methods {
}, },
("unwrap", []) => { ("unwrap", []) => {
match method_call(recv) { match method_call(recv) {
Some(("get", recv, [get_arg], _)) => { Some(("get", recv, [get_arg], _, _)) => {
get_unwrap::check(cx, expr, recv, get_arg, false); get_unwrap::check(cx, expr, recv, get_arg, false);
}, },
Some(("get_mut", recv, [get_arg], _)) => { Some(("get_mut", recv, [get_arg], _, _)) => {
get_unwrap::check(cx, expr, recv, get_arg, true); get_unwrap::check(cx, expr, recv, get_arg, true);
}, },
Some(("or", recv, [or_arg], or_span)) => { Some(("or", recv, [or_arg], or_span, _)) => {
or_then_unwrap::check(cx, expr, recv, or_arg, or_span); or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
}, },
_ => {}, _ => {},
@ -3664,19 +3747,19 @@ impl Methods {
}, },
("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests), ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
("unwrap_or", [u_arg]) => match method_call(recv) { ("unwrap_or", [u_arg]) => match method_call(recv) {
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => { Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => {
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
}, },
Some(("map", m_recv, [m_arg], span)) => { Some(("map", m_recv, [m_arg], span, _)) => {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
}, },
Some(("then_some", t_recv, [t_arg], _)) => { Some(("then_some", t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
}, },
_ => {}, _ => {},
}, },
("unwrap_or_else", [u_arg]) => match method_call(recv) { ("unwrap_or_else", [u_arg]) => match method_call(recv) {
Some(("map", recv, [map_arg], _)) Some(("map", recv, [map_arg], _, _))
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
_ => { _ => {
unwrap_or_else_default::check(cx, expr, recv, u_arg); unwrap_or_else_default::check(cx, expr, recv, u_arg);
@ -3697,7 +3780,7 @@ impl Methods {
} }
fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) { if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) = method_call(recv) {
search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
} }
} }
@ -3906,14 +3989,6 @@ impl OutType {
} }
} }
fn is_bool(ty: &hir::Ty<'_>) -> bool {
if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
matches!(path.res, Res::PrimTy(PrimTy::Bool))
} else {
false
}
}
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
expected.constness == actual.constness expected.constness == actual.constness
&& expected.unsafety == actual.unsafety && expected.unsafety == actual.unsafety

View file

@ -3,94 +3,99 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::higher; use clippy_utils::higher;
use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection};
use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; use clippy_utils::{
use if_chain::if_chain; can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id,
CaptureKind,
};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, MultiSpan}; use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; use rustc_hir::{
BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind,
};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::Ident;
use rustc_span::sym; use rustc_span::{sym, Span, Symbol};
use rustc_span::Span;
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { pub(super) fn check<'tcx>(
check_needless_collect_direct_usage(expr, cx); cx: &LateContext<'tcx>,
check_needless_collect_indirect_usage(expr, cx); name_span: Span,
} collect_expr: &'tcx Expr<'_>,
fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { iter_expr: &'tcx Expr<'tcx>,
if_chain! { call_span: Span,
if let ExprKind::MethodCall(method, receiver, args, _) = expr.kind; ) {
if let ExprKind::MethodCall(chain_method, ..) = receiver.kind; if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) {
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, receiver, sym::Iterator); match parent {
then { Node::Expr(parent) => {
let ty = cx.typeck_results().expr_ty(receiver); if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
let mut applicability = Applicability::MaybeIncorrect; let mut app = Applicability::MachineApplicable;
let is_empty_sugg = "next().is_none()".to_string(); let name = name.ident.as_str();
let method_name = method.ident.name.as_str(); let collect_ty = cx.typeck_results().expr_ty(collect_expr);
let sugg = if is_type_diagnostic_item(cx, ty, sym::Vec) ||
is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) {
match method_name {
"len" => "count()".to_string(),
"is_empty" => is_empty_sugg,
"contains" => {
let contains_arg = snippet_with_applicability(cx, args[0].span, "??", &mut applicability);
let (arg, pred) = contains_arg
.strip_prefix('&')
.map_or(("&x", &*contains_arg), |s| ("x", s));
format!("any(|{arg}| x == {pred})")
}
_ => return,
}
}
else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
is_type_diagnostic_item(cx, ty, sym::HashMap) {
match method_name {
"is_empty" => is_empty_sugg,
_ => return,
}
}
else {
return;
};
span_lint_and_sugg(
cx,
NEEDLESS_COLLECT,
chain_method.ident.span.with_hi(expr.span.hi()),
NEEDLESS_COLLECT_MSG,
"replace with",
sugg,
applicability,
);
}
}
}
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { let sugg: String = match name {
if let ExprKind::Block(block, _) = expr.kind { "len" => {
for stmt in block.stmts { if let Some(adt) = collect_ty.ty_adt_def()
if_chain! { && matches!(
if let StmtKind::Local(local) = stmt.kind; cx.tcx.get_diagnostic_name(adt.did()),
if let PatKind::Binding(_, id, ..) = local.pat.kind; Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap)
if let Some(init_expr) = local.init; )
if let ExprKind::MethodCall(method_name, iter_source, [], ..) = init_expr.kind; {
if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); "count()".into()
let ty = cx.typeck_results().expr_ty(init_expr); } else {
if is_type_diagnostic_item(cx, ty, sym::Vec) || return;
is_type_diagnostic_item(cx, ty, sym::VecDeque) || }
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || },
is_type_diagnostic_item(cx, ty, sym::LinkedList); "is_empty"
let iter_ty = cx.typeck_results().expr_ty(iter_source); if is_is_empty_sig(cx, parent.hir_id)
if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)); && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
if let [iter_call] = &*iter_calls; {
then { "next().is_none()".into()
},
"contains" => {
if is_contains_sig(cx, parent.hir_id, iter_expr)
&& let Some(arg) = args.first()
{
let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind {
(arg.span, "")
} else {
(arg.span, "*")
};
let snip = snippet_with_applicability(cx, span, "??", &mut app);
format!("any(|x| x == {prefix}{snip})")
} else {
return;
}
},
_ => return,
};
span_lint_and_sugg(
cx,
NEEDLESS_COLLECT,
call_span.with_hi(parent.span.hi()),
NEEDLESS_COLLECT_MSG,
"replace with",
sugg,
app,
);
}
},
Node::Local(l) => {
if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None)
= l.pat.kind
&& let ty = cx.typeck_results().expr_ty(collect_expr)
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter()
.any(|item| is_type_diagnostic_item(cx, ty, item))
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
&& let [iter_call] = &*iter_calls
{
let mut used_count_visitor = UsedCountVisitor { let mut used_count_visitor = UsedCountVisitor {
cx, cx,
id, id,
@ -102,20 +107,20 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
} }
// Suggest replacing iter_call with iter_replacement, and removing stmt // Suggest replacing iter_call with iter_replacement, and removing stmt
let mut span = MultiSpan::from_span(method_name.ident.span); let mut span = MultiSpan::from_span(name_span);
span.push_span_label(iter_call.span, "the iterator could be used here instead"); span.push_span_label(iter_call.span, "the iterator could be used here instead");
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
super::NEEDLESS_COLLECT, super::NEEDLESS_COLLECT,
init_expr.hir_id, collect_expr.hir_id,
span, span,
NEEDLESS_COLLECT_MSG, NEEDLESS_COLLECT_MSG,
|diag| { |diag| {
let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
diag.multipart_suggestion( diag.multipart_suggestion(
iter_call.get_suggestion_text(), iter_call.get_suggestion_text(),
vec![ vec![
(stmt.span, String::new()), (l.span, String::new()),
(iter_call.span, iter_replacement) (iter_call.span, iter_replacement)
], ],
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
@ -123,11 +128,61 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
}, },
); );
} }
} },
_ => (),
} }
} }
} }
/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| {
let sig = cx.tcx.fn_sig(id).skip_binder();
sig.inputs().len() == 1 && sig.output().is_bool()
})
}
/// Checks if `<iter_ty as Iterator>::Item` is the same as `<collect_ty as IntoIter>::Item`
fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool {
let item = Symbol::intern("Item");
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
&& let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty])
&& let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty])
&& let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
cx.param_env,
cx.tcx.mk_projection(into_iter_item_proj.item_def_id, into_iter_item_proj.substs)
)
{
iter_item_ty == into_iter_item_ty
} else {
false
}
}
/// Checks if the given method call matches the expected signature of
/// `([&[mut]] self, &<iter_ty as Iterator>::Item) -> bool`
fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool {
let typeck = cx.typeck_results();
if let Some(id) = typeck.type_dependent_def_id(call_id)
&& let sig = cx.tcx.fn_sig(id)
&& sig.skip_binder().output().is_bool()
&& let [_, search_ty] = *sig.skip_binder().inputs()
&& let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind()
&& let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& let Some(iter_item) = cx.tcx
.associated_items(iter_trait)
.find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait)
&& let substs = cx.tcx.mk_substs([GenericArg::from(typeck.expr_ty_adjusted(iter_expr))].into_iter())
&& let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs)
&& let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
{
item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id))
} else {
false
}
}
struct IterFunction { struct IterFunction {
func: IterFunctionKind, func: IterFunctionKind,
span: Span, span: Span,

View file

@ -13,8 +13,8 @@ use rustc_span::sym;
use super::OPTION_AS_REF_DEREF; use super::OPTION_AS_REF_DEREF;
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
pub(super) fn check<'tcx>( pub(super) fn check(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
expr: &hir::Expr<'_>, expr: &hir::Expr<'_>,
as_ref_recv: &hir::Expr<'_>, as_ref_recv: &hir::Expr<'_>,
map_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>,

View file

@ -83,6 +83,8 @@ pub(super) fn check<'tcx>(
method_span: Span, method_span: Span,
self_expr: &hir::Expr<'_>, self_expr: &hir::Expr<'_>,
arg: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>,
// `Some` if fn has second argument
second_arg: Option<&hir::Expr<'_>>,
span: Span, span: Span,
// None if lambda is required // None if lambda is required
fun_span: Option<Span>, fun_span: Option<Span>,
@ -109,30 +111,40 @@ pub(super) fn check<'tcx>(
if poss.contains(&name); if poss.contains(&name);
then { then {
let macro_expanded_snipped; let sugg = {
let sugg: Cow<'_, str> = {
let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
(false, Some(fun_span)) => (fun_span, false), (false, Some(fun_span)) => (fun_span, false),
_ => (arg.span, true), _ => (arg.span, true),
}; };
let snippet = {
let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); let format_span = |span: Span| {
if not_macro_argument_snippet == "vec![]" { let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, "..");
macro_expanded_snipped = snippet(cx, snippet_span, ".."); let snip = if not_macro_argument_snippet == "vec![]" {
let macro_expanded_snipped = snippet(cx, snippet_span, "..");
match macro_expanded_snipped.strip_prefix("$crate::vec::") { match macro_expanded_snipped.strip_prefix("$crate::vec::") {
Some(stripped) => Cow::from(stripped), Some(stripped) => Cow::Owned(stripped.to_owned()),
None => macro_expanded_snipped, None => macro_expanded_snipped,
} }
} else { } else {
not_macro_argument_snippet not_macro_argument_snippet
} };
snip.to_string()
}; };
if use_lambda { let snip = format_span(snippet_span);
let snip = if use_lambda {
let l_arg = if fn_has_arguments { "_" } else { "" }; let l_arg = if fn_has_arguments { "_" } else { "" };
format!("|{l_arg}| {snippet}").into() format!("|{l_arg}| {snip}")
} else { } else {
snippet snip
};
if let Some(f) = second_arg {
let f = format_span(f.span);
format!("{snip}, {f}")
} else {
snip
} }
}; };
let span_replace_word = method_span.with_hi(span.hi()); let span_replace_word = method_span.with_hi(span.hi());
@ -149,8 +161,8 @@ pub(super) fn check<'tcx>(
} }
} }
if let [arg] = args { let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| {
let inner_arg = if let hir::ExprKind::Block( if let hir::ExprKind::Block(
hir::Block { hir::Block {
stmts: [], stmts: [],
expr: Some(expr), expr: Some(expr),
@ -162,19 +174,32 @@ pub(super) fn check<'tcx>(
expr expr
} else { } else {
arg arg
}; }
};
if let [arg] = args {
let inner_arg = extract_inner_arg(arg);
match inner_arg.kind { match inner_arg.kind {
hir::ExprKind::Call(fun, or_args) => { hir::ExprKind::Call(fun, or_args) => {
let or_has_args = !or_args.is_empty(); let or_has_args = !or_args.is_empty();
if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) { if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) {
let fun_span = if or_has_args { None } else { Some(fun.span) }; let fun_span = if or_has_args { None } else { Some(fun.span) };
check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span); check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span);
} }
}, },
hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
check_general_case(cx, name, method_span, receiver, arg, expr.span, None); check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None);
}, },
_ => (), _ => (),
} }
} }
// `map_or` takes two arguments
if let [arg, lambda] = args {
let inner_arg = extract_inner_arg(arg);
if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind {
let fun_span = if or_args.is_empty() { Some(fun.span) } else { None };
check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span);
}
}
} }

View file

@ -0,0 +1,48 @@
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use clippy_utils::{
diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability,
ty::implements_trait,
};
use super::SEEK_FROM_CURRENT;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
let ty = cx.typeck_results().expr_ty(recv);
if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) {
if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) {
let mut applicability = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
span_lint_and_sugg(
cx,
SEEK_FROM_CURRENT,
expr.span,
"using `SeekFrom::Current` to start from current position",
"replace with",
format!("{snip}.stream_position()"),
applicability,
);
}
}
}
fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
if let ExprKind::Call(f, args) = expr.kind &&
let ExprKind::Path(ref path) = f.kind &&
let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() &&
match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
// check if argument of `SeekFrom::Current` is `0`
if args.len() == 1 &&
let ExprKind::Lit(ref lit) = args[0].kind &&
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
return true
}
}
false
}

View file

@ -0,0 +1,45 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_trait_def_id, match_def_path, paths};
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::Span;
use super::SEEK_TO_START_INSTEAD_OF_REWIND;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
recv: &'tcx Expr<'_>,
arg: &'tcx Expr<'_>,
name_span: Span,
) {
// Get receiver type
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) &&
implements_trait(cx, ty, seek_trait_id, &[]) &&
let ExprKind::Call(func, args1) = arg.kind &&
let ExprKind::Path(ref path) = func.kind &&
let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() &&
match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) &&
args1.len() == 1 &&
let ExprKind::Lit(ref lit) = args1[0].kind &&
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
{
let method_call_span = expr.span.with_lo(name_span.lo());
span_lint_and_then(
cx,
SEEK_TO_START_INSTEAD_OF_REWIND,
method_call_span,
"used `seek` to go to the start of the stream",
|diag| {
let app = Applicability::MachineApplicable;
diag.span_suggestion(method_call_span, "replace with", "rewind()", app);
},
);
}
}

View file

@ -18,7 +18,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
let target = &arglists[0].0; let target = &arglists[0].0;
let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
let ref_str = if *self_ty.kind() == ty::Str { let ref_str = if *self_ty.kind() == ty::Str {
"" if matches!(target.kind, hir::ExprKind::Index(..)) {
"&"
} else {
""
}
} else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) {
"&" "&"
} else { } else {

View file

@ -8,7 +8,7 @@ use rustc_span::sym;
use super::SUSPICIOUS_MAP; use super::SUSPICIOUS_MAP;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
if_chain! { if_chain! {
if is_trait_method(cx, count_recv, sym::Iterator); if is_trait_method(cx, count_recv, sym::Iterator);
let closure = expr_or_init(cx, map_arg); let closure = expr_or_init(cx, map_arg);

View file

@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(
cx, cx,
UNNECESSARY_JOIN, UNNECESSARY_JOIN,
span.with_hi(expr.span.hi()), span.with_hi(expr.span.hi()),
r#"called `.collect<Vec<String>>().join("")` on an iterator"#, r#"called `.collect::<Vec<String>>().join("")` on an iterator"#,
"try using", "try using",
"collect::<String>()".to_owned(), "collect::<String>()".to_owned(),
applicability, applicability,

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_in_test_function, is_lint_allowed}; use clippy_utils::{is_in_cfg_test, is_lint_allowed};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::sym; use rustc_span::sym;
@ -18,16 +18,16 @@ pub(super) fn check(
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
Some((UNWRAP_USED, "an Option", "None", "")) Some((UNWRAP_USED, "an `Option`", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
} else { } else {
None None
}; };
let method_suffix = if is_err { "_err" } else { "" }; let method_suffix = if is_err { "_err" } else { "" };
if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) {
return; return;
} }
@ -45,7 +45,7 @@ pub(super) fn check(
cx, cx,
lint, lint,
expr.span, expr.span,
&format!("used `unwrap{method_suffix}()` on `{kind}` value"), &format!("used `unwrap{method_suffix}()` on {kind} value"),
None, None,
&help, &help,
); );

View file

@ -59,7 +59,7 @@ impl LateLintPass<'_> for ImportRename {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for Rename { path, rename } in &self.conf_renames { for Rename { path, rename } in &self.conf_renames {
let segs = path.split("::").collect::<Vec<_>>(); let segs = path.split("::").collect::<Vec<_>>();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.renames.insert(id, Symbol::intern(rename)); self.renames.insert(id, Symbol::intern(rename));
} }
} }

View file

@ -218,7 +218,7 @@ enum StopEarly {
Stop, Stop,
} }
fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
if expr.hir_id == vis.last_expr.hir_id { if expr.hir_id == vis.last_expr.hir_id {
return StopEarly::KeepGoing; return StopEarly::KeepGoing;
} }
@ -265,7 +265,7 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -
StopEarly::KeepGoing StopEarly::KeepGoing
} }
fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
match stmt.kind { match stmt.kind {
StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
// If the declaration is of a local variable, check its initializer // If the declaration is of a local variable, check its initializer

View file

@ -1,10 +1,11 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::trait_ref_of_method; use clippy_utils::{def_path_def_ids, trait_ref_of_method};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::TypeVisitable;
use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty}; use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use std::iter; use std::iter;
@ -78,26 +79,44 @@ declare_clippy_lint! {
"Check for mutable `Map`/`Set` key type" "Check for mutable `Map`/`Set` key type"
} }
declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); #[derive(Clone)]
pub struct MutableKeyType {
ignore_interior_mutability: Vec<String>,
ignore_mut_def_ids: FxHashSet<hir::def_id::DefId>,
}
impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
impl<'tcx> LateLintPass<'tcx> for MutableKeyType { impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
self.ignore_mut_def_ids.clear();
let mut path = Vec::new();
for ty in &self.ignore_interior_mutability {
path.extend(ty.split("::"));
for id in def_path_def_ids(cx, &path[..]) {
self.ignore_mut_def_ids.insert(id);
}
path.clear();
}
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
if let hir::ItemKind::Fn(ref sig, ..) = item.kind { if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
check_sig(cx, item.hir_id(), sig.decl); self.check_sig(cx, item.hir_id(), sig.decl);
} }
} }
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind {
if trait_ref_of_method(cx, item.owner_id.def_id).is_none() { if trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
check_sig(cx, item.hir_id(), sig.decl); self.check_sig(cx, item.hir_id(), sig.decl);
} }
} }
} }
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
check_sig(cx, item.hir_id(), sig.decl); self.check_sig(cx, item.hir_id(), sig.decl);
} }
} }
@ -105,73 +124,81 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
if let hir::PatKind::Wild = local.pat.kind { if let hir::PatKind::Wild = local.pat.kind {
return; return;
} }
check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat)); self.check_ty_(cx, local.span, cx.typeck_results().pat_ty(local.pat));
} }
} }
fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { impl MutableKeyType {
let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
let fn_sig = cx.tcx.fn_sig(fn_def_id); Self {
for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { ignore_interior_mutability,
check_ty(cx, hir_ty.span, *ty); ignore_mut_def_ids: FxHashSet::default(),
}
check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
}
// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
// generics (because the compiler cannot ensure immutability for unknown types).
fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = ty.peel_refs();
if let Adt(def, substs) = ty.kind() {
let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) {
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
} }
} }
}
/// Determines if a type contains interior mutability which would affect its implementation of fn check_sig(&self, cx: &LateContext<'_>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) {
/// [`Hash`] or [`Ord`]. let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool { let fn_sig = cx.tcx.fn_sig(fn_def_id);
match *ty.kind() { for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
Ref(_, inner_ty, mutbl) => { self.check_ty_(cx, hir_ty.span, *ty);
mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span)
} }
Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span), self.check_ty_(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
Array(inner_ty, size) => { }
size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
&& is_interior_mutable_type(cx, inner_ty, span) // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
} // generics (because the compiler cannot ensure immutability for unknown types).
Tuple(fields) => fields.iter().any(|ty| is_interior_mutable_type(cx, ty, span)), fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
Adt(def, substs) => { let ty = ty.peel_refs();
// Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to if let Adt(def, substs) = ty.kind() {
// that of their type parameters. Note: we don't include `HashSet` and `HashMap` let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
// because they have no impl for `Hash` or `Ord`. .iter()
let is_std_collection = [ .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
sym::Option, if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) {
sym::Result, span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
sym::LinkedList,
sym::Vec,
sym::VecDeque,
sym::BTreeMap,
sym::BTreeSet,
sym::Rc,
sym::Arc,
]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
let is_box = Some(def.did()) == cx.tcx.lang_items().owned_box();
if is_std_collection || is_box {
// The type is mutable if any of its type parameters are
substs.types().any(|ty| is_interior_mutable_type(cx, ty, span))
} else {
!ty.has_escaping_bound_vars()
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
&& !ty.is_freeze(cx.tcx, cx.param_env)
} }
} }
_ => false, }
/// Determines if a type contains interior mutability which would affect its implementation of
/// [`Hash`] or [`Ord`].
fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match *ty.kind() {
Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty),
Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty),
Array(inner_ty, size) => {
size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
&& self.is_interior_mutable_type(cx, inner_ty)
},
Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)),
Adt(def, substs) => {
// Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
// that of their type parameters. Note: we don't include `HashSet` and `HashMap`
// because they have no impl for `Hash` or `Ord`.
let def_id = def.did();
let is_std_collection = [
sym::Option,
sym::Result,
sym::LinkedList,
sym::Vec,
sym::VecDeque,
sym::BTreeMap,
sym::BTreeSet,
sym::Rc,
sym::Arc,
]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) {
// The type is mutable if any of its type parameters are
substs.types().any(|ty| self.is_interior_mutable_type(cx, ty))
} else {
!ty.has_escaping_bound_vars()
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
&& !ty.is_freeze(cx.tcx, cx.param_env)
}
},
_ => false,
}
} }
} }

View file

@ -68,13 +68,15 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
expr.span, expr.span,
"generally you want to avoid `&mut &mut _` if possible", "generally you want to avoid `&mut &mut _` if possible",
); );
} else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
span_lint( if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) {
self.cx, span_lint(
MUT_MUT, self.cx,
expr.span, MUT_MUT,
"this expression mutably borrows a mutable reference. Consider reborrowing", expr.span,
); "this expression mutably borrows a mutable reference. Consider reborrowing",
);
}
} }
} }
} }

View file

@ -36,14 +36,14 @@ declare_clippy_lint! {
declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) {
if pat.span.from_expansion() { if ref_pat.span.from_expansion() {
// OK, simple enough, lints doesn't check in macro. // OK, simple enough, lints doesn't check in macro.
return; return;
} }
// Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms
for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) {
let Node::Pat(pat) = node else { break }; let Node::Pat(pat) = node else { break };
if matches!(pat.kind, PatKind::Or(_)) { if matches!(pat.kind, PatKind::Or(_)) {
@ -52,20 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
} }
// Only lint immutable refs, because `&mut ref T` may be useful. // Only lint immutable refs, because `&mut ref T` may be useful.
let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return };
match sub_pat.kind { match pat.kind {
// Check sub_pat got a `ref` keyword (excluding `ref mut`). // Check sub_pat got a `ref` keyword (excluding `ref mut`).
PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
span_lint_and_then( span_lint_and_then(
cx, cx,
NEEDLESS_BORROWED_REFERENCE, NEEDLESS_BORROWED_REFERENCE,
pat.span, ref_pat.span,
"this pattern takes a reference on something that is being dereferenced", "this pattern takes a reference on something that is being dereferenced",
|diag| { |diag| {
// `&ref ident` // `&ref ident`
// ^^^^^ // ^^^^^
let span = pat.span.until(ident.span); let span = ref_pat.span.until(ident.span);
diag.span_suggestion_verbose( diag.span_suggestion_verbose(
span, span,
"try removing the `&ref` part", "try removing the `&ref` part",
@ -84,41 +84,71 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
}), }),
after, after,
) => { ) => {
let mut suggestions = Vec::new(); check_subpatterns(
cx,
for element_pat in itertools::chain(before, after) { "dereferencing a slice pattern where every element takes a reference",
if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { ref_pat,
// `&[..., ref ident, ...]` pat,
// ^^^^ itertools::chain(before, after),
let span = element_pat.span.until(ident.span); );
suggestions.push((span, String::new())); },
} else { PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => {
return; check_subpatterns(
} cx,
} "dereferencing a tuple pattern where every element takes a reference",
ref_pat,
if !suggestions.is_empty() { pat,
span_lint_and_then( subpatterns,
cx, );
NEEDLESS_BORROWED_REFERENCE, },
pat.span, PatKind::Struct(_, fields, _) => {
"dereferencing a slice pattern where every element takes a reference", check_subpatterns(
|diag| { cx,
// `&[...]` "dereferencing a struct pattern where every field's pattern takes a reference",
// ^ ref_pat,
let span = pat.span.until(sub_pat.span); pat,
suggestions.push((span, String::new())); fields.iter().map(|field| field.pat),
);
diag.multipart_suggestion(
"try removing the `&` and `ref` parts",
suggestions,
Applicability::MachineApplicable,
);
},
);
}
}, },
_ => {}, _ => {},
} }
} }
} }
fn check_subpatterns<'tcx>(
cx: &LateContext<'tcx>,
message: &str,
ref_pat: &Pat<'_>,
pat: &Pat<'_>,
subpatterns: impl IntoIterator<Item = &'tcx Pat<'tcx>>,
) {
let mut suggestions = Vec::new();
for subpattern in subpatterns {
match subpattern.kind {
PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
// `ref ident`
// ^^^^
let span = subpattern.span.until(ident.span);
suggestions.push((span, String::new()));
},
PatKind::Wild => {},
_ => return,
}
}
if !suggestions.is_empty() {
span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| {
// `&pat`
// ^
let span = ref_pat.span.until(pat.span);
suggestions.push((span, String::new()));
diag.multipart_suggestion(
"try removing the `&` and `ref` parts",
suggestions,
Applicability::MachineApplicable,
);
});
}
}

View file

@ -287,7 +287,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression";
fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) { fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) {
// snip is the whole *help* message that appears after the warning. // snip is the whole *help* message that appears after the warning.
// message is the warning message. // message is the warning message.
// expr is the expression which the lint warning message refers to. // expr is the expression which the lint warning message refers to.
@ -313,7 +313,7 @@ fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str,
); );
} }
fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String {
let cond_code = snippet(cx, data.if_cond.span, ".."); let cond_code = snippet(cx, data.if_cond.span, "..");
let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span));
@ -327,7 +327,7 @@ fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'
) )
} }
fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String {
let cond_code = snippet(cx, data.if_cond.span, ".."); let cond_code = snippet(cx, data.if_cond.span, "..");
// Region B // Region B
@ -361,7 +361,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data:
) )
} }
fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) { fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
if_chain! { if_chain! {
if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind; if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind;
if let Some(last_stmt) = loop_block.stmts.last(); if let Some(last_stmt) = loop_block.stmts.last();

View file

@ -340,11 +340,5 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
fn fake_read( fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
&mut self,
_: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
_: FakeReadCause,
_: HirId,
) {
}
} }

View file

@ -58,9 +58,9 @@ impl EarlyLintPass for OctalEscapes {
if let ExprKind::Lit(token_lit) = &expr.kind { if let ExprKind::Lit(token_lit) = &expr.kind {
if matches!(token_lit.kind, LitKind::Str) { if matches!(token_lit.kind, LitKind::Str) {
check_lit(cx, &token_lit, expr.span, true); check_lit(cx, token_lit, expr.span, true);
} else if matches!(token_lit.kind, LitKind::ByteStr) { } else if matches!(token_lit.kind, LitKind::ByteStr) {
check_lit(cx, &token_lit, expr.span, false); check_lit(cx, token_lit, expr.span, false);
} }
} }
} }

View file

@ -1,5 +1,9 @@
use super::ARITHMETIC_SIDE_EFFECTS; use super::ARITHMETIC_SIDE_EFFECTS;
use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; use clippy_utils::{
consts::{constant, constant_simple},
diagnostics::span_lint,
peel_hir_expr_refs,
};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
@ -38,24 +42,6 @@ impl ArithmeticSideEffects {
} }
} }
/// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a
/// non-constant environment that won't overflow.
fn has_valid_op(op: &Spanned<hir::BinOpKind>, expr: &hir::Expr<'_>) -> bool {
if let hir::ExprKind::Lit(ref lit) = expr.kind &&
let ast::LitKind::Int(value, _) = lit.node
{
match (&op.node, value) {
(hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
| (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
| (hir::BinOpKind::Mul, 0 | 1) => true,
_ => false,
}
} else {
false
}
}
/// Checks if the given `expr` has any of the inner `allowed` elements. /// Checks if the given `expr` has any of the inner `allowed` elements.
fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { fn is_allowed_ty(&self, ty: Ty<'_>) -> bool {
self.allowed self.allowed
@ -74,15 +60,14 @@ impl ArithmeticSideEffects {
self.expr_span = Some(expr.span); self.expr_span = Some(expr.span);
} }
/// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. /// If `expr` is not a literal integer like `1`, returns `None`.
fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option<LiteralIntegerTy<'expr, 'tcx>> { fn literal_integer(expr: &hir::Expr<'_>) -> Option<u128> {
if matches!(expr.kind, hir::ExprKind::Lit(_)) { if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node {
return Some(LiteralIntegerTy::Value(expr)); Some(n)
} }
if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { else {
return Some(LiteralIntegerTy::Ref(inn)); None
} }
None
} }
/// Manages when the lint should be triggered. Operations in constant environments, hard coded /// Manages when the lint should be triggered. Operations in constant environments, hard coded
@ -117,10 +102,20 @@ impl ArithmeticSideEffects {
return; return;
} }
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
(None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
(Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) {
(None, None) | (Some(_), Some(_)) => false, (None, None) => false,
(None, Some(n)) | (Some(n), None) => match (&op.node, n) {
(hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
| (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
| (hir::BinOpKind::Mul, 0 | 1) => true,
_ => false,
},
(Some(_), Some(_)) => {
matches!((lhs_ref_counter, rhs_ref_counter), (0, 0))
},
} }
} else { } else {
false false
@ -129,21 +124,45 @@ impl ArithmeticSideEffects {
self.issue_lint(cx, expr); self.issue_lint(cx, expr);
} }
} }
fn manage_unary_ops<'tcx>(
&mut self,
cx: &LateContext<'tcx>,
expr: &hir::Expr<'tcx>,
un_expr: &hir::Expr<'tcx>,
un_op: hir::UnOp,
) {
let hir::UnOp::Neg = un_op else { return; };
if constant(cx, cx.typeck_results(), un_expr).is_some() {
return;
}
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
if self.is_allowed_ty(ty) {
return;
}
let actual_un_expr = peel_hir_expr_refs(un_expr).0;
if Self::literal_integer(actual_un_expr).is_some() {
return;
}
self.issue_lint(cx, expr);
}
fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool {
self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span))
}
} }
impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) {
if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { if self.should_skip_expr(expr) {
return; return;
} }
match &expr.kind { match &expr.kind {
hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => {
self.manage_bin_ops(cx, expr, op, lhs, rhs); self.manage_bin_ops(cx, expr, op, lhs, rhs);
}, },
hir::ExprKind::Unary(hir::UnOp::Neg, _) => { hir::ExprKind::Unary(un_op, un_expr) => {
if constant_simple(cx, cx.typeck_results(), expr).is_none() { self.manage_unary_ops(cx, expr, un_expr, *un_op);
self.issue_lint(cx, expr);
}
}, },
_ => {}, _ => {},
} }
@ -177,22 +196,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
} }
} }
} }
/// Tells if an expression is a integer declared by value or by reference.
///
/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather
/// than `hirExprKind::Addr`.
enum LiteralIntegerTy<'expr, 'tcx> {
/// For example, `&199`
Ref(&'expr hir::Expr<'tcx>),
/// For example, `1` or `i32::MAX`
Value(&'expr hir::Expr<'tcx>),
}
impl<'expr, 'tcx> From<LiteralIntegerTy<'expr, 'tcx>> for &'expr hir::Expr<'tcx> {
fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self {
match from {
LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem,
}
}
}

View file

@ -199,7 +199,7 @@ fn in_impl<'tcx>(
} }
} }
fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
if_chain! { if_chain! {
if let ty::Adt(adt_def, _) = middle_ty.kind(); if let ty::Adt(adt_def, _) = middle_ty.kind();
if let Some(local_did) = adt_def.did().as_local(); if let Some(local_did) = adt_def.did().as_local();

Some files were not shown because too many files have changed in this diff Show more