--- /dev/null
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [Changelog Update](doc/changelog_update.md) if you want to update this
+document.
+
+## Unreleased / In Rust Nightly
+
+[891e1a8...master](https://github.com/rust-lang/rust-clippy/compare/891e1a8...master)
+
+## Rust 1.44
+
+Current beta, release 2020-06-04
+
+[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
+
+### New lints
+
+* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
+* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
+* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
+* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
+* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
+* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
+* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
+* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
+* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
+* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+
+
+### Moves and Deprecations
+
+* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
+* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
+* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
+* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
+* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
+* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
+* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
+* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
+* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
+
+### Enhancements
+
+* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
+ auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
+* Make [`redundant_clone`] also trigger on cases where the cloned value is not
+ consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
+* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
+* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
+* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
+* [`bool_comparison`] now also checks for inequality comparisons that can be
+ written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
+* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
+* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
+* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
+* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
+* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
+
+### False Positive Fixes
+
+* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
+* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
+* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
+* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
+* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
+* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
+* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
+* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
+* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
+
+
+### Suggestion Improvements
+
+* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
+* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
+* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
+
+### ICE Fixes
+
+* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
+* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
+
+### Documentation
+
+* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
+* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
+* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
+* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
+* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
+ not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
+
+## Rust 1.43
+
+Current stable, released 2020-04-23
+
+[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
+
+### New lints
+
+* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
+* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
+* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
+* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
+* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
+* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
+* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
+
+### Moves and Deprecations
+
+* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
+
+### Enhancements
+
+* Make [`missing_errors_doc`] lint also trigger on `async` functions
+ [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
+* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
+* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
+
+### False Positive Fixes
+
+* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
+* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
+* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
+* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
+
+### Suggestion Improvements
+
+* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
+
+### ICE Fixes
+
+* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
+* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
+* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
+
+### Documentation
+
+* Improve documentation of [`iter_nth_zero`]
+* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
+
+### Others
+
+* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
+
+
+## Rust 1.42
+
+Released 2020-03-12
+
+[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
+
+### New lints
+
+* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
+* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
+* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
+* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
+* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
+* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
+* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
+* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
+* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
+* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
+
+### Moves and Deprecations
+
+* Move [`transmute_float_to_int`] from nursery to complexity group
+ [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
+* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
+* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
+
+### Enhancements
+
+* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
+* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
+* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
+* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
+* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
+
+### False Positive Fixes
+
+* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
+* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
+* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
+* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
+* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
+* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Don't trigger [`let_underscore_must_use`] in external macros
+ [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
+* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
+
+### Suggestion Improvements
+
+* [`option_map_unwrap_or`] [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
+* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
+* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
+* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
+* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
+* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
+* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
+* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
+
+### ICE fixes
+
+* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
+
+### Documentation
+
+* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
+
+
+## Rust 1.41
+
+Released 2020-01-30
+
+[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
+
+* New Lints:
+ * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
+ * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
+ * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
+ * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
+ * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
+ * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
+ * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
+ * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
+ * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
+* Remove plugin interface, see
+ [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
+ details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
+* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
+* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
+* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
+ [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
+* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
+* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
+* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
+* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
+* Fix false positive in `print_with_newline` and `write_with_newline`
+ [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
+* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
+* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
+* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
+* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
+* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
+* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
+* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
+* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
+* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
+* Display help when running `clippy-driver` without arguments, instead of ICEing
+ [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
+* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
+* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
+* Improve Documentation by adding positive examples to some lints
+ [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
+
+## Rust 1.40
+
+Released 2019-12-19
+
+[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
+
+* New Lints:
+ * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
+ * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
+ * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
+ * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
+ * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
+ * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
+ * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
+ * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
+ * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`option_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`result_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
+* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
+* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
+* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
+* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
+* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
+* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
+* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
+* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
+* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
+* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
+* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
+* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
+* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
+* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
+* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
+
+## Rust 1.39
+
+Released 2019-11-07
+
+[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
+
+* New Lints:
+ * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
+ * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
+ * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
+ * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
+ * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
+ * [`option_and_then_some`] [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
+ * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
+* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
+* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
+* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
+* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
+* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
+* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
+* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
+* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
+* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
+* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
+* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
+* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
+* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
+* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
+* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
+* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
+* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
+* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
+* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
+* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
+* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
+* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
+* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
+* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
+* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
+* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
+* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
+* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
+* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
+
+## Rust 1.38
+
+Released 2019-09-26
+
+[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
+
+* New Lints:
+ * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
+ * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
+ * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
+* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
+* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
+* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
+* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
+* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
+* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
+* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
+* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
+* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
+* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
+* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
+* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
+* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
+* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
+* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
+
+## Rust 1.37
+
+Released 2019-08-15
+
+[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
+
+* New Lints:
+ * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
+ * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
+ * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
+* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
+ The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
+* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
+* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
+* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
+* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
+* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
+* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
+* Improve suggestions for [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
+* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
+* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
+* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
+* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
+
+## Rust 1.36
+
+Released 2019-07-04
+
+[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
+
+* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
+* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
+* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
+* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
+* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
+* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
+* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
+* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
+* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
+* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
+* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
+* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
+* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
+* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
+* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
+* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
+* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
+* Add macro check for [`unnecessary_cast`] [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
+* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
+* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
+* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
+
+
+## Rust 1.35
+
+Released 2019-05-20
+
+[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
+
+* New lint: [`drop_bounds`] to detect `T: Drop` bounds
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
+* Move [`get_unwrap`] to the restriction category
+* Improve suggestions for [`iter_cloned_collect`]
+* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
+* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
+* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
+* Fix false positive in [`bool_comparison`] pertaining to non-bool types
+* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
+* Fix false positive in [`option_map_unwrap_or`] on non-copy types
+* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
+* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
+* Fix false positive in [`needless_continue`] pertaining to loop labels
+* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
+* Fix false positive for [`use_self`] in nested functions
+* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
+* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
+* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
+* Avoid triggering [`redundant_closure`] in macros
+* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
+
+## Rust 1.34
+
+Released 2019-04-10
+
+[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
+
+* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
+* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
+* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
+* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
+ configured using the `too-many-lines-threshold` configuration.
+* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
+* Expand `redundant_closure` to also work for methods (not only functions)
+* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
+* Fix false positive in `cast_sign_loss`
+* Fix false positive in `integer_arithmetic`
+* Fix false positive in `unit_arg`
+* Fix false positives in `implicit_return`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `cast_lossless`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `needless_bool`
+* Fix incorrect suggestion for `needless_range_loop`
+* Fix incorrect suggestion for `use_self`
+* Fix incorrect suggestion for `while_let_on_iterator`
+* Clippy is now slightly easier to invoke in non-cargo contexts. See
+ [#3665][pull3665] for more details.
+* We now have [improved documentation][adding_lints] on how to add new lints
+
+## Rust 1.33
+
+Released 2019-02-26
+
+[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
+
+* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
+* The `rust-clippy` repository is now part of the `rust-lang` org.
+* Rename `stutter` to `module_name_repetitions`
+* Merge `new_without_default_derive` into `new_without_default` lint
+* Move `large_digit_groups` from `style` group to `pedantic`
+* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
+ comparisons against booleans
+* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
+* Expand `redundant_clone` to work on struct fields
+* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
+* Expand `use_self` to work on tuple structs and also in local macros
+* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
+* Fix false positives in `implicit_return`
+* Fix false positives in `use_self`
+* Fix false negative in `clone_on_copy`
+* Fix false positive in `doc_markdown`
+* Fix false positive in `empty_loop`
+* Fix false positive in `if_same_then_else`
+* Fix false positive in `infinite_iter`
+* Fix false positive in `question_mark`
+* Fix false positive in `useless_asref`
+* Fix false positive in `wildcard_dependencies`
+* Fix false positive in `write_with_newline`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `get_unwrap`
+
+## Rust 1.32
+
+Released 2019-01-17
+
+[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
+
+* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
+ [`redundant_clone`], [`wildcard_dependencies`],
+ [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`],
+ [`mem_discriminant_non_enum`], [`cargo_common_metadata`]
+* Add support for `u128` and `i128` to integer related lints
+* Add float support to `mistyped_literal_suffixes`
+* Fix false positives in `use_self`
+* Fix false positives in `missing_comma`
+* Fix false positives in `new_ret_no_self`
+* Fix false positives in `possible_missing_comma`
+* Fix false positive in `integer_arithmetic` in constant items
+* Fix false positive in `needless_borrow`
+* Fix false positive in `out_of_bounds_indexing`
+* Fix false positive in `new_without_default_derive`
+* Fix false positive in `string_lit_as_bytes`
+* Fix false negative in `out_of_bounds_indexing`
+* Fix false negative in `use_self`. It will now also check existential types
+* Fix incorrect suggestion for `redundant_closure_call`
+* Fix various suggestions that contained expanded macros
+* Fix `bool_comparison` triggering 3 times on on on the same code
+* Expand `trivially_copy_pass_by_ref` to work on trait methods
+* Improve suggestion for `needless_range_loop`
+* Move `needless_pass_by_value` from `pedantic` group to `style`
+
+## Rust 1.31
+
+Released 2018-12-06
+
+[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
+
+* Clippy has been relicensed under a dual MIT / Apache license.
+ See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
+ information.
+* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
+ installation method is via `rustup component add clippy`.
+* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
+ [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
+* Fix ICE in `if_let_redundant_pattern_matching`
+* Fix ICE in `needless_pass_by_value` when encountering a generic function
+ argument with a lifetime parameter
+* Fix ICE in `needless_range_loop`
+* Fix ICE in `single_char_pattern` when encountering a constant value
+* Fix false positive in `assign_op_pattern`
+* Fix false positive in `boxed_local` on trait implementations
+* Fix false positive in `cmp_owned`
+* Fix false positive in `collapsible_if` when conditionals have comments
+* Fix false positive in `double_parens`
+* Fix false positive in `excessive_precision`
+* Fix false positive in `explicit_counter_loop`
+* Fix false positive in `fn_to_numeric_cast_with_truncation`
+* Fix false positive in `map_clone`
+* Fix false positive in `new_ret_no_self`
+* Fix false positive in `new_without_default` when `new` is unsafe
+* Fix false positive in `type_complexity` when using extern types
+* Fix false positive in `useless_format`
+* Fix false positive in `wrong_self_convention`
+* Fix incorrect suggestion for `excessive_precision`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `get_unwrap`
+* Fix incorrect suggestion for `useless_format`
+* `fn_to_numeric_cast_with_truncation` lint can be disabled again
+* Improve suggestions for `manual_memcpy`
+* Improve help message for `needless_lifetimes`
+
+## Rust 1.30
+
+Released 2018-10-25
+
+[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
+
+* Deprecate `assign_ops` lint
+* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
+ [`needless_collect`], [`copy_iterator`]
+* `cargo clippy -V` now includes the Clippy commit hash of the Rust
+ Clippy component
+* Fix ICE in `implicit_hasher`
+* Fix ICE when encountering `println!("{}" a);`
+* Fix ICE when encountering a macro call in match statements
+* Fix false positive in `default_trait_access`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `similar_names`
+* Fix false positive in `redundant_field_name`
+* Fix false positive in `expect_fun_call`
+* Fix false negative in `identity_conversion`
+* Fix false negative in `explicit_counter_loop`
+* Fix `range_plus_one` suggestion and false negative
+* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
+* Fix `useless_attribute` to also whitelist `unused_extern_crates`
+* Fix incorrect suggestion for `single_char_pattern`
+* Improve suggestion for `identity_conversion` lint
+* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
+* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
+* Move `shadow_unrelated` from `restriction` group to `pedantic`
+* Move `indexing_slicing` from `pedantic` group to `restriction`
+
+## Rust 1.29
+
+Released 2018-09-13
+
+[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
+
+* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
+ :tada:
+ You can now run `rustup component add clippy-preview` and then `cargo
+ clippy` to run Clippy. This should put an end to the continuous nightly
+ upgrades for Clippy users.
+* Clippy now follows the Rust versioning scheme instead of its own
+* Fix ICE when encountering a `while let (..) = x.iter()` construct
+* Fix false positives in `use_self`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `useless_attribute` lint
+* Fix false positive in `print_literal`
+* Fix `use_self` regressions
+* Improve lint message for `neg_cmp_op_on_partial_ord`
+* Improve suggestion highlight for `single_char_pattern`
+* Improve suggestions for various print/write macro lints
+* Improve website header
+
+## 0.0.212 (2018-07-10)
+* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
+
+## 0.0.211
+* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
+
+## 0.0.210
+* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
+
+## 0.0.209
+* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
+
+## 0.0.208
+* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
+
+## 0.0.207
+* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
+
+## 0.0.206
+* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
+
+## 0.0.205
+* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
+* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
+
+## 0.0.204
+* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
+
+## 0.0.203
+* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
+* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
+
+## 0.0.202
+* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
+
+## 0.0.201
+* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
+
+## 0.0.200
+* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
+
+## 0.0.199
+* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
+
+## 0.0.198
+* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
+
+## 0.0.197
+* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
+
+## 0.0.196
+* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
+
+## 0.0.195
+* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
+
+## 0.0.194
+* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
+* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
+
+## 0.0.193
+* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
+
+## 0.0.192
+* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
+* New lint: [`print_literal`]
+
+## 0.0.191
+* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
+* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
+
+## 0.0.190
+* Fix a bunch of intermittent cargo bugs
+
+## 0.0.189
+* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
+
+## 0.0.188
+* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
+* New lint: [`while_immutable_condition`]
+
+## 0.0.187
+* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
+* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
+
+## 0.0.186
+* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
+* Various false positive fixes
+
+## 0.0.185
+* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
+* New lint: [`question_mark`]
+
+## 0.0.184
+* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
+* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
+
+## 0.0.183
+* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
+* New lint: [`misaligned_transmute`]
+
+## 0.0.182
+* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
+* New lint: [`decimal_literal_representation`]
+
+## 0.0.181
+* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
+* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
+* Removed `unit_expr`
+* Various false positive fixes for [`needless_pass_by_value`]
+
+## 0.0.180
+* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
+
+## 0.0.179
+* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
+
+## 0.0.178
+* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
+
+## 0.0.177
+* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
+* New lint: [`match_as_ref`]
+
+## 0.0.176
+* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
+
+## 0.0.175
+* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
+
+## 0.0.174
+* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
+
+## 0.0.173
+* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
+
+## 0.0.172
+* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
+
+## 0.0.171
+* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
+
+## 0.0.170
+* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
+
+## 0.0.169
+* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
+* New lints: [`just_underscores_and_digits`], [`result_map_unwrap_or_else`], [`transmute_bytes_to_str`]
+
+## 0.0.168
+* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
+
+## 0.0.167
+* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
+* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
+
+## 0.0.166
+* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
+* New lints: [`explicit_write`], [`identity_conversion`], [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`],
+ [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
+ [`transmute_int_to_float`]
+
+## 0.0.165
+* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
+* New lint: [`mut_range_bound`]
+
+## 0.0.164
+* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
+* New lint: [`int_plus_one`]
+
+## 0.0.163
+* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
+
+## 0.0.162
+* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
+* New lint: [`chars_last_cmp`]
+* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
+
+## 0.0.161
+* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
+
+## 0.0.160
+* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
+
+## 0.0.159
+* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
+* New lint: [`clone_on_ref_ptr`]
+
+## 0.0.158
+* New lint: [`manual_memcpy`]
+* [`cast_lossless`] no longer has redundant parentheses in its suggestions
+* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
+
+## 0.0.157 - 2017-09-04
+* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
+* New lint: `unit_expr`
+
+## 0.0.156 - 2017-09-03
+* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
+
+## 0.0.155
+* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
+* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
+
+## 0.0.154
+* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
+* Fix [`use_self`] triggering inside derives
+* Add support for linting an entire workspace with `cargo clippy --all`
+* New lint: [`naive_bytecount`]
+
+## 0.0.153
+* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
+* New lint: [`use_self`]
+
+## 0.0.152
+* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
+
+## 0.0.151
+* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
+
+## 0.0.150
+* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
+
+## 0.0.148
+* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
+* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
+
+## 0.0.147
+* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
+
+## 0.0.146
+* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
+* Fixes false positives in `inline_always`
+* Fixes false negatives in `panic_params`
+
+## 0.0.145
+* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
+
+## 0.0.144
+* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
+
+## 0.0.143
+* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
+* Fix `cargo clippy` crashing on `dylib` projects
+* Fix false positives around `nested_while_let` and `never_loop`
+
+## 0.0.142
+* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
+
+## 0.0.141
+* Rewrite of the `doc_markdown` lint.
+* Deprecated [`range_step_by_zero`]
+* New lint: [`iterator_step_by_zero`]
+* New lint: [`needless_borrowed_reference`]
+* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
+
+## 0.0.140 - 2017-06-16
+* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
+
+## 0.0.139 — 2017-06-10
+* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
+* Fix bugs with for loop desugaring
+* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
+
+## 0.0.138 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
+
+## 0.0.137 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
+
+## 0.0.136 — 2017—05—26
+* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
+
+## 0.0.135 — 2017—05—24
+* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
+
+## 0.0.134 — 2017—05—19
+* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
+
+## 0.0.133 — 2017—05—14
+* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
+
+## 0.0.132 — 2017—05—05
+* Fix various bugs and some ices
+
+## 0.0.131 — 2017—05—04
+* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
+
+## 0.0.130 — 2017—05—03
+* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
+
+## 0.0.129 — 2017-05-01
+* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
+
+## 0.0.128 — 2017-04-28
+* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
+
+## 0.0.127 — 2017-04-27
+* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
+* New lint: [`needless_continue`]
+
+## 0.0.126 — 2017-04-24
+* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
+
+## 0.0.125 — 2017-04-19
+* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
+
+## 0.0.124 — 2017-04-16
+* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
+
+## 0.0.123 — 2017-04-07
+* Fix various false positives
+
+## 0.0.122 — 2017-04-07
+* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
+* New lint: [`op_ref`]
+
+## 0.0.121 — 2017-03-21
+* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
+
+## 0.0.120 — 2017-03-17
+* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
+
+## 0.0.119 — 2017-03-13
+* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
+
+## 0.0.118 — 2017-03-05
+* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
+
+## 0.0.117 — 2017-03-01
+* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
+
+## 0.0.116 — 2017-02-28
+* Fix `cargo clippy` on 64 bit windows systems
+
+## 0.0.115 — 2017-02-27
+* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
+* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
+
+## 0.0.114 — 2017-02-08
+* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
+* Tests are now ui tests (testing the exact output of rustc)
+
+## 0.0.113 — 2017-02-04
+* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
+* New lint: [`large_enum_variant`]
+* `explicit_into_iter_loop` provides suggestions
+
+## 0.0.112 — 2017-01-27
+* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
+
+## 0.0.111 — 2017-01-21
+* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
+
+## 0.0.110 — 2017-01-20
+* Add badges and categories to `Cargo.toml`
+
+## 0.0.109 — 2017-01-19
+* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
+
+## 0.0.108 — 2017-01-12
+* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
+
+## 0.0.107 — 2017-01-11
+* Update regex dependency
+* Fix FP when matching `&&mut` by `&ref`
+* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
+* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
+
+## 0.0.106 — 2017-01-04
+* Fix FP introduced by rustup in [`wrong_self_convention`]
+
+## 0.0.105 — 2017-01-04
+* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
+* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
+* Fix suggestion in [`new_without_default`]
+* FP fix in [`absurd_extreme_comparisons`]
+
+## 0.0.104 — 2016-12-15
+* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
+
+## 0.0.103 — 2016-11-25
+* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
+
+## 0.0.102 — 2016-11-24
+* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
+
+## 0.0.101 — 2016-11-23
+* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
+* New lint: [`string_extend_chars`]
+
+## 0.0.100 — 2016-11-20
+* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
+
+## 0.0.99 — 2016-11-18
+* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
+* New lint: [`get_unwrap`]
+
+## 0.0.98 — 2016-11-08
+* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
+
+## 0.0.97 — 2016-11-03
+* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
+ previously added for a short time under the name `clippy` but removed for
+ compatibility.
+* `cargo clippy --help` is more helping (and less helpful :smile:)
+* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
+* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
+
+## 0.0.96 — 2016-10-22
+* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
+* New lint: [`iter_skip_next`]
+
+## 0.0.95 — 2016-10-06
+* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
+
+## 0.0.94 — 2016-10-04
+* Fixes bustage on Windows due to forbidden directory name
+
+## 0.0.93 — 2016-10-03
+* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
+* [`option_map_unwrap_or`] and [`option_map_unwrap_or_else`] are now
+ allowed by default.
+* New lint: [`explicit_into_iter_loop`]
+
+## 0.0.92 — 2016-09-30
+* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
+
+## 0.0.91 — 2016-09-28
+* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
+
+## 0.0.90 — 2016-09-09
+* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
+
+## 0.0.89 — 2016-09-06
+* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
+
+## 0.0.88 — 2016-09-04
+* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
+* The following lints are not new but were only usable through the `clippy`
+ lint groups: [`filter_next`], [`for_loop_over_option`],
+ [`for_loop_over_result`] and [`match_overlapping_arm`]. You should now be
+ able to `#[allow/deny]` them individually and they are available directly
+ through `cargo clippy`.
+
+## 0.0.87 — 2016-08-31
+* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
+* New lints: [`builtin_type_shadow`]
+* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
+
+## 0.0.86 — 2016-08-28
+* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
+* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
+
+## 0.0.85 — 2016-08-19
+* Fix ICE with [`useless_attribute`]
+* [`useless_attribute`] ignores `unused_imports` on `use` statements
+
+## 0.0.84 — 2016-08-18
+* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
+
+## 0.0.83 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
+* New lints: [`print_with_newline`], [`useless_attribute`]
+
+## 0.0.82 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
+* New lint: [`module_inception`]
+
+## 0.0.81 — 2016-08-14
+* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
+* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
+* False positive fix in [`too_many_arguments`]
+* Addition of functionality to [`needless_borrow`]
+* Suggestions for [`clone_on_copy`]
+* Bug fix in [`wrong_self_convention`]
+* Doc improvements
+
+## 0.0.80 — 2016-07-31
+* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
+* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
+
+## 0.0.79 — 2016-07-10
+* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
+* Major suggestions refactoring
+
+## 0.0.78 — 2016-07-02
+* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
+* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
+* For compatibility, `cargo clippy` does not defines the `clippy` feature
+ introduced in 0.0.76 anymore
+* [`collapsible_if`] now considers `if let`
+
+## 0.0.77 — 2016-06-21
+* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
+* New lints: `stutter` and [`iter_nth`]
+
+## 0.0.76 — 2016-06-10
+* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
+* `cargo clippy` now automatically defines the `clippy` feature
+* New lint: [`not_unsafe_ptr_arg_deref`]
+
+## 0.0.75 — 2016-06-08
+* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
+
+## 0.0.74 — 2016-06-07
+* Fix bug with `cargo-clippy` JSON parsing
+* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
+ “for further information visit *lint-link*” message.
+
+## 0.0.73 — 2016-06-05
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.72 — 2016-06-04
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.71 — 2016-05-31
+* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
+* New lint: [`useless_let_if_seq`]
+
+## 0.0.70 — 2016-05-28
+* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
+* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
+ `RegexBuilder::new` and byte regexes
+
+## 0.0.69 — 2016-05-20
+* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
+* [`used_underscore_binding`] has been made `Allow` temporarily
+
+## 0.0.68 — 2016-05-17
+* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
+* New lint: [`unnecessary_operation`]
+
+## 0.0.67 — 2016-05-12
+* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
+
+## 0.0.66 — 2016-05-11
+* New `cargo clippy` subcommand
+* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
+
+## 0.0.65 — 2016-05-08
+* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
+* New lints: [`float_arithmetic`], [`integer_arithmetic`]
+
+## 0.0.64 — 2016-04-26
+* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
+* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`]
+
+## 0.0.63 — 2016-04-08
+* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
+
+## 0.0.62 — 2016-04-07
+* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
+
+## 0.0.61 — 2016-04-03
+* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
+* New lint: [`invalid_upcast_comparisons`]
+
+## 0.0.60 — 2016-04-01
+* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
+
+## 0.0.59 — 2016-03-31
+* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
+* New lints: [`logic_bug`], [`nonminimal_bool`]
+* Fixed: [`match_same_arms`] now ignores arms with guards
+* Improved: [`useless_vec`] now warns on `for … in vec![…]`
+
+## 0.0.58 — 2016-03-27
+* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
+* New lint: [`doc_markdown`]
+
+## 0.0.57 — 2016-03-27
+* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
+* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
+* New lint: [`crosspointer_transmute`]
+
+## 0.0.56 — 2016-03-23
+* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
+* New lints: [`many_single_char_names`] and [`similar_names`]
+
+## 0.0.55 — 2016-03-21
+* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
+
+## 0.0.54 — 2016-03-16
+* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
+
+## 0.0.53 — 2016-03-15
+* Add a [configuration file]
+
+## ~~0.0.52~~
+
+## 0.0.51 — 2016-03-13
+* Add `str` to types considered by [`len_zero`]
+* New lints: [`indexing_slicing`]
+
+## 0.0.50 — 2016-03-11
+* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
+
+## 0.0.49 — 2016-03-09
+* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
+* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`]
+
+## 0.0.48 — 2016-03-07
+* Fixed: ICE in [`needless_range_loop`] with globals
+
+## 0.0.47 — 2016-03-07
+* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
+* New lint: [`redundant_closure_call`]
+
+[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
+[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
+[configuration file]: ./rust-clippy#configuration
+[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+
+<!-- lint disable no-unused-definitions -->
+<!-- begin autogenerated links to lint list -->
+[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
+[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
+[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
+[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
+[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
+[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
+[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
+[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
+[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
+[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
+[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
+[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
+[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
+[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
+[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
+[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
+[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
+[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
+[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
+[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
+[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
+[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
+[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
+[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
+[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
+[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
+[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
+[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
+[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
+[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
+[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
+[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
+[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
+[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
+[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
+[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
+[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
+[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
+[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
+[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
+[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
+[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
+[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
+[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
+[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
+[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
+[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
+[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
+[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
+[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
+[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
+[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
+[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
+[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
+[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
+[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
+[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
+[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
+[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
+[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
+[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
+[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
+[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
+[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
+[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
+[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
+[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
+[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
+[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
+[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
+[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
+[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
+[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
+[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
+[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
+[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
+[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
+[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
+[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
+[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
+[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
+[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
+[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
+[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
+[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
+[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
+[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
+[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
+[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
+[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
+[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
+[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
+[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
+[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
+[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
+[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
+[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
+[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
+[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
+[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
+[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
+[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
+[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
+[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
+[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
+[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
+[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
+[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
+[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
+[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
+[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
+[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
+[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
+[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
+[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
+[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
+[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
+[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
+[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
+[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
+[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
+[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
+[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
+[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
+[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
+[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
+[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
+[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
+[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
+[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
+[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
+[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
+[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
+[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
+[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
+[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
+[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
+[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
+[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
+[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
+[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
+[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
+[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
+[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
+[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
+[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
+[`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_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_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
+[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
+[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
+[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
+[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
+[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
++[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
+[`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_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
+[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
+[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
+[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
+[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
+[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
+[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
+[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
+[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
+[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
+[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
+[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
+[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
+[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
+[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
+[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
+[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
+[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
+[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
+[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
+[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
+[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
+[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
+[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
+[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
+[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
+[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
+[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
+[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
+[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
+[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
+[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
+[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
+[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
+[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
+[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
+[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
+[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
+[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
+[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
+[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
+[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
+[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
+[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
+[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
+[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
+[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
+[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
+[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
+[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
+[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
+[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
+[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
+[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
+[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
+[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
+[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
+[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
+[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
+[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
+[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
+[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
+[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
+[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
+[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
+[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
+[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
+[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
+[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
+[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
+[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
+[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
+[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
+[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
+[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
+[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
+[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
+[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
+[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
+[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
+[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
+[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
+[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
+[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
+[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
+[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
+[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
+[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
+[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
+[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
+[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
+[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
+[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
+[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
+[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
+[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
+[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
+[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
+[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
+[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
+[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
+[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
+[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
+[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
+[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
+[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
+[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
+[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
+[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
+[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
+[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
+[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
+[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
+[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
+[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
+[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
+[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
+[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
+[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
+[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
+[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
+[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
+[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
+[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
+[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
+[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
+[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
+[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
+[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
+[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
+[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
+[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
+[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
+[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
+[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
+[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
+[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
+[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
+[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
+[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
+[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
+[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
+[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
+[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
+[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
+[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
+[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
+[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
+[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
+[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
+[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
+[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
+[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
+[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
+[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
+[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
+[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
+[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
+[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
+[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
+[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
+[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
+[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
+[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
+[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
+[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
+[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
+[`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_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
+[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
+[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
+[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
+[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
+[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
+[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
+[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
+[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
+[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
+[`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_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
+[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
+[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
+[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
+[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
+[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
+[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
+[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
+[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
+[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
+[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
+[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
+[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
+[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
+[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
+[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
+[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
+[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
+[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
+[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
+[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
+[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
+[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
+[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
+[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
+[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
+[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
+[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
+[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
+[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
+[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
+[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
+[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
+[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
+[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
+[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
+[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
+[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
+[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
+[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
+[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
+[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
+[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
+[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
+[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
+[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
+[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
+[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
+<!-- end autogenerated links to lint list -->
--- /dev/null
- regex = "1"
+[package]
+name = "clippy"
+version = "0.0.212"
+authors = [
+ "Manish Goregaokar <manishsmail@gmail.com>",
+ "Andre Bogus <bogusandre@gmail.com>",
+ "Georg Brandl <georg@python.org>",
+ "Martin Carton <cartonmartin@gmail.com>",
+ "Oliver Schneider <clippy-iethah7aipeen8neex1a@oli-obk.de>"
+]
+description = "A bunch of helpful lints to avoid common pitfalls in Rust"
+repository = "https://github.com/rust-lang/rust-clippy"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
+categories = ["development-tools", "development-tools::cargo-plugins"]
+build = "build.rs"
+edition = "2018"
+publish = false
+
+[[bin]]
+name = "cargo-clippy"
+test = false
+path = "src/main.rs"
+
+[[bin]]
+name = "clippy-driver"
+path = "src/driver.rs"
+
+[dependencies]
+# begin automatic update
+clippy_lints = { version = "0.0.212", path = "clippy_lints" }
+# end automatic update
+semver = "0.9"
+rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
+tempfile = { version = "3.1.0", optional = true }
+lazy_static = "1.0"
+
+[dev-dependencies]
+cargo_metadata = "0.9.0"
+compiletest_rs = { version = "0.5.0", features = ["tmp"] }
+tester = "0.7"
+lazy_static = "1.0"
+clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
+serde = { version = "1.0", features = ["derive"] }
+derive-new = "0.5"
+
+# A noop dependency that changes in the Rust repository, it's a bit of a hack.
+# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
+# for more information.
+rustc-workspace-hack = "1.0.0"
+
+[build-dependencies]
+rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
+
+[features]
+deny-warnings = []
+integration = ["tempfile"]
--- /dev/null
- store.register_late_pass(|| box wildcard_imports::WildcardImports);
+// error-pattern:cargo-clippy
+
+#![feature(box_syntax)]
+#![feature(box_patterns)]
+#![feature(or_patterns)]
+#![feature(rustc_private)]
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
+#![recursion_limit = "512"]
+#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
+#![deny(rustc::internal)]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![feature(crate_visibility_modifier)]
+#![feature(concat_idents)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+#[allow(unused_extern_crates)]
+extern crate fmt_macros;
+#[allow(unused_extern_crates)]
+extern crate rustc_ast;
+#[allow(unused_extern_crates)]
+extern crate rustc_ast_pretty;
+#[allow(unused_extern_crates)]
+extern crate rustc_attr;
+#[allow(unused_extern_crates)]
+extern crate rustc_data_structures;
+#[allow(unused_extern_crates)]
+extern crate rustc_driver;
+#[allow(unused_extern_crates)]
+extern crate rustc_errors;
+#[allow(unused_extern_crates)]
+extern crate rustc_hir;
+#[allow(unused_extern_crates)]
+extern crate rustc_hir_pretty;
+#[allow(unused_extern_crates)]
+extern crate rustc_index;
+#[allow(unused_extern_crates)]
+extern crate rustc_infer;
+#[allow(unused_extern_crates)]
+extern crate rustc_lexer;
+#[allow(unused_extern_crates)]
+extern crate rustc_lint;
+#[allow(unused_extern_crates)]
+extern crate rustc_middle;
+#[allow(unused_extern_crates)]
+extern crate rustc_mir;
+#[allow(unused_extern_crates)]
+extern crate rustc_parse;
+#[allow(unused_extern_crates)]
+extern crate rustc_session;
+#[allow(unused_extern_crates)]
+extern crate rustc_span;
+#[allow(unused_extern_crates)]
+extern crate rustc_target;
+#[allow(unused_extern_crates)]
+extern crate rustc_trait_selection;
+#[allow(unused_extern_crates)]
+extern crate rustc_typeck;
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_lint::LintId;
+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`, `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)]
+/// # #[allow(unused_extern_crates)]
+/// # extern crate rustc_middle;
+/// # #[allow(unused_extern_crates)]
+/// # extern crate rustc_session;
+/// # #[macro_use]
+/// # use clippy_lints::declare_clippy_lint;
+/// use rustc_session::declare_tool_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.
+/// ///
+/// /// **Known problems:** None. (Or describe where it could go wrong.)
+/// ///
+/// /// **Example:**
+/// ///
+/// /// ```rust
+/// /// // Bad
+/// /// Insert a short example of code that triggers the lint
+/// ///
+/// /// // Good
+/// /// 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, 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
+ }
+ };
+}
+
+mod consts;
+#[macro_use]
+mod utils;
+
+// begin lints modules, do not remove this comment, it’s used in `update_lints`
+mod approx_const;
+mod arithmetic;
+mod as_conversions;
+mod assertions_on_constants;
+mod assign_ops;
+mod atomic_ordering;
+mod attrs;
+mod await_holding_lock;
+mod bit_mask;
+mod blacklisted_name;
+mod block_in_if_condition;
+mod booleans;
+mod bytecount;
+mod cargo_common_metadata;
+mod checked_conversions;
+mod cognitive_complexity;
+mod collapsible_if;
+mod comparison_chain;
+mod copies;
+mod copy_iterator;
+mod dbg_macro;
+mod default_trait_access;
+mod dereference;
+mod derive;
+mod doc;
+mod double_comparison;
+mod double_parens;
+mod drop_bounds;
+mod drop_forget_ref;
+mod duration_subsec;
+mod else_if_without_else;
+mod empty_enum;
+mod entry;
+mod enum_clike;
+mod enum_variants;
+mod eq_op;
+mod erasing_op;
+mod escape;
+mod eta_reduction;
+mod eval_order_dependence;
+mod excessive_bools;
+mod exit;
+mod explicit_write;
+mod fallible_impl_from;
+mod float_literal;
+mod floating_point_arithmetic;
+mod format;
+mod formatting;
+mod functions;
+mod future_not_send;
+mod get_last_with_len;
+mod identity_conversion;
+mod identity_op;
+mod if_let_mutex;
+mod if_let_some_result;
+mod if_not_else;
+mod implicit_return;
+mod implicit_saturating_sub;
+mod indexing_slicing;
+mod infinite_iter;
+mod inherent_impl;
+mod inherent_to_string;
+mod inline_fn_without_body;
+mod int_plus_one;
+mod integer_division;
+mod items_after_statements;
+mod large_const_arrays;
+mod large_enum_variant;
+mod large_stack_arrays;
+mod len_zero;
+mod let_if_seq;
+mod let_underscore;
+mod lifetimes;
+mod literal_representation;
+mod loops;
+mod macro_use;
+mod main_recursion;
++mod manual_async_fn;
++mod manual_non_exhaustive;
+mod map_clone;
+mod map_unit_fn;
+mod match_on_vec_items;
+mod matches;
+mod mem_discriminant;
+mod mem_forget;
+mod mem_replace;
+mod methods;
+mod minmax;
+mod misc;
+mod misc_early;
+mod missing_const_for_fn;
+mod missing_doc;
+mod missing_inline;
+mod modulo_arithmetic;
+mod multiple_crate_versions;
+mod mut_key;
+mod mut_mut;
+mod mut_reference;
+mod mutable_debug_assertion;
+mod mutex_atomic;
+mod needless_bool;
+mod needless_borrow;
+mod needless_borrowed_ref;
+mod needless_continue;
+mod needless_pass_by_value;
+mod needless_update;
+mod neg_cmp_op_on_partial_ord;
+mod neg_multiply;
+mod new_without_default;
+mod no_effect;
+mod non_copy_const;
+mod non_expressive_names;
+mod open_options;
+mod option_env_unwrap;
+mod overflow_check_conditional;
+mod panic_unimplemented;
+mod partialeq_ne_impl;
+mod path_buf_push_overwrite;
+mod precedence;
+mod ptr;
+mod ptr_offset_with_cast;
+mod question_mark;
+mod ranges;
+mod redundant_clone;
+mod redundant_field_names;
+mod redundant_pattern_matching;
+mod redundant_pub_crate;
+mod redundant_static_lifetimes;
+mod reference;
+mod regex;
+mod returns;
+mod serde_api;
+mod shadow;
+mod single_component_path_imports;
+mod slow_vector_initialization;
+mod strings;
+mod suspicious_trait_impl;
+mod swap;
+mod tabs_in_doc_comments;
+mod temporary_assignment;
+mod to_digit_is_some;
+mod trait_bounds;
+mod transmute;
+mod transmuting_null;
+mod trivially_copy_pass_by_ref;
+mod try_err;
+mod types;
+mod unicode;
+mod unnamed_address;
+mod unsafe_removed_from_name;
+mod unused_io_amount;
+mod unused_self;
+mod unwrap;
+mod use_self;
+mod vec;
+mod verbose_file_reads;
+mod wildcard_dependencies;
+mod wildcard_imports;
+mod write;
+mod zero_div_zero;
+// end lints modules, do not remove this comment, it’s used in `update_lints`
+
+pub use crate::utils::conf::Conf;
+
+mod reexport {
+ pub use rustc_span::Symbol as Name;
+}
+
+/// Register all pre expansion lints
+///
+/// Pre-expansion lints run before any macro expansion has happened.
+///
+/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
+/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
+///
+/// Used in `./src/driver.rs`.
+pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Conf) {
+ store.register_pre_expansion_pass(|| box write::Write::default());
+ store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames);
+ let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
+ store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames {
+ single_char_binding_names_threshold,
+ });
+ store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
+ store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
+}
+
+#[doc(hidden)]
+pub fn read_conf(args: &[rustc_ast::ast::NestedMetaItem], sess: &Session) -> Conf {
+ use std::path::Path;
+ match utils::conf::file_from_args(args) {
+ Ok(file_name) => {
+ // if the user specified a file, it must exist, otherwise default to `clippy.toml` but
+ // do not require the file to exist
+ let file_name = match file_name {
+ Some(file_name) => file_name,
+ None => match utils::conf::lookup_conf_file() {
+ Ok(Some(path)) => path,
+ Ok(None) => return Conf::default(),
+ Err(error) => {
+ sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
+ .emit();
+ return Conf::default();
+ },
+ },
+ };
+
+ let file_name = if file_name.is_relative() {
+ sess.local_crate_source_file
+ .as_deref()
+ .and_then(Path::parent)
+ .unwrap_or_else(|| Path::new(""))
+ .join(file_name)
+ } else {
+ file_name
+ };
+
+ let (conf, errors) = utils::conf::read(&file_name);
+
+ // all conf errors are non-fatal, we just use the default conf in case of error
+ for error in errors {
+ sess.struct_err(&format!(
+ "error reading Clippy's configuration file `{}`: {}",
+ file_name.display(),
+ error
+ ))
+ .emit();
+ }
+
+ conf
+ },
+ Err((err, span)) => {
+ sess.struct_span_err(span, err)
+ .span_note(span, "Clippy will use default configuration")
+ .emit();
+ Conf::default()
+ },
+ }
+}
+
+/// Register all lints and lint groups with the rustc plugin registry
+///
+/// Used in `./src/driver.rs`.
+#[allow(clippy::too_many_lines)]
+#[rustfmt::skip]
+pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
+ register_removed_non_tool_lints(store);
+
+ // begin deprecated lints, do not remove this comment, it’s used in `update_lints`
+ store.register_removed(
+ "clippy::should_assert_eq",
+ "`assert!()` will be more flexible with RFC 2011",
+ );
+ store.register_removed(
+ "clippy::extend_from_slice",
+ "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
+ );
+ store.register_removed(
+ "clippy::range_step_by_zero",
+ "`iterator.step_by(0)` panics nowadays",
+ );
+ store.register_removed(
+ "clippy::unstable_as_slice",
+ "`Vec::as_slice` has been stabilized in 1.7",
+ );
+ store.register_removed(
+ "clippy::unstable_as_mut_slice",
+ "`Vec::as_mut_slice` has been stabilized in 1.7",
+ );
+ store.register_removed(
+ "clippy::str_to_string",
+ "using `str::to_string` is common even today and specialization will likely happen soon",
+ );
+ store.register_removed(
+ "clippy::string_to_string",
+ "using `string::to_string` is common even today and specialization will likely happen soon",
+ );
+ store.register_removed(
+ "clippy::misaligned_transmute",
+ "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
+ );
+ store.register_removed(
+ "clippy::assign_ops",
+ "using compound assignment operators (e.g., `+=`) is harmless",
+ );
+ store.register_removed(
+ "clippy::if_let_redundant_pattern_matching",
+ "this lint has been changed to redundant_pattern_matching",
+ );
+ store.register_removed(
+ "clippy::unsafe_vector_initialization",
+ "the replacement suggested by this lint had substantially different behavior",
+ );
+ store.register_removed(
+ "clippy::invalid_ref",
+ "superseded by rustc lint `invalid_value`",
+ );
+ store.register_removed(
+ "clippy::unused_collect",
+ "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
+ );
+ store.register_removed(
+ "clippy::into_iter_on_array",
+ "this lint has been uplifted to rustc and is now called `array_into_iter`",
+ );
+ store.register_removed(
+ "clippy::unused_label",
+ "this lint has been uplifted to rustc and is now called `unused_labels`",
+ );
+ store.register_removed(
+ "clippy::replace_consts",
+ "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants",
+ );
+ // end deprecated lints, do not remove this comment, it’s used in `update_lints`
+
+ // begin register lints, do not remove this comment, it’s used in `update_lints`
+ store.register_lints(&[
+ &approx_const::APPROX_CONSTANT,
+ &arithmetic::FLOAT_ARITHMETIC,
+ &arithmetic::INTEGER_ARITHMETIC,
+ &as_conversions::AS_CONVERSIONS,
+ &assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
+ &assign_ops::ASSIGN_OP_PATTERN,
+ &assign_ops::MISREFACTORED_ASSIGN_OP,
+ &atomic_ordering::INVALID_ATOMIC_ORDERING,
+ &attrs::DEPRECATED_CFG_ATTR,
+ &attrs::DEPRECATED_SEMVER,
+ &attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
+ &attrs::INLINE_ALWAYS,
+ &attrs::MISMATCHED_TARGET_OS,
+ &attrs::UNKNOWN_CLIPPY_LINTS,
+ &attrs::USELESS_ATTRIBUTE,
+ &await_holding_lock::AWAIT_HOLDING_LOCK,
+ &bit_mask::BAD_BIT_MASK,
+ &bit_mask::INEFFECTIVE_BIT_MASK,
+ &bit_mask::VERBOSE_BIT_MASK,
+ &blacklisted_name::BLACKLISTED_NAME,
+ &block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR,
+ &block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT,
+ &booleans::LOGIC_BUG,
+ &booleans::NONMINIMAL_BOOL,
+ &bytecount::NAIVE_BYTECOUNT,
+ &cargo_common_metadata::CARGO_COMMON_METADATA,
+ &checked_conversions::CHECKED_CONVERSIONS,
+ &cognitive_complexity::COGNITIVE_COMPLEXITY,
+ &collapsible_if::COLLAPSIBLE_IF,
+ &comparison_chain::COMPARISON_CHAIN,
+ &copies::IFS_SAME_COND,
+ &copies::IF_SAME_THEN_ELSE,
+ &copies::MATCH_SAME_ARMS,
+ &copies::SAME_FUNCTIONS_IN_IF_CONDITION,
+ ©_iterator::COPY_ITERATOR,
+ &dbg_macro::DBG_MACRO,
+ &default_trait_access::DEFAULT_TRAIT_ACCESS,
+ &dereference::EXPLICIT_DEREF_METHODS,
+ &derive::DERIVE_HASH_XOR_EQ,
+ &derive::EXPL_IMPL_CLONE_ON_COPY,
+ &derive::UNSAFE_DERIVE_DESERIALIZE,
+ &doc::DOC_MARKDOWN,
+ &doc::MISSING_ERRORS_DOC,
+ &doc::MISSING_SAFETY_DOC,
+ &doc::NEEDLESS_DOCTEST_MAIN,
+ &double_comparison::DOUBLE_COMPARISONS,
+ &double_parens::DOUBLE_PARENS,
+ &drop_bounds::DROP_BOUNDS,
+ &drop_forget_ref::DROP_COPY,
+ &drop_forget_ref::DROP_REF,
+ &drop_forget_ref::FORGET_COPY,
+ &drop_forget_ref::FORGET_REF,
+ &duration_subsec::DURATION_SUBSEC,
+ &else_if_without_else::ELSE_IF_WITHOUT_ELSE,
+ &empty_enum::EMPTY_ENUM,
+ &entry::MAP_ENTRY,
+ &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
+ &enum_variants::ENUM_VARIANT_NAMES,
+ &enum_variants::MODULE_INCEPTION,
+ &enum_variants::MODULE_NAME_REPETITIONS,
+ &enum_variants::PUB_ENUM_VARIANT_NAMES,
+ &eq_op::EQ_OP,
+ &eq_op::OP_REF,
+ &erasing_op::ERASING_OP,
+ &escape::BOXED_LOCAL,
+ &eta_reduction::REDUNDANT_CLOSURE,
+ &eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+ &eval_order_dependence::DIVERGING_SUB_EXPRESSION,
+ &eval_order_dependence::EVAL_ORDER_DEPENDENCE,
+ &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
+ &excessive_bools::STRUCT_EXCESSIVE_BOOLS,
+ &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,
+ &formatting::POSSIBLE_MISSING_COMMA,
+ &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
+ &formatting::SUSPICIOUS_ELSE_FORMATTING,
+ &formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
+ &functions::DOUBLE_MUST_USE,
+ &functions::MUST_USE_CANDIDATE,
+ &functions::MUST_USE_UNIT,
+ &functions::NOT_UNSAFE_PTR_ARG_DEREF,
+ &functions::TOO_MANY_ARGUMENTS,
+ &functions::TOO_MANY_LINES,
+ &future_not_send::FUTURE_NOT_SEND,
+ &get_last_with_len::GET_LAST_WITH_LEN,
+ &identity_conversion::IDENTITY_CONVERSION,
+ &identity_op::IDENTITY_OP,
+ &if_let_mutex::IF_LET_MUTEX,
+ &if_let_some_result::IF_LET_SOME_RESULT,
+ &if_not_else::IF_NOT_ELSE,
+ &implicit_return::IMPLICIT_RETURN,
+ &implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
+ &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,
+ &inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
+ &int_plus_one::INT_PLUS_ONE,
+ &integer_division::INTEGER_DIVISION,
+ &items_after_statements::ITEMS_AFTER_STATEMENTS,
+ &large_const_arrays::LARGE_CONST_ARRAYS,
+ &large_enum_variant::LARGE_ENUM_VARIANT,
+ &large_stack_arrays::LARGE_STACK_ARRAYS,
+ &len_zero::LEN_WITHOUT_IS_EMPTY,
+ &len_zero::LEN_ZERO,
+ &let_if_seq::USELESS_LET_IF_SEQ,
+ &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,
+ &loops::EMPTY_LOOP,
+ &loops::EXPLICIT_COUNTER_LOOP,
+ &loops::EXPLICIT_INTO_ITER_LOOP,
+ &loops::EXPLICIT_ITER_LOOP,
+ &loops::FOR_KV_MAP,
+ &loops::FOR_LOOP_OVER_OPTION,
+ &loops::FOR_LOOP_OVER_RESULT,
+ &loops::ITER_NEXT_LOOP,
+ &loops::MANUAL_MEMCPY,
+ &loops::MUT_RANGE_BOUND,
+ &loops::NEEDLESS_COLLECT,
+ &loops::NEEDLESS_RANGE_LOOP,
+ &loops::NEVER_LOOP,
+ &loops::REVERSE_RANGE_LOOP,
+ &loops::WHILE_IMMUTABLE_CONDITION,
+ &loops::WHILE_LET_LOOP,
+ &loops::WHILE_LET_ON_ITERATOR,
+ ¯o_use::MACRO_USE_IMPORTS,
+ &main_recursion::MAIN_RECURSION,
++ &manual_async_fn::MANUAL_ASYNC_FN,
++ &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
+ &map_clone::MAP_CLONE,
+ &map_unit_fn::OPTION_MAP_UNIT_FN,
+ &map_unit_fn::RESULT_MAP_UNIT_FN,
+ &match_on_vec_items::MATCH_ON_VEC_ITEMS,
+ &matches::INFALLIBLE_DESTRUCTURING_MATCH,
+ &matches::MATCH_AS_REF,
+ &matches::MATCH_BOOL,
+ &matches::MATCH_OVERLAPPING_ARM,
+ &matches::MATCH_REF_PATS,
+ &matches::MATCH_SINGLE_BINDING,
+ &matches::MATCH_WILD_ERR_ARM,
+ &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
+ &matches::SINGLE_MATCH,
+ &matches::SINGLE_MATCH_ELSE,
+ &matches::WILDCARD_ENUM_MATCH_ARM,
+ &matches::WILDCARD_IN_OR_PATTERNS,
+ &mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
+ &mem_forget::MEM_FORGET,
+ &mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
+ &mem_replace::MEM_REPLACE_WITH_DEFAULT,
+ &mem_replace::MEM_REPLACE_WITH_UNINIT,
+ &methods::CHARS_LAST_CMP,
+ &methods::CHARS_NEXT_CMP,
+ &methods::CLONE_DOUBLE_REF,
+ &methods::CLONE_ON_COPY,
+ &methods::CLONE_ON_REF_PTR,
+ &methods::EXPECT_FUN_CALL,
+ &methods::FILETYPE_IS_FILE,
+ &methods::FILTER_MAP,
+ &methods::FILTER_MAP_NEXT,
+ &methods::FILTER_NEXT,
+ &methods::FIND_MAP,
+ &methods::FLAT_MAP_IDENTITY,
+ &methods::GET_UNWRAP,
+ &methods::INEFFICIENT_TO_STRING,
+ &methods::INTO_ITER_ON_REF,
+ &methods::ITERATOR_STEP_BY_ZERO,
+ &methods::ITER_CLONED_COLLECT,
+ &methods::ITER_NTH,
+ &methods::ITER_NTH_ZERO,
+ &methods::ITER_SKIP_NEXT,
+ &methods::MANUAL_SATURATING_ARITHMETIC,
+ &methods::MAP_FLATTEN,
+ &methods::NEW_RET_NO_SELF,
+ &methods::OK_EXPECT,
+ &methods::OPTION_AND_THEN_SOME,
+ &methods::OPTION_AS_REF_DEREF,
+ &methods::OPTION_EXPECT_USED,
+ &methods::OPTION_MAP_OR_NONE,
+ &methods::OPTION_MAP_UNWRAP_OR,
+ &methods::OPTION_MAP_UNWRAP_OR_ELSE,
+ &methods::OPTION_UNWRAP_USED,
+ &methods::OR_FUN_CALL,
+ &methods::RESULT_EXPECT_USED,
+ &methods::RESULT_MAP_OR_INTO_OPTION,
+ &methods::RESULT_MAP_UNWRAP_OR_ELSE,
+ &methods::RESULT_UNWRAP_USED,
+ &methods::SEARCH_IS_SOME,
+ &methods::SHOULD_IMPLEMENT_TRAIT,
+ &methods::SINGLE_CHAR_PATTERN,
+ &methods::SKIP_WHILE_NEXT,
+ &methods::STRING_EXTEND_CHARS,
+ &methods::SUSPICIOUS_MAP,
+ &methods::TEMPORARY_CSTRING_AS_PTR,
+ &methods::UNINIT_ASSUMED_INIT,
+ &methods::UNNECESSARY_FILTER_MAP,
+ &methods::UNNECESSARY_FOLD,
+ &methods::USELESS_ASREF,
+ &methods::WRONG_PUB_SELF_CONVENTION,
+ &methods::WRONG_SELF_CONVENTION,
+ &methods::ZST_OFFSET,
+ &minmax::MIN_MAX,
+ &misc::CMP_NAN,
+ &misc::CMP_OWNED,
+ &misc::FLOAT_CMP,
+ &misc::FLOAT_CMP_CONST,
+ &misc::MODULO_ONE,
+ &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_CLOSURE_CALL,
+ &misc_early::REDUNDANT_PATTERN,
+ &misc_early::UNNEEDED_FIELD_PATTERN,
+ &misc_early::UNNEEDED_WILDCARD_PATTERN,
+ &misc_early::UNSEPARATED_LITERAL_SUFFIX,
+ &misc_early::ZERO_PREFIXED_LITERAL,
+ &missing_const_for_fn::MISSING_CONST_FOR_FN,
+ &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
+ &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
+ &modulo_arithmetic::MODULO_ARITHMETIC,
+ &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
+ &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_bool::BOOL_COMPARISON,
+ &needless_bool::NEEDLESS_BOOL,
+ &needless_borrow::NEEDLESS_BORROW,
+ &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
+ &needless_continue::NEEDLESS_CONTINUE,
+ &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
+ &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::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,
+ &open_options::NONSENSICAL_OPEN_OPTIONS,
+ &option_env_unwrap::OPTION_ENV_UNWRAP,
+ &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
+ &panic_unimplemented::PANIC,
+ &panic_unimplemented::PANIC_PARAMS,
+ &panic_unimplemented::TODO,
+ &panic_unimplemented::UNIMPLEMENTED,
+ &panic_unimplemented::UNREACHABLE,
+ &partialeq_ne_impl::PARTIALEQ_NE_IMPL,
+ &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
+ &precedence::PRECEDENCE,
+ &ptr::CMP_NULL,
+ &ptr::MUT_FROM_REF,
+ &ptr::PTR_ARG,
+ &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
+ &question_mark::QUESTION_MARK,
+ &ranges::RANGE_MINUS_ONE,
+ &ranges::RANGE_PLUS_ONE,
+ &ranges::RANGE_ZIP_WITH_LEN,
+ &redundant_clone::REDUNDANT_CLONE,
+ &redundant_field_names::REDUNDANT_FIELD_NAMES,
+ &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING,
+ &redundant_pub_crate::REDUNDANT_PUB_CRATE,
+ &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
+ &reference::DEREF_ADDROF,
+ &reference::REF_IN_DEREF,
+ ®ex::INVALID_REGEX,
+ ®ex::REGEX_MACRO,
+ ®ex::TRIVIAL_REGEX,
+ &returns::LET_AND_RETURN,
+ &returns::NEEDLESS_RETURN,
+ &returns::UNUSED_UNIT,
+ &serde_api::SERDE_API_MISUSE,
+ &shadow::SHADOW_REUSE,
+ &shadow::SHADOW_SAME,
+ &shadow::SHADOW_UNRELATED,
+ &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
+ &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
+ &strings::STRING_ADD,
+ &strings::STRING_ADD_ASSIGN,
+ &strings::STRING_LIT_AS_BYTES,
+ &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
+ &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
+ &swap::ALMOST_SWAPPED,
+ &swap::MANUAL_SWAP,
+ &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
+ &temporary_assignment::TEMPORARY_ASSIGNMENT,
+ &to_digit_is_some::TO_DIGIT_IS_SOME,
+ &trait_bounds::TYPE_REPETITION_IN_BOUNDS,
+ &transmute::CROSSPOINTER_TRANSMUTE,
+ &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_PTR_TO_PTR,
+ &transmute::TRANSMUTE_PTR_TO_REF,
+ &transmute::UNSOUND_COLLECTION_TRANSMUTE,
+ &transmute::USELESS_TRANSMUTE,
+ &transmute::WRONG_TRANSMUTE,
+ &transmuting_null::TRANSMUTING_NULL,
+ &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF,
+ &try_err::TRY_ERR,
+ &types::ABSURD_EXTREME_COMPARISONS,
+ &types::BORROWED_BOX,
+ &types::BOX_VEC,
+ &types::CAST_LOSSLESS,
+ &types::CAST_POSSIBLE_TRUNCATION,
+ &types::CAST_POSSIBLE_WRAP,
+ &types::CAST_PRECISION_LOSS,
+ &types::CAST_PTR_ALIGNMENT,
+ &types::CAST_REF_TO_MUT,
+ &types::CAST_SIGN_LOSS,
+ &types::CHAR_LIT_AS_U8,
+ &types::FN_TO_NUMERIC_CAST,
+ &types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
+ &types::IMPLICIT_HASHER,
+ &types::INVALID_UPCAST_COMPARISONS,
+ &types::LET_UNIT_VALUE,
+ &types::LINKEDLIST,
+ &types::OPTION_OPTION,
+ &types::REDUNDANT_ALLOCATION,
+ &types::TYPE_COMPLEXITY,
+ &types::UNIT_ARG,
+ &types::UNIT_CMP,
+ &types::UNNECESSARY_CAST,
+ &types::VEC_BOX,
+ &unicode::NON_ASCII_LITERAL,
+ &unicode::UNICODE_NOT_NFC,
+ &unicode::ZERO_WIDTH_SPACE,
+ &unnamed_address::FN_ADDRESS_COMPARISONS,
+ &unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+ &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
+ &unused_io_amount::UNUSED_IO_AMOUNT,
+ &unused_self::UNUSED_SELF,
+ &unwrap::PANICKING_UNWRAP,
+ &unwrap::UNNECESSARY_UNWRAP,
+ &use_self::USE_SELF,
+ &utils::internal_lints::CLIPPY_LINTS_INTERNAL,
+ &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
+ &utils::internal_lints::COMPILER_LINT_FUNCTIONS,
+ &utils::internal_lints::DEFAULT_LINT,
+ &utils::internal_lints::LINT_WITHOUT_LINT_PASS,
+ &utils::internal_lints::OUTER_EXPN_EXPN_DATA,
+ &utils::internal_lints::PRODUCE_ICE,
+ &vec::USELESS_VEC,
+ &verbose_file_reads::VERBOSE_FILE_READS,
+ &wildcard_dependencies::WILDCARD_DEPENDENCIES,
+ &wildcard_imports::ENUM_GLOB_USE,
+ &wildcard_imports::WILDCARD_IMPORTS,
+ &write::PRINTLN_EMPTY_STRING,
+ &write::PRINT_LITERAL,
+ &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,
+ ]);
+ // end register lints, do not remove this comment, it’s used in `update_lints`
+
+ store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock);
+ store.register_late_pass(|| box serde_api::SerdeAPI);
+ store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
+ store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
+ store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
+ store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
+ store.register_late_pass(|| box utils::author::Author);
+ let vec_box_size_threshold = conf.vec_box_size_threshold;
+ store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
+ store.register_late_pass(|| box booleans::NonminimalBool);
+ store.register_late_pass(|| box eq_op::EqOp);
+ store.register_late_pass(|| box enum_clike::UnportableVariant);
+ store.register_late_pass(|| box float_literal::FloatLiteral);
+ let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
+ store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold));
+ store.register_late_pass(|| box ptr::Ptr);
+ store.register_late_pass(|| box needless_bool::NeedlessBool);
+ store.register_late_pass(|| box needless_bool::BoolComparison);
+ store.register_late_pass(|| box approx_const::ApproxConstant);
+ store.register_late_pass(|| box misc::MiscLints);
+ store.register_late_pass(|| box eta_reduction::EtaReduction);
+ store.register_late_pass(|| box identity_op::IdentityOp);
+ store.register_late_pass(|| box erasing_op::ErasingOp);
+ store.register_late_pass(|| box mut_mut::MutMut);
+ store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed);
+ store.register_late_pass(|| box len_zero::LenZero);
+ store.register_late_pass(|| box attrs::Attributes);
+ store.register_late_pass(|| box block_in_if_condition::BlockInIfCondition);
+ store.register_late_pass(|| box unicode::Unicode);
+ store.register_late_pass(|| box strings::StringAdd);
+ store.register_late_pass(|| box implicit_return::ImplicitReturn);
+ store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
+ store.register_late_pass(|| box methods::Methods);
+ store.register_late_pass(|| box map_clone::MapClone);
+ store.register_late_pass(|| box shadow::Shadow);
+ store.register_late_pass(|| box types::LetUnitValue);
+ store.register_late_pass(|| box types::UnitCmp);
+ store.register_late_pass(|| box loops::Loops);
+ store.register_late_pass(|| box main_recursion::MainRecursion::default());
+ store.register_late_pass(|| box lifetimes::Lifetimes);
+ store.register_late_pass(|| box entry::HashMapPass);
+ store.register_late_pass(|| box ranges::Ranges);
+ store.register_late_pass(|| box types::Casts);
+ let type_complexity_threshold = conf.type_complexity_threshold;
+ store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
+ store.register_late_pass(|| box matches::Matches::default());
+ store.register_late_pass(|| box minmax::MinMaxPass);
+ store.register_late_pass(|| box open_options::OpenOptions);
+ store.register_late_pass(|| box zero_div_zero::ZeroDiv);
+ store.register_late_pass(|| box mutex_atomic::Mutex);
+ store.register_late_pass(|| box needless_update::NeedlessUpdate);
+ store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default());
+ store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef);
+ store.register_late_pass(|| box no_effect::NoEffect);
+ store.register_late_pass(|| box temporary_assignment::TemporaryAssignment);
+ store.register_late_pass(|| box transmute::Transmute);
+ let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
+ store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold));
+ let too_large_for_stack = conf.too_large_for_stack;
+ store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack});
+ store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented);
+ store.register_late_pass(|| box strings::StringLitAsBytes);
+ store.register_late_pass(|| box derive::Derive);
+ store.register_late_pass(|| box types::CharLitAsU8);
+ store.register_late_pass(|| box vec::UselessVec);
+ store.register_late_pass(|| box drop_bounds::DropBounds);
+ store.register_late_pass(|| box get_last_with_len::GetLastWithLen);
+ store.register_late_pass(|| box drop_forget_ref::DropForgetRef);
+ store.register_late_pass(|| box empty_enum::EmptyEnum);
+ store.register_late_pass(|| box types::AbsurdExtremeComparisons);
+ store.register_late_pass(|| box types::InvalidUpcastComparisons);
+ store.register_late_pass(|| box regex::Regex::default());
+ store.register_late_pass(|| box copies::CopyAndPaste);
+ store.register_late_pass(|| box copy_iterator::CopyIterator);
+ store.register_late_pass(|| box format::UselessFormat);
+ store.register_late_pass(|| box swap::Swap);
+ store.register_late_pass(|| box overflow_check_conditional::OverflowCheckConditional);
+ store.register_late_pass(|| box new_without_default::NewWithoutDefault::default());
+ let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone()));
+ let too_many_arguments_threshold1 = conf.too_many_arguments_threshold;
+ let too_many_lines_threshold2 = conf.too_many_lines_threshold;
+ store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold1, too_many_lines_threshold2));
+ let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone()));
+ store.register_late_pass(|| box neg_multiply::NegMultiply);
+ store.register_late_pass(|| box mem_discriminant::MemDiscriminant);
+ store.register_late_pass(|| box mem_forget::MemForget);
+ store.register_late_pass(|| box mem_replace::MemReplace);
+ store.register_late_pass(|| box arithmetic::Arithmetic::default());
+ store.register_late_pass(|| box assign_ops::AssignOps);
+ store.register_late_pass(|| box let_if_seq::LetIfSeq);
+ store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
+ store.register_late_pass(|| box missing_doc::MissingDoc::new());
+ store.register_late_pass(|| box missing_inline::MissingInline);
+ store.register_late_pass(|| box if_let_some_result::OkIfLet);
+ store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching);
+ store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
+ store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
+ let enum_variant_size_threshold = conf.enum_variant_size_threshold;
+ store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold));
+ store.register_late_pass(|| box explicit_write::ExplicitWrite);
+ store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue);
+ let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new(
+ conf.trivial_copy_size_limit,
+ &sess.target,
+ );
+ store.register_late_pass(move || box trivially_copy_pass_by_ref);
+ store.register_late_pass(|| box try_err::TryErr);
+ store.register_late_pass(|| box use_self::UseSelf);
+ store.register_late_pass(|| box bytecount::ByteCount);
+ store.register_late_pass(|| box infinite_iter::InfiniteIter);
+ store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
+ store.register_late_pass(|| box identity_conversion::IdentityConversion::default());
+ store.register_late_pass(|| box types::ImplicitHasher);
+ store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom);
+ store.register_late_pass(|| box types::UnitArg);
+ store.register_late_pass(|| box double_comparison::DoubleComparisons);
+ store.register_late_pass(|| box question_mark::QuestionMark);
+ store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl);
+ store.register_late_pass(|| box map_unit_fn::MapUnit);
+ store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default());
+ store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
+ store.register_late_pass(|| box unwrap::Unwrap);
+ store.register_late_pass(|| box duration_subsec::DurationSubsec);
+ store.register_late_pass(|| box default_trait_access::DefaultTraitAccess);
+ store.register_late_pass(|| box indexing_slicing::IndexingSlicing);
+ store.register_late_pass(|| box non_copy_const::NonCopyConst);
+ store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast);
+ store.register_late_pass(|| box redundant_clone::RedundantClone);
+ store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit);
+ store.register_late_pass(|| box types::RefToMut);
+ store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
+ store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
+ store.register_late_pass(|| box transmuting_null::TransmutingNull);
+ store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
+ store.register_late_pass(|| box checked_conversions::CheckedConversions);
+ store.register_late_pass(|| box integer_division::IntegerDivision);
+ store.register_late_pass(|| box inherent_to_string::InherentToString);
+ store.register_late_pass(|| box trait_bounds::TraitBounds);
+ store.register_late_pass(|| box comparison_chain::ComparisonChain);
+ store.register_late_pass(|| box mut_key::MutableKeyType);
+ store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic);
+ store.register_early_pass(|| box reference::DerefAddrOf);
+ store.register_early_pass(|| box reference::RefInDeref);
+ store.register_early_pass(|| box double_parens::DoubleParens);
+ store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval);
+ store.register_early_pass(|| box if_not_else::IfNotElse);
+ store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
+ store.register_early_pass(|| box int_plus_one::IntPlusOne);
+ store.register_early_pass(|| box formatting::Formatting);
+ store.register_early_pass(|| box misc_early::MiscEarlyLints);
+ store.register_early_pass(|| box returns::Return);
+ store.register_early_pass(|| box collapsible_if::CollapsibleIf);
+ store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
+ store.register_early_pass(|| box precedence::Precedence);
+ store.register_early_pass(|| box needless_continue::NeedlessContinue);
+ store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
+ store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
+ store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
+ store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
+ store.register_early_pass(|| box literal_representation::LiteralDigitGrouping);
+ let literal_representation_threshold = conf.literal_representation_threshold;
+ store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold));
+ store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
+ let enum_variant_name_threshold = conf.enum_variant_name_threshold;
+ store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
+ store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
+ store.register_late_pass(|| box unused_self::UnusedSelf);
+ store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
+ store.register_late_pass(|| box exit::Exit);
+ store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
+ let array_size_threshold = conf.array_size_threshold;
+ store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
+ store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
+ store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic);
+ store.register_early_pass(|| box as_conversions::AsConversions);
+ store.register_early_pass(|| box utils::internal_lints::ProduceIce);
+ store.register_late_pass(|| box let_underscore::LetUnderscore);
+ store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
+ store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports);
+ let max_fn_params_bools = conf.max_fn_params_bools;
+ let max_struct_bools = conf.max_struct_bools;
+ store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
+ store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
- LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
++ let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
++ store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports));
+ store.register_early_pass(|| box macro_use::MacroUseImports);
+ store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
+ store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
+ store.register_late_pass(|| box unnamed_address::UnnamedAddress);
+ store.register_late_pass(|| box dereference::Dereferencing);
+ store.register_late_pass(|| box future_not_send::FutureNotSend);
+ store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
+ store.register_late_pass(|| box if_let_mutex::IfLetMutex);
+ store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
++ store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
++ store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
+
+ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
+ LintId::of(&arithmetic::FLOAT_ARITHMETIC),
+ LintId::of(&arithmetic::INTEGER_ARITHMETIC),
+ LintId::of(&as_conversions::AS_CONVERSIONS),
+ LintId::of(&dbg_macro::DBG_MACRO),
+ LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+ LintId::of(&exit::EXIT),
+ LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
+ LintId::of(&implicit_return::IMPLICIT_RETURN),
+ LintId::of(&indexing_slicing::INDEXING_SLICING),
+ LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL),
+ LintId::of(&integer_division::INTEGER_DIVISION),
+ 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::WILDCARD_ENUM_MATCH_ARM),
+ LintId::of(&mem_forget::MEM_FORGET),
+ LintId::of(&methods::CLONE_ON_REF_PTR),
+ LintId::of(&methods::FILETYPE_IS_FILE),
+ LintId::of(&methods::GET_UNWRAP),
+ LintId::of(&methods::OPTION_EXPECT_USED),
+ LintId::of(&methods::OPTION_UNWRAP_USED),
+ LintId::of(&methods::RESULT_EXPECT_USED),
+ LintId::of(&methods::RESULT_UNWRAP_USED),
+ LintId::of(&methods::WRONG_PUB_SELF_CONVENTION),
+ LintId::of(&misc::FLOAT_CMP_CONST),
+ LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN),
+ LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
+ LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
+ LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC),
+ LintId::of(&panic_unimplemented::PANIC),
+ LintId::of(&panic_unimplemented::TODO),
+ LintId::of(&panic_unimplemented::UNIMPLEMENTED),
+ LintId::of(&panic_unimplemented::UNREACHABLE),
+ LintId::of(&shadow::SHADOW_REUSE),
+ LintId::of(&shadow::SHADOW_SAME),
+ LintId::of(&strings::STRING_ADD),
+ LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
+ LintId::of(&write::PRINT_STDOUT),
+ LintId::of(&write::USE_DEBUG),
+ ]);
+
+ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
+ LintId::of(&attrs::INLINE_ALWAYS),
+ LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
+ LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
+ LintId::of(&copies::MATCH_SAME_ARMS),
+ LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
+ LintId::of(©_iterator::COPY_ITERATOR),
+ LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
+ LintId::of(&dereference::EXPLICIT_DEREF_METHODS),
+ LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
+ LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE),
+ LintId::of(&doc::DOC_MARKDOWN),
+ LintId::of(&doc::MISSING_ERRORS_DOC),
+ LintId::of(&empty_enum::EMPTY_ENUM),
+ LintId::of(&enum_variants::MODULE_NAME_REPETITIONS),
+ LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES),
+ 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(&functions::MUST_USE_CANDIDATE),
+ LintId::of(&functions::TOO_MANY_LINES),
+ LintId::of(&if_not_else::IF_NOT_ELSE),
+ LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
+ LintId::of(&infinite_iter::MAYBE_INFINITE_ITER),
+ LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS),
+ LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS),
+ 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(¯o_use::MACRO_USE_IMPORTS),
++ LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
+ LintId::of(&matches::MATCH_BOOL),
+ LintId::of(&matches::SINGLE_MATCH_ELSE),
+ LintId::of(&methods::FILTER_MAP),
+ LintId::of(&methods::FILTER_MAP_NEXT),
+ LintId::of(&methods::FIND_MAP),
+ LintId::of(&methods::INEFFICIENT_TO_STRING),
+ LintId::of(&methods::MAP_FLATTEN),
+ LintId::of(&methods::OPTION_MAP_UNWRAP_OR),
+ LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE),
+ LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE),
+ LintId::of(&misc::USED_UNDERSCORE_BINDING),
+ LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX),
+ LintId::of(&mut_mut::MUT_MUT),
+ LintId::of(&needless_continue::NEEDLESS_CONTINUE),
+ LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
+ LintId::of(&non_expressive_names::SIMILAR_NAMES),
+ LintId::of(&ranges::RANGE_PLUS_ONE),
+ LintId::of(&shadow::SHADOW_UNRELATED),
+ LintId::of(&strings::STRING_ADD_ASSIGN),
+ LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
+ LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
+ LintId::of(&types::CAST_LOSSLESS),
+ LintId::of(&types::CAST_POSSIBLE_TRUNCATION),
+ LintId::of(&types::CAST_POSSIBLE_WRAP),
+ LintId::of(&types::CAST_PRECISION_LOSS),
+ LintId::of(&types::CAST_SIGN_LOSS),
+ LintId::of(&types::IMPLICIT_HASHER),
+ LintId::of(&types::INVALID_UPCAST_COMPARISONS),
+ LintId::of(&types::LET_UNIT_VALUE),
+ LintId::of(&types::LINKEDLIST),
+ LintId::of(&types::OPTION_OPTION),
+ LintId::of(&unicode::NON_ASCII_LITERAL),
+ LintId::of(&unicode::UNICODE_NOT_NFC),
+ LintId::of(&unused_self::UNUSED_SELF),
+ LintId::of(&wildcard_imports::ENUM_GLOB_USE),
+ LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
+ ]);
+
+ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
+ LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL),
+ LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
+ LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
+ LintId::of(&utils::internal_lints::DEFAULT_LINT),
+ LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
+ LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
+ LintId::of(&utils::internal_lints::PRODUCE_ICE),
+ ]);
+
+ store.register_group(true, "clippy::all", Some("clippy"), vec![
+ LintId::of(&approx_const::APPROX_CONSTANT),
+ LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
+ LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
+ LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
+ LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
+ LintId::of(&attrs::DEPRECATED_CFG_ATTR),
+ LintId::of(&attrs::DEPRECATED_SEMVER),
+ LintId::of(&attrs::MISMATCHED_TARGET_OS),
+ LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
+ LintId::of(&attrs::USELESS_ATTRIBUTE),
+ LintId::of(&bit_mask::BAD_BIT_MASK),
+ LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
+ LintId::of(&bit_mask::VERBOSE_BIT_MASK),
+ LintId::of(&blacklisted_name::BLACKLISTED_NAME),
+ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR),
+ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT),
+ LintId::of(&booleans::LOGIC_BUG),
+ LintId::of(&booleans::NONMINIMAL_BOOL),
+ LintId::of(&bytecount::NAIVE_BYTECOUNT),
+ 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(&derive::DERIVE_HASH_XOR_EQ),
+ LintId::of(&doc::MISSING_SAFETY_DOC),
+ LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
+ LintId::of(&double_comparison::DOUBLE_COMPARISONS),
+ LintId::of(&double_parens::DOUBLE_PARENS),
+ LintId::of(&drop_bounds::DROP_BOUNDS),
+ 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(&duration_subsec::DURATION_SUBSEC),
+ 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(&eq_op::EQ_OP),
+ LintId::of(&eq_op::OP_REF),
+ LintId::of(&erasing_op::ERASING_OP),
+ LintId::of(&escape::BOXED_LOCAL),
+ LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
+ LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
+ LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
+ LintId::of(&explicit_write::EXPLICIT_WRITE),
+ LintId::of(&float_literal::EXCESSIVE_PRECISION),
+ LintId::of(&format::USELESS_FORMAT),
+ 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(&functions::DOUBLE_MUST_USE),
+ LintId::of(&functions::MUST_USE_UNIT),
+ LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
+ LintId::of(&functions::TOO_MANY_ARGUMENTS),
+ LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
+ LintId::of(&identity_conversion::IDENTITY_CONVERSION),
+ LintId::of(&identity_op::IDENTITY_OP),
+ LintId::of(&if_let_mutex::IF_LET_MUTEX),
+ LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
+ 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(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
+ LintId::of(&int_plus_one::INT_PLUS_ONE),
+ LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
+ LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
+ LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
+ LintId::of(&len_zero::LEN_ZERO),
+ LintId::of(&let_if_seq::USELESS_LET_IF_SEQ),
+ 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(&loops::EMPTY_LOOP),
+ LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
+ LintId::of(&loops::FOR_KV_MAP),
+ LintId::of(&loops::FOR_LOOP_OVER_OPTION),
+ LintId::of(&loops::FOR_LOOP_OVER_RESULT),
+ LintId::of(&loops::ITER_NEXT_LOOP),
+ LintId::of(&loops::MANUAL_MEMCPY),
+ 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::REVERSE_RANGE_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_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
+ LintId::of(&map_clone::MAP_CLONE),
+ LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
+ LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
- LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
+ LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
+ LintId::of(&matches::MATCH_AS_REF),
+ LintId::of(&matches::MATCH_OVERLAPPING_ARM),
+ LintId::of(&matches::MATCH_REF_PATS),
+ LintId::of(&matches::MATCH_SINGLE_BINDING),
+ LintId::of(&matches::MATCH_WILD_ERR_ARM),
+ LintId::of(&matches::SINGLE_MATCH),
+ LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
+ LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
+ 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::CHARS_LAST_CMP),
+ LintId::of(&methods::CHARS_NEXT_CMP),
+ LintId::of(&methods::CLONE_DOUBLE_REF),
+ LintId::of(&methods::CLONE_ON_COPY),
+ LintId::of(&methods::EXPECT_FUN_CALL),
+ LintId::of(&methods::FILTER_NEXT),
+ LintId::of(&methods::FLAT_MAP_IDENTITY),
+ LintId::of(&methods::INTO_ITER_ON_REF),
+ LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
+ LintId::of(&methods::ITER_CLONED_COLLECT),
+ LintId::of(&methods::ITER_NTH),
+ LintId::of(&methods::ITER_NTH_ZERO),
+ LintId::of(&methods::ITER_SKIP_NEXT),
+ LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
+ LintId::of(&methods::NEW_RET_NO_SELF),
+ LintId::of(&methods::OK_EXPECT),
+ LintId::of(&methods::OPTION_AND_THEN_SOME),
+ LintId::of(&methods::OPTION_AS_REF_DEREF),
+ LintId::of(&methods::OPTION_MAP_OR_NONE),
+ LintId::of(&methods::OR_FUN_CALL),
+ 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_PATTERN),
+ LintId::of(&methods::SKIP_WHILE_NEXT),
+ LintId::of(&methods::STRING_EXTEND_CHARS),
+ LintId::of(&methods::SUSPICIOUS_MAP),
+ LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
+ LintId::of(&methods::UNINIT_ASSUMED_INIT),
+ LintId::of(&methods::UNNECESSARY_FILTER_MAP),
+ LintId::of(&methods::UNNECESSARY_FOLD),
+ LintId::of(&methods::USELESS_ASREF),
+ LintId::of(&methods::WRONG_SELF_CONVENTION),
+ LintId::of(&methods::ZST_OFFSET),
+ LintId::of(&minmax::MIN_MAX),
+ LintId::of(&misc::CMP_NAN),
+ LintId::of(&misc::CMP_OWNED),
+ LintId::of(&misc::FLOAT_CMP),
+ LintId::of(&misc::MODULO_ONE),
+ 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_CLOSURE_CALL),
+ LintId::of(&misc_early::REDUNDANT_PATTERN),
+ LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
+ LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
+ LintId::of(&mut_key::MUTABLE_KEY_TYPE),
+ LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
+ LintId::of(&mutex_atomic::MUTEX_ATOMIC),
+ LintId::of(&needless_bool::BOOL_COMPARISON),
+ LintId::of(&needless_bool::NEEDLESS_BOOL),
+ LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+ 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_expressive_names::MANY_SINGLE_CHAR_NAMES),
+ LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
+ LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
+ LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
+ LintId::of(&panic_unimplemented::PANIC_PARAMS),
+ LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
+ LintId::of(&precedence::PRECEDENCE),
+ LintId::of(&ptr::CMP_NULL),
+ 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::RANGE_MINUS_ONE),
+ LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
+ LintId::of(&redundant_clone::REDUNDANT_CLONE),
+ LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
+ LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
+ LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
+ LintId::of(&reference::DEREF_ADDROF),
+ LintId::of(&reference::REF_IN_DEREF),
+ LintId::of(®ex::INVALID_REGEX),
+ LintId::of(®ex::REGEX_MACRO),
+ LintId::of(®ex::TRIVIAL_REGEX),
+ LintId::of(&returns::LET_AND_RETURN),
+ LintId::of(&returns::NEEDLESS_RETURN),
+ LintId::of(&returns::UNUSED_UNIT),
+ LintId::of(&serde_api::SERDE_API_MISUSE),
+ LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
+ LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
+ LintId::of(&strings::STRING_LIT_AS_BYTES),
+ 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(&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::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_PTR_TO_PTR),
+ LintId::of(&transmute::TRANSMUTE_PTR_TO_REF),
+ LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
+ LintId::of(&transmute::WRONG_TRANSMUTE),
+ LintId::of(&transmuting_null::TRANSMUTING_NULL),
+ LintId::of(&try_err::TRY_ERR),
+ LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
+ LintId::of(&types::BORROWED_BOX),
+ LintId::of(&types::BOX_VEC),
+ LintId::of(&types::CAST_PTR_ALIGNMENT),
+ LintId::of(&types::CAST_REF_TO_MUT),
+ LintId::of(&types::CHAR_LIT_AS_U8),
+ LintId::of(&types::FN_TO_NUMERIC_CAST),
+ LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
+ LintId::of(&types::REDUNDANT_ALLOCATION),
+ LintId::of(&types::TYPE_COMPLEXITY),
+ LintId::of(&types::UNIT_ARG),
+ LintId::of(&types::UNIT_CMP),
+ LintId::of(&types::UNNECESSARY_CAST),
+ LintId::of(&types::VEC_BOX),
+ LintId::of(&unicode::ZERO_WIDTH_SPACE),
+ LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
+ LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
+ LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
+ LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
+ LintId::of(&unwrap::PANICKING_UNWRAP),
+ LintId::of(&unwrap::UNNECESSARY_UNWRAP),
+ LintId::of(&vec::USELESS_VEC),
+ 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),
+ ]);
+
+ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
+ LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
+ LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
+ LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
+ LintId::of(&bit_mask::VERBOSE_BIT_MASK),
+ LintId::of(&blacklisted_name::BLACKLISTED_NAME),
+ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR),
+ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT),
+ LintId::of(&collapsible_if::COLLAPSIBLE_IF),
+ LintId::of(&comparison_chain::COMPARISON_CHAIN),
+ 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(&eq_op::OP_REF),
+ LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
+ LintId::of(&float_literal::EXCESSIVE_PRECISION),
+ LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
+ LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
+ LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
+ LintId::of(&functions::DOUBLE_MUST_USE),
+ LintId::of(&functions::MUST_USE_UNIT),
+ LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
+ LintId::of(&inherent_to_string::INHERENT_TO_STRING),
+ LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
+ LintId::of(&len_zero::LEN_ZERO),
+ LintId::of(&let_if_seq::USELESS_LET_IF_SEQ),
+ LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
+ LintId::of(&loops::EMPTY_LOOP),
+ LintId::of(&loops::FOR_KV_MAP),
+ LintId::of(&loops::NEEDLESS_RANGE_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_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
+ LintId::of(&map_clone::MAP_CLONE),
+ LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
+ LintId::of(&matches::MATCH_OVERLAPPING_ARM),
+ LintId::of(&matches::MATCH_REF_PATS),
+ LintId::of(&matches::MATCH_WILD_ERR_ARM),
+ 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::CHARS_LAST_CMP),
+ LintId::of(&methods::CHARS_NEXT_CMP),
+ LintId::of(&methods::INTO_ITER_ON_REF),
+ LintId::of(&methods::ITER_CLONED_COLLECT),
+ LintId::of(&methods::ITER_NTH_ZERO),
+ LintId::of(&methods::ITER_SKIP_NEXT),
+ LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
+ LintId::of(&methods::NEW_RET_NO_SELF),
+ 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::STRING_EXTEND_CHARS),
+ LintId::of(&methods::UNNECESSARY_FOLD),
+ 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(&neg_multiply::NEG_MULTIPLY),
+ LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
+ LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
+ LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES),
+ LintId::of(&panic_unimplemented::PANIC_PARAMS),
+ LintId::of(&ptr::CMP_NULL),
+ LintId::of(&ptr::PTR_ARG),
+ LintId::of(&question_mark::QUESTION_MARK),
+ LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
+ LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
+ LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
+ LintId::of(®ex::REGEX_MACRO),
+ LintId::of(®ex::TRIVIAL_REGEX),
+ LintId::of(&returns::LET_AND_RETURN),
+ LintId::of(&returns::NEEDLESS_RETURN),
+ LintId::of(&returns::UNUSED_UNIT),
+ LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
+ LintId::of(&strings::STRING_LIT_AS_BYTES),
+ LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
+ LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
+ LintId::of(&try_err::TRY_ERR),
+ LintId::of(&types::FN_TO_NUMERIC_CAST),
+ LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
+ LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
+ 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),
+ ]);
+
+ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
+ LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
+ LintId::of(&attrs::DEPRECATED_CFG_ATTR),
+ LintId::of(&booleans::NONMINIMAL_BOOL),
+ LintId::of(&double_comparison::DOUBLE_COMPARISONS),
+ LintId::of(&double_parens::DOUBLE_PARENS),
+ LintId::of(&duration_subsec::DURATION_SUBSEC),
+ LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
+ LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
+ LintId::of(&explicit_write::EXPLICIT_WRITE),
+ LintId::of(&format::USELESS_FORMAT),
+ LintId::of(&functions::TOO_MANY_ARGUMENTS),
+ LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
+ LintId::of(&identity_conversion::IDENTITY_CONVERSION),
+ LintId::of(&identity_op::IDENTITY_OP),
+ 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::MUT_RANGE_BOUND),
+ LintId::of(&loops::WHILE_LET_LOOP),
+ LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
+ LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
+ LintId::of(&matches::MATCH_AS_REF),
+ LintId::of(&matches::MATCH_SINGLE_BINDING),
+ LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
+ LintId::of(&methods::CLONE_ON_COPY),
+ LintId::of(&methods::FILTER_NEXT),
+ LintId::of(&methods::FLAT_MAP_IDENTITY),
+ LintId::of(&methods::OPTION_AND_THEN_SOME),
+ LintId::of(&methods::OPTION_AS_REF_DEREF),
+ LintId::of(&methods::SEARCH_IS_SOME),
+ LintId::of(&methods::SKIP_WHILE_NEXT),
+ LintId::of(&methods::SUSPICIOUS_MAP),
+ LintId::of(&methods::UNNECESSARY_FILTER_MAP),
+ LintId::of(&methods::USELESS_ASREF),
+ LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
+ LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL),
+ LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
+ LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
+ LintId::of(&needless_bool::BOOL_COMPARISON),
+ LintId::of(&needless_bool::NEEDLESS_BOOL),
+ LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+ 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(&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(&ranges::RANGE_MINUS_ONE),
+ LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
+ LintId::of(&reference::DEREF_ADDROF),
+ LintId::of(&reference::REF_IN_DEREF),
+ LintId::of(&swap::MANUAL_SWAP),
+ LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
+ LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
+ 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_PTR_TO_PTR),
+ LintId::of(&transmute::TRANSMUTE_PTR_TO_REF),
+ LintId::of(&types::BORROWED_BOX),
+ LintId::of(&types::CHAR_LIT_AS_U8),
+ LintId::of(&types::TYPE_COMPLEXITY),
+ LintId::of(&types::UNIT_ARG),
+ LintId::of(&types::UNNECESSARY_CAST),
+ LintId::of(&types::VEC_BOX),
+ LintId::of(&unwrap::UNNECESSARY_UNWRAP),
+ LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
+ ]);
+
+ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
+ LintId::of(&approx_const::APPROX_CONSTANT),
+ LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
+ LintId::of(&attrs::DEPRECATED_SEMVER),
+ LintId::of(&attrs::MISMATCHED_TARGET_OS),
+ LintId::of(&attrs::USELESS_ATTRIBUTE),
+ LintId::of(&bit_mask::BAD_BIT_MASK),
+ LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
+ LintId::of(&booleans::LOGIC_BUG),
+ LintId::of(&copies::IFS_SAME_COND),
+ LintId::of(&copies::IF_SAME_THEN_ELSE),
+ LintId::of(&derive::DERIVE_HASH_XOR_EQ),
+ LintId::of(&drop_bounds::DROP_BOUNDS),
+ 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(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
+ LintId::of(&eq_op::EQ_OP),
+ LintId::of(&erasing_op::ERASING_OP),
+ 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(&let_underscore::LET_UNDERSCORE_LOCK),
+ LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
+ LintId::of(&loops::FOR_LOOP_OVER_OPTION),
+ LintId::of(&loops::FOR_LOOP_OVER_RESULT),
+ LintId::of(&loops::ITER_NEXT_LOOP),
+ LintId::of(&loops::NEVER_LOOP),
+ LintId::of(&loops::REVERSE_RANGE_LOOP),
+ LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
+ LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
+ LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT),
+ LintId::of(&methods::CLONE_DOUBLE_REF),
+ LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
+ LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
+ LintId::of(&methods::UNINIT_ASSUMED_INIT),
+ LintId::of(&methods::ZST_OFFSET),
+ LintId::of(&minmax::MIN_MAX),
+ LintId::of(&misc::CMP_NAN),
+ LintId::of(&misc::FLOAT_CMP),
+ LintId::of(&misc::MODULO_ONE),
+ LintId::of(&mut_key::MUTABLE_KEY_TYPE),
+ LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
+ LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
+ LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
+ LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
+ LintId::of(&ptr::MUT_FROM_REF),
+ LintId::of(®ex::INVALID_REGEX),
+ LintId::of(&serde_api::SERDE_API_MISUSE),
+ LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
+ LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
+ LintId::of(&swap::ALMOST_SWAPPED),
+ LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
+ LintId::of(&transmute::WRONG_TRANSMUTE),
+ LintId::of(&transmuting_null::TRANSMUTING_NULL),
+ LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
+ LintId::of(&types::CAST_PTR_ALIGNMENT),
+ LintId::of(&types::CAST_REF_TO_MUT),
+ LintId::of(&types::UNIT_CMP),
+ LintId::of(&unicode::ZERO_WIDTH_SPACE),
+ 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),
+ ]);
+
+ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
+ LintId::of(&bytecount::NAIVE_BYTECOUNT),
+ LintId::of(&entry::MAP_ENTRY),
+ LintId::of(&escape::BOXED_LOCAL),
+ LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
+ LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
+ LintId::of(&loops::MANUAL_MEMCPY),
+ LintId::of(&loops::NEEDLESS_COLLECT),
+ LintId::of(&methods::EXPECT_FUN_CALL),
+ LintId::of(&methods::ITER_NTH),
+ LintId::of(&methods::OR_FUN_CALL),
+ LintId::of(&methods::SINGLE_CHAR_PATTERN),
+ LintId::of(&misc::CMP_OWNED),
+ LintId::of(&mutex_atomic::MUTEX_ATOMIC),
+ LintId::of(&redundant_clone::REDUNDANT_CLONE),
+ LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
+ LintId::of(&types::BOX_VEC),
+ LintId::of(&types::REDUNDANT_ALLOCATION),
+ LintId::of(&vec::USELESS_VEC),
+ ]);
+
+ store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
+ LintId::of(&cargo_common_metadata::CARGO_COMMON_METADATA),
+ LintId::of(&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
+ LintId::of(&wildcard_dependencies::WILDCARD_DEPENDENCIES),
+ ]);
+
+ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
+ LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
+ LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY),
+ 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(&missing_const_for_fn::MISSING_CONST_FOR_FN),
+ LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
+ LintId::of(&mutex_atomic::MUTEX_INTEGER),
+ LintId::of(&needless_borrow::NEEDLESS_BORROW),
+ LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
+ LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE),
+ LintId::of(&transmute::USELESS_TRANSMUTE),
+ LintId::of(&use_self::USE_SELF),
+ ]);
+}
+
+#[rustfmt::skip]
+fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
+ store.register_removed(
+ "should_assert_eq",
+ "`assert!()` will be more flexible with RFC 2011",
+ );
+ store.register_removed(
+ "extend_from_slice",
+ "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
+ );
+ store.register_removed(
+ "range_step_by_zero",
+ "`iterator.step_by(0)` panics nowadays",
+ );
+ store.register_removed(
+ "unstable_as_slice",
+ "`Vec::as_slice` has been stabilized in 1.7",
+ );
+ store.register_removed(
+ "unstable_as_mut_slice",
+ "`Vec::as_mut_slice` has been stabilized in 1.7",
+ );
+ store.register_removed(
+ "str_to_string",
+ "using `str::to_string` is common even today and specialization will likely happen soon",
+ );
+ store.register_removed(
+ "string_to_string",
+ "using `string::to_string` is common even today and specialization will likely happen soon",
+ );
+ store.register_removed(
+ "misaligned_transmute",
+ "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
+ );
+ store.register_removed(
+ "assign_ops",
+ "using compound assignment operators (e.g., `+=`) is harmless",
+ );
+ store.register_removed(
+ "if_let_redundant_pattern_matching",
+ "this lint has been changed to redundant_pattern_matching",
+ );
+ store.register_removed(
+ "unsafe_vector_initialization",
+ "the replacement suggested by this lint had substantially different behavior",
+ );
+}
+
+/// Register renamed lints.
+///
+/// Used in `./src/driver.rs`.
+pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
+ ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
+ ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
+ ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
+ ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
+}
+
+// only exists to let the dogfood integration test works.
+// Don't run clippy as an executable directly
+#[allow(dead_code)]
+fn main() {
+ panic!("Please use the cargo-clippy executable");
+}
--- /dev/null
- use itertools::Itertools;
+use crate::consts::{constant, Constant};
+use crate::reexport::Name;
+use crate::utils::paths;
+use crate::utils::usage::{is_unused, mutated_variables};
+use crate::utils::{
+ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
+ is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
+ multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
+ span_lint_and_sugg, span_lint_and_then, SpanlessEq,
+};
+use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg};
+use if_chain::if_chain;
- if let ExprKind::Path(ref qpath) = expr.kind;
- if let QPath::Resolved(None, ref path) = *qpath;
+use rustc_ast::ast;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
+use rustc_hir::{
+ def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, LoopSource,
+ MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
+};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::middle::region;
+use rustc_middle::ty::{self, Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::BytePos;
+use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
+use std::iter::{once, Iterator};
+use std::mem;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for for-loops that manually copy items between
+ /// slices that could be optimized by having a memcpy.
+ ///
+ /// **Why is this bad?** It is not as fast as a memcpy.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let src = vec![1];
+ /// # let mut dst = vec![0; 65];
+ /// for i in 0..src.len() {
+ /// dst[i + 64] = src[i];
+ /// }
+ /// ```
+ /// Could be written as:
+ /// ```rust
+ /// # let src = vec![1];
+ /// # let mut dst = vec![0; 65];
+ /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
+ /// ```
+ pub MANUAL_MEMCPY,
+ perf,
+ "manually copying items between slices"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for looping over the range of `0..len` of some
+ /// collection just to get the values by index.
+ ///
+ /// **Why is this bad?** Just iterating the collection itself makes the intent
+ /// more clear and is probably faster.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let vec = vec!['a', 'b', 'c'];
+ /// for i in 0..vec.len() {
+ /// println!("{}", vec[i]);
+ /// }
+ /// ```
+ /// Could be written as:
+ /// ```rust
+ /// let vec = vec!['a', 'b', 'c'];
+ /// for i in vec {
+ /// println!("{}", i);
+ /// }
+ /// ```
+ pub NEEDLESS_RANGE_LOOP,
+ style,
+ "for-looping over a range of indices where an iterator over items would do"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for loops on `x.iter()` where `&x` will do, and
+ /// suggests the latter.
+ ///
+ /// **Why is this bad?** Readability.
+ ///
+ /// **Known problems:** False negatives. We currently only warn on some known
+ /// types.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// // with `y` a `Vec` or slice:
+ /// # let y = vec![1];
+ /// for x in y.iter() {
+ /// // ..
+ /// }
+ /// ```
+ /// can be rewritten to
+ /// ```rust
+ /// # let y = vec![1];
+ /// for x in &y {
+ /// // ..
+ /// }
+ /// ```
+ pub EXPLICIT_ITER_LOOP,
+ pedantic,
+ "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and
+ /// suggests the latter.
+ ///
+ /// **Why is this bad?** Readability.
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let y = vec![1];
+ /// // with `y` a `Vec` or slice:
+ /// for x in y.into_iter() {
+ /// // ..
+ /// }
+ /// ```
+ /// can be rewritten to
+ /// ```rust
+ /// # let y = vec![1];
+ /// for x in y {
+ /// // ..
+ /// }
+ /// ```
+ pub EXPLICIT_INTO_ITER_LOOP,
+ pedantic,
+ "for-looping over `_.into_iter()` when `_` would do"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for loops on `x.next()`.
+ ///
+ /// **Why is this bad?** `next()` returns either `Some(value)` if there was a
+ /// value, or `None` otherwise. The insidious thing is that `Option<_>`
+ /// implements `IntoIterator`, so that possibly one value will be iterated,
+ /// leading to some hard to find bugs. No one will want to write such code
+ /// [except to win an Underhanded Rust
+ /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// for x in y.next() {
+ /// ..
+ /// }
+ /// ```
+ pub ITER_NEXT_LOOP,
+ correctness,
+ "for-looping over `_.next()` which is probably not intended"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `for` loops over `Option` values.
+ ///
+ /// **Why is this bad?** Readability. This is more clearly expressed as an `if
+ /// let`.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// for x in option {
+ /// ..
+ /// }
+ /// ```
+ ///
+ /// This should be
+ /// ```ignore
+ /// if let Some(x) = option {
+ /// ..
+ /// }
+ /// ```
+ pub FOR_LOOP_OVER_OPTION,
+ correctness,
+ "for-looping over an `Option`, which is more clearly expressed as an `if let`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `for` loops over `Result` values.
+ ///
+ /// **Why is this bad?** Readability. This is more clearly expressed as an `if
+ /// let`.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// for x in result {
+ /// ..
+ /// }
+ /// ```
+ ///
+ /// This should be
+ /// ```ignore
+ /// if let Ok(x) = result {
+ /// ..
+ /// }
+ /// ```
+ pub FOR_LOOP_OVER_RESULT,
+ correctness,
+ "for-looping over a `Result`, which is more clearly expressed as an `if let`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Detects `loop + match` combinations that are easier
+ /// written as a `while let` loop.
+ ///
+ /// **Why is this bad?** The `while let` loop is usually shorter and more
+ /// readable.
+ ///
+ /// **Known problems:** Sometimes the wrong binding is displayed (#383).
+ ///
+ /// **Example:**
+ /// ```rust,no_run
+ /// # let y = Some(1);
+ /// loop {
+ /// let x = match y {
+ /// Some(x) => x,
+ /// None => break,
+ /// };
+ /// // .. do something with x
+ /// }
+ /// // is easier written as
+ /// while let Some(x) = y {
+ /// // .. do something with x
+ /// };
+ /// ```
+ pub WHILE_LET_LOOP,
+ complexity,
+ "`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.
+ ///
+ /// **Known problems:**
+ /// None
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let iterator = vec![1].into_iter();
+ /// let len = iterator.clone().collect::<Vec<_>>().len();
+ /// // should be
+ /// let len = iterator.count();
+ /// ```
+ pub NEEDLESS_COLLECT,
+ perf,
+ "collecting an iterator when collect is not needed"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for loops over ranges `x..y` where both `x` and `y`
+ /// are constant and `x` is greater or equal to `y`, unless the range is
+ /// reversed or has a negative `.step_by(_)`.
+ ///
+ /// **Why is it bad?** Such loops will either be skipped or loop until
+ /// wrap-around (in debug code, this may `panic!()`). Both options are probably
+ /// not intended.
+ ///
+ /// **Known problems:** The lint cannot catch loops over dynamically defined
+ /// ranges. Doing this would require simulating all possible inputs and code
+ /// paths through the program, which would be complex and error-prone.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// for x in 5..10 - 5 {
+ /// ..
+ /// } // oops, stray `-`
+ /// ```
+ pub REVERSE_RANGE_LOOP,
+ correctness,
+ "iteration over an empty range, such as `10..0` or `5..5`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks `for` loops over slices with an explicit counter
+ /// and suggests the use of `.enumerate()`.
+ ///
+ /// **Why is it bad?** Using `.enumerate()` makes the intent more clear,
+ /// declutters the code and may be faster in some instances.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let v = vec![1];
+ /// # fn bar(bar: usize, baz: usize) {}
+ /// let mut i = 0;
+ /// for item in &v {
+ /// bar(i, *item);
+ /// i += 1;
+ /// }
+ /// ```
+ /// Could be written as
+ /// ```rust
+ /// # let v = vec![1];
+ /// # fn bar(bar: usize, baz: usize) {}
+ /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
+ /// ```
+ pub EXPLICIT_COUNTER_LOOP,
+ complexity,
+ "for-looping with an explicit counter when `_.enumerate()` would do"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for empty `loop` expressions.
+ ///
+ /// **Why is this bad?** Those busy loops burn CPU cycles without doing
+ /// anything. Think of the environment and either block on something or at least
+ /// make the thread sleep for some microseconds.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```no_run
+ /// loop {}
+ /// ```
+ pub EMPTY_LOOP,
+ style,
+ "empty `loop {}`, which should block or sleep"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `while let` expressions on iterators.
+ ///
+ /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys
+ /// the intent better.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// while let Some(val) = iter() {
+ /// ..
+ /// }
+ /// ```
+ pub WHILE_LET_ON_ITERATOR,
+ style,
+ "using a while-let loop instead of a for loop on an iterator"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and
+ /// ignoring either the keys or values.
+ ///
+ /// **Why is this bad?** Readability. There are `keys` and `values` methods that
+ /// can be used to express that don't need the values or keys.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// for (k, _) in &map {
+ /// ..
+ /// }
+ /// ```
+ ///
+ /// could be replaced by
+ ///
+ /// ```ignore
+ /// for k in map.keys() {
+ /// ..
+ /// }
+ /// ```
+ pub FOR_KV_MAP,
+ style,
+ "looping on a map using `iter` when `keys` or `values` would do"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for loops that will always `break`, `return` or
+ /// `continue` an outer loop.
+ ///
+ /// **Why is this bad?** This loop never loops, all it does is obfuscating the
+ /// code.
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// ```rust
+ /// loop {
+ /// ..;
+ /// break;
+ /// }
+ /// ```
+ pub NEVER_LOOP,
+ correctness,
+ "any loop that will always `break` or `return`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for loops which have a range bound that is a mutable variable
+ ///
+ /// **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let mut foo = 42;
+ /// for i in 0..foo {
+ /// foo -= 1;
+ /// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
+ /// }
+ /// ```
+ pub MUT_RANGE_BOUND,
+ complexity,
+ "for loop over a range where one of the bounds is a mutable variable"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks whether variables used within while loop condition
+ /// can be (and are) mutated in the body.
+ ///
+ /// **Why is this bad?** If the condition is unchanged, entering the body of the loop
+ /// will lead to an infinite loop.
+ ///
+ /// **Known problems:** If the `while`-loop is in a closure, the check for mutation of the
+ /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
+ /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let i = 0;
+ /// while i > 10 {
+ /// println!("let me loop forever!");
+ /// }
+ /// ```
+ pub WHILE_IMMUTABLE_CONDITION,
+ correctness,
+ "variables used within while expression are not mutated in the body"
+}
+
+declare_lint_pass!(Loops => [
+ MANUAL_MEMCPY,
+ NEEDLESS_RANGE_LOOP,
+ EXPLICIT_ITER_LOOP,
+ EXPLICIT_INTO_ITER_LOOP,
+ ITER_NEXT_LOOP,
+ FOR_LOOP_OVER_RESULT,
+ FOR_LOOP_OVER_OPTION,
+ WHILE_LET_LOOP,
+ NEEDLESS_COLLECT,
+ REVERSE_RANGE_LOOP,
+ EXPLICIT_COUNTER_LOOP,
+ EMPTY_LOOP,
+ WHILE_LET_ON_ITERATOR,
+ FOR_KV_MAP,
+ NEVER_LOOP,
+ MUT_RANGE_BOUND,
+ WHILE_IMMUTABLE_CONDITION,
+]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Loops {
+ #[allow(clippy::too_many_lines)]
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
+ if let Some((pat, arg, body)) = higher::for_loop(expr) {
+ // we don't want to check expanded macros
+ // this check is not at the top of the function
+ // since higher::for_loop expressions are marked as expansions
+ if body.span.from_expansion() {
+ return;
+ }
+ check_for_loop(cx, pat, arg, body, expr);
+ }
+
+ // we don't want to check expanded macros
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ // check for never_loop
+ if let ExprKind::Loop(ref block, _, _) = expr.kind {
+ match never_loop_block(block, expr.hir_id) {
+ NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"),
+ NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
+ }
+ }
+
+ // check for `loop { if let {} else break }` that could be `while let`
+ // (also matches an explicit "match" instead of "if let")
+ // (even if the "match" or "if let" is used for declaration)
+ if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
+ // also check for empty `loop {}` statements
+ if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
+ span_lint(
+ cx,
+ EMPTY_LOOP,
+ expr.span,
+ "empty `loop {}` detected. You may want to either use `panic!()` or add \
+ `std::thread::sleep(..);` to the loop body.",
+ );
+ }
+
+ // extract the expression from the first statement (if any) in a block
+ let inner_stmt_expr = extract_expr_from_first_stmt(block);
+ // or extract the first expression (if any) from the block
+ if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) {
+ if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind {
+ // ensure "if let" compatible match structure
+ match *source {
+ MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
+ if arms.len() == 2
+ && arms[0].guard.is_none()
+ && arms[1].guard.is_none()
+ && is_simple_break_expr(&arms[1].body)
+ {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+
+ // NOTE: we used to build a body here instead of using
+ // ellipsis, this was removed because:
+ // 1) it was ugly with big bodies;
+ // 2) it was not indented properly;
+ // 3) it wasn’t very smart (see #675).
+ let mut applicability = Applicability::HasPlaceholders;
+ span_lint_and_sugg(
+ cx,
+ WHILE_LET_LOOP,
+ expr.span,
+ "this loop could be written as a `while let` loop",
+ "try",
+ format!(
+ "while let {} = {} {{ .. }}",
+ snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability),
+ snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability),
+ ),
+ applicability,
+ );
+ }
+ },
+ _ => (),
+ }
+ }
+ }
+ }
+ if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind {
+ let pat = &arms[0].pat.kind;
+ if let (
+ &PatKind::TupleStruct(ref qpath, ref pat_args, _),
+ &ExprKind::MethodCall(ref method_path, _, ref method_args),
+ ) = (pat, &match_expr.kind)
+ {
+ let iter_expr = &method_args[0];
+
+ // Don't lint when the iterator is recreated on every iteration
+ if_chain! {
+ if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind;
+ if let Some(iter_def_id) = get_trait_def_id(cx, &paths::ITERATOR);
+ if implements_trait(cx, cx.tables.expr_ty(iter_expr), iter_def_id, &[]);
+ then {
+ return;
+ }
+ }
+
+ let lhs_constructor = last_path_segment(qpath);
+ if method_path.ident.name == sym!(next)
+ && match_trait_method(cx, match_expr, &paths::ITERATOR)
+ && lhs_constructor.ident.name == sym!(Some)
+ && (pat_args.is_empty()
+ || !is_refutable(cx, &pat_args[0])
+ && !is_used_inside(cx, iter_expr, &arms[0].body)
+ && !is_iterator_used_after_while_let(cx, iter_expr)
+ && !is_nested(cx, expr, &method_args[0]))
+ {
+ let mut applicability = Applicability::MachineApplicable;
+ let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
+ let loop_var = if pat_args.is_empty() {
+ "_".to_string()
+ } else {
+ snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned()
+ };
+ span_lint_and_sugg(
+ cx,
+ WHILE_LET_ON_ITERATOR,
+ expr.span.with_hi(match_expr.span.hi()),
+ "this loop could be written as a `for` loop",
+ "try",
+ format!("for {} in {}", loop_var, iterator),
+ applicability,
+ );
+ }
+ }
+ }
+
+ if let Some((cond, body)) = higher::while_loop(&expr) {
+ check_infinite_loop(cx, cond, body);
+ }
+
+ check_needless_collect(expr, cx);
+ }
+}
+
+enum NeverLoopResult {
+ // A break/return always get triggered but not necessarily for the main loop.
+ AlwaysBreak,
+ // A continue may occur for the main loop.
+ MayContinueMainLoop,
+ Otherwise,
+}
+
+#[must_use]
+fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
+ match *arg {
+ NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
+ NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
+ }
+}
+
+// Combine two results for parts that are called in order.
+#[must_use]
+fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
+ match first {
+ NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
+ NeverLoopResult::Otherwise => second,
+ }
+}
+
+// Combine two results where both parts are called but not necessarily in order.
+#[must_use]
+fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
+ match (left, right) {
+ (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
+ NeverLoopResult::MayContinueMainLoop
+ },
+ (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
+ (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
+ }
+}
+
+// Combine two results where only one of the part may have been executed.
+#[must_use]
+fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
+ match (b1, b2) {
+ (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
+ (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
+ NeverLoopResult::MayContinueMainLoop
+ },
+ (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
+ }
+}
+
+fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
+ let stmts = block.stmts.iter().map(stmt_to_expr);
+ let expr = once(block.expr.as_deref());
+ let mut iter = stmts.chain(expr).filter_map(|e| e);
+ never_loop_expr_seq(&mut iter, main_loop_id)
+}
+
+fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ match stmt.kind {
+ StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e),
+ StmtKind::Local(ref local) => local.init.as_deref(),
+ _ => None,
+ }
+}
+
+fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
+ match expr.kind {
+ ExprKind::Box(ref e)
+ | ExprKind::Unary(_, ref e)
+ | ExprKind::Cast(ref e, _)
+ | ExprKind::Type(ref e, _)
+ | ExprKind::Field(ref e, _)
+ | ExprKind::AddrOf(_, _, ref e)
+ | ExprKind::Struct(_, _, Some(ref e))
+ | ExprKind::Repeat(ref e, _)
+ | ExprKind::DropTemps(ref e) => never_loop_expr(e, main_loop_id),
+ ExprKind::Array(ref es) | ExprKind::MethodCall(_, _, ref es) | ExprKind::Tup(ref es) => {
+ never_loop_expr_all(&mut es.iter(), main_loop_id)
+ },
+ ExprKind::Call(ref e, ref es) => never_loop_expr_all(&mut once(&**e).chain(es.iter()), main_loop_id),
+ ExprKind::Binary(_, ref e1, ref e2)
+ | ExprKind::Assign(ref e1, ref e2, _)
+ | ExprKind::AssignOp(_, ref e1, ref e2)
+ | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id),
+ ExprKind::Loop(ref b, _, _) => {
+ // Break can come from the inner loop so remove them.
+ absorb_break(&never_loop_block(b, main_loop_id))
+ },
+ ExprKind::Match(ref e, ref arms, _) => {
+ let e = never_loop_expr(e, main_loop_id);
+ if arms.is_empty() {
+ e
+ } else {
+ let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
+ combine_seq(e, arms)
+ }
+ },
+ ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id),
+ ExprKind::Continue(d) => {
+ let id = d
+ .target_id
+ .expect("target ID can only be missing in the presence of compilation errors");
+ if id == main_loop_id {
+ NeverLoopResult::MayContinueMainLoop
+ } else {
+ NeverLoopResult::AlwaysBreak
+ }
+ },
+ ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => {
+ if let Some(ref e) = *e {
+ combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
+ } else {
+ NeverLoopResult::AlwaysBreak
+ }
+ },
+ ExprKind::Struct(_, _, None)
+ | ExprKind::Yield(_, _)
+ | ExprKind::Closure(_, _, _, _, _)
+ | ExprKind::LlvmInlineAsm(_)
+ | ExprKind::Path(_)
+ | ExprKind::Lit(_)
+ | ExprKind::Err => NeverLoopResult::Otherwise,
+ }
+}
+
+fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
+ es.map(|e| never_loop_expr(e, main_loop_id))
+ .fold(NeverLoopResult::Otherwise, combine_seq)
+}
+
+fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
+ es.map(|e| never_loop_expr(e, main_loop_id))
+ .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 {
+ e.map(|e| never_loop_expr(e, main_loop_id))
+ .fold(NeverLoopResult::AlwaysBreak, combine_branches)
+}
+
+fn check_for_loop<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ pat: &'tcx Pat<'_>,
+ arg: &'tcx Expr<'_>,
+ body: &'tcx Expr<'_>,
+ expr: &'tcx Expr<'_>,
+) {
+ check_for_loop_range(cx, pat, arg, body, expr);
+ check_for_loop_reverse_range(cx, arg, expr);
+ check_for_loop_arg(cx, pat, arg, expr);
+ check_for_loop_explicit_counter(cx, pat, arg, body, expr);
+ check_for_loop_over_map_kv(cx, pat, arg, body, expr);
+ check_for_mut_range_bound(cx, arg, body);
+ detect_manual_memcpy(cx, pat, arg, body, expr);
+}
+
+fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
+ if_chain! {
- // our variable!
- if local_id == var;
++ if let ExprKind::Path(qpath) = &expr.kind;
++ if let QPath::Resolved(None, path) = qpath;
+ if path.segments.len() == 1;
+ if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id);
- return true;
+ then {
- false
++ // our variable!
++ local_id == var
++ } else {
++ false
+ }
+ }
++}
+
- negate: bool,
++#[derive(Clone, Copy)]
++enum OffsetSign {
++ Positive,
++ Negative,
+}
+
+struct Offset {
+ value: String,
- fn negative(s: String) -> Self {
- Self { value: s, negate: true }
++ sign: OffsetSign,
+}
+
+impl Offset {
- fn positive(s: String) -> Self {
++ fn negative(value: String) -> Self {
++ Self {
++ value,
++ sign: OffsetSign::Negative,
++ }
+ }
+
- value: s,
- negate: false,
++ fn positive(value: String) -> Self {
+ Self {
- struct FixedOffsetVar {
- var_name: String,
++ value,
++ sign: OffsetSign::Positive,
+ }
+ }
+}
+
- fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option<FixedOffsetVar> {
++struct FixedOffsetVar<'hir> {
++ var: &'hir Expr<'hir>,
+ offset: Offset,
+}
+
+fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool {
+ let is_slice = match ty.kind {
+ ty::Ref(_, subty, _) => is_slice_like(cx, subty),
+ ty::Slice(..) | ty::Array(..) => true,
+ _ => false,
+ };
+
+ is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
+}
+
- match e.kind {
- ExprKind::Lit(ref l) => match l.node {
++fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
++ if_chain! {
++ if let ExprKind::MethodCall(method, _, args) = expr.kind;
++ if method.ident.name == sym!(clone);
++ if args.len() == 1;
++ if let Some(arg) = args.get(0);
++ then { arg } else { expr }
++ }
++}
++
++fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) -> Option<Offset> {
+ fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option<String> {
- if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind {
- let ty = cx.tables.expr_ty(seqexpr);
- if !is_slice_like(cx, ty) {
- return None;
- }
-
- let offset = match idx.kind {
- ExprKind::Binary(op, ref lhs, ref rhs) => match op.node {
- BinOpKind::Add => {
- let offset_opt = if same_var(cx, lhs, var) {
- extract_offset(cx, rhs, var)
- } else if same_var(cx, rhs, var) {
- extract_offset(cx, lhs, var)
- } else {
- None
- };
-
- offset_opt.map(Offset::positive)
- },
- BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
- _ => None,
- },
- ExprKind::Path(..) => {
- if same_var(cx, idx, var) {
- Some(Offset::positive("0".into()))
++ match &e.kind {
++ ExprKind::Lit(l) => match l.node {
+ ast::LitKind::Int(x, _ty) => Some(x.to_string()),
+ _ => None,
+ },
+ ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())),
+ _ => None,
+ }
+ }
+
- }
++ match idx.kind {
++ ExprKind::Binary(op, lhs, rhs) => match op.node {
++ BinOpKind::Add => {
++ let offset_opt = if same_var(cx, lhs, var) {
++ extract_offset(cx, rhs, var)
++ } else if same_var(cx, rhs, var) {
++ extract_offset(cx, lhs, var)
+ } else {
+ None
- };
-
- offset.map(|o| FixedOffsetVar {
- var_name: snippet_opt(cx, seqexpr.span).unwrap_or_else(|| "???".into()),
- offset: o,
- })
- } else {
- None
- }
- }
-
- fn fetch_cloned_fixed_offset_var<'a, 'tcx>(
- cx: &LateContext<'a, 'tcx>,
- expr: &Expr<'_>,
- var: HirId,
- ) -> Option<FixedOffsetVar> {
- if_chain! {
- if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind;
- if method.ident.name == sym!(clone);
- if args.len() == 1;
- if let Some(arg) = args.get(0);
- then {
- return get_fixed_offset_var(cx, arg, var);
- }
++ };
++
++ offset_opt.map(Offset::positive)
+ },
++ BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
+ _ => None,
-
- get_fixed_offset_var(cx, expr, var)
++ },
++ ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())),
++ _ => None,
+ }
- fn get_indexed_assignments<'a, 'tcx>(
- cx: &LateContext<'a, 'tcx>,
- body: &Expr<'_>,
- var: HirId,
- ) -> Vec<(FixedOffsetVar, FixedOffsetVar)> {
- fn get_assignment<'a, 'tcx>(
- cx: &LateContext<'a, 'tcx>,
- e: &Expr<'_>,
- var: HirId,
- ) -> Option<(FixedOffsetVar, FixedOffsetVar)> {
- if let ExprKind::Assign(ref lhs, ref rhs, _) = e.kind {
- match (
- get_fixed_offset_var(cx, lhs, var),
- fetch_cloned_fixed_offset_var(cx, rhs, var),
- ) {
- (Some(offset_left), Some(offset_right)) => {
- // Source and destination must be different
- if offset_left.var_name == offset_right.var_name {
- None
- } else {
- Some((offset_left, offset_right))
- }
- },
- _ => None,
- }
+}
+
- if let ExprKind::Block(ref b, _) = body.kind {
- let Block {
- ref stmts, ref expr, ..
- } = **b;
++fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> {
++ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
++ if let ExprKind::Assign(lhs, rhs, _) = e.kind {
++ Some((lhs, rhs))
+ } else {
+ None
+ }
+ }
+
- stmts
++ // This is one of few ways to return different iterators
++ // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434
++ let mut iter_a = None;
++ let mut iter_b = None;
++
++ if let ExprKind::Block(b, _) = body.kind {
++ let Block { stmts, expr, .. } = *b;
+
- .map(|stmt| match stmt.kind {
++ iter_a = stmts
+ .iter()
- StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => Some(get_assignment(cx, e, var)),
++ .filter_map(|stmt| match stmt.kind {
+ StmtKind::Local(..) | StmtKind::Item(..) => None,
- .chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var))))
- .filter_map(|op| op)
- .collect::<Option<Vec<_>>>()
- .unwrap_or_default()
++ StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
+ })
- get_assignment(cx, body, var).into_iter().collect()
++ .chain(expr.into_iter())
++ .map(get_assignment)
++ .into()
+ } else {
- ref end,
++ iter_b = Some(get_assignment(body))
+ }
++
++ iter_a.into_iter().flatten().chain(iter_b.into_iter())
+}
+
++fn build_manual_memcpy_suggestion<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ start: &Expr<'_>,
++ end: &Expr<'_>,
++ limits: ast::RangeLimits,
++ dst_var: FixedOffsetVar<'_>,
++ src_var: FixedOffsetVar<'_>,
++) -> String {
++ fn print_sum(arg1: &str, arg2: &Offset) -> String {
++ match (arg1, &arg2.value[..], arg2.sign) {
++ ("0", "0", _) => "0".into(),
++ ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(),
++ ("0", x, OffsetSign::Negative) => format!("-{}", x),
++ (x, y, OffsetSign::Positive) => format!("({} + {})", x, y),
++ (x, y, OffsetSign::Negative) => {
++ if x == y {
++ "0".into()
++ } else {
++ format!("({} - {})", x, y)
++ }
++ },
++ }
++ }
++
++ fn print_offset(start_str: &str, inline_offset: &Offset) -> String {
++ let offset = print_sum(start_str, inline_offset);
++ if offset.as_str() == "0" {
++ "".into()
++ } else {
++ offset
++ }
++ }
++
++ let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| {
++ if_chain! {
++ if let ExprKind::MethodCall(method, _, len_args) = end.kind;
++ if method.ident.name == sym!(len);
++ if len_args.len() == 1;
++ if let Some(arg) = len_args.get(0);
++ if var_def_id(cx, arg) == var_def_id(cx, var);
++ then {
++ match offset.sign {
++ OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value),
++ OffsetSign::Positive => "".into(),
++ }
++ } else {
++ let end_str = match limits {
++ ast::RangeLimits::Closed => {
++ let end = sugg::Sugg::hir(cx, end, "<count>");
++ format!("{}", end + sugg::ONE)
++ },
++ ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
++ };
++
++ print_sum(&end_str, &offset)
++ }
++ }
++ };
++
++ let start_str = snippet(cx, start.span, "").to_string();
++ let dst_offset = print_offset(&start_str, &dst_var.offset);
++ let dst_limit = print_limit(end, dst_var.offset, dst_var.var);
++ let src_offset = print_offset(&start_str, &src_var.offset);
++ let src_limit = print_limit(end, src_var.offset, src_var.var);
++
++ let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into());
++ let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into());
++
++ let dst = if dst_offset == "" && dst_limit == "" {
++ dst_var_name
++ } else {
++ format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit)
++ };
++
++ format!(
++ "{}.clone_from_slice(&{}[{}..{}])",
++ dst, src_var_name, src_offset, src_limit
++ )
++}
+/// Checks for for loops that sequentially copy items from one slice-like
+/// object to another.
+fn detect_manual_memcpy<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ pat: &'tcx Pat<'_>,
+ arg: &'tcx Expr<'_>,
+ body: &'tcx Expr<'_>,
+ expr: &'tcx Expr<'_>,
+) {
+ if let Some(higher::Range {
+ start: Some(start),
- let print_sum = |arg1: &Offset, arg2: &Offset| -> String {
- match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) {
- ("0", _, "0", _) => "".into(),
- ("0", _, x, false) | (x, false, "0", false) => x.into(),
- ("0", _, x, true) | (x, false, "0", true) => format!("-{}", x),
- (x, false, y, false) => format!("({} + {})", x, y),
- (x, false, y, true) => {
- if x == y {
- "0".into()
- } else {
- format!("({} - {})", x, y)
- }
- },
- (x, true, y, false) => {
- if x == y {
- "0".into()
- } else {
- format!("({} - {})", y, x)
- }
- },
- (x, true, y, true) => format!("-({} + {})", x, y),
- }
- };
-
- let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| {
- if let Some(end) = *end {
- if_chain! {
- if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind;
- if method.ident.name == sym!(len);
- if len_args.len() == 1;
- if let Some(arg) = len_args.get(0);
- if snippet(cx, arg.span, "??") == var_name;
- then {
- return if offset.negate {
- format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value)
- } else {
- String::new()
- };
- }
- }
-
- let end_str = match limits {
- ast::RangeLimits::Closed => {
- let end = sugg::Sugg::hir(cx, end, "<count>");
- format!("{}", end + sugg::ONE)
- },
- ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
- };
-
- print_sum(&Offset::positive(end_str), &offset)
- } else {
- "..".into()
- }
- };
-
++ end: Some(end),
+ limits,
+ }) = higher::range(cx, arg)
+ {
+ // the var must be a single name
+ if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
- let manual_copies = get_indexed_assignments(cx, body, canonical_id);
-
- let big_sugg = manual_copies
- .into_iter()
- .map(|(dst_var, src_var)| {
- let start_str = Offset::positive(snippet(cx, start.span, "").to_string());
- let dst_offset = print_sum(&start_str, &dst_var.offset);
- let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name);
- let src_offset = print_sum(&start_str, &src_var.offset);
- let src_limit = print_limit(end, src_var.offset, &src_var.var_name);
- let dst = if dst_offset == "" && dst_limit == "" {
- dst_var.var_name
- } else {
- format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit)
- };
-
- format!(
- "{}.clone_from_slice(&{}[{}..{}])",
- dst, src_var.var_name, src_offset, src_limit
- )
+ // The only statements in the for loops can be indexed assignments from
+ // indexed retrievals.
- .join("\n ");
++ let big_sugg = get_assignments(body)
++ .map(|o| {
++ o.and_then(|(lhs, rhs)| {
++ let rhs = fetch_cloned_expr(rhs);
++ if_chain! {
++ if let ExprKind::Index(seqexpr_left, idx_left) = lhs.kind;
++ if let ExprKind::Index(seqexpr_right, idx_right) = rhs.kind;
++ if is_slice_like(cx, cx.tables.expr_ty(seqexpr_left))
++ && is_slice_like(cx, cx.tables.expr_ty(seqexpr_right));
++ if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id);
++ if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id);
++
++ // Source and destination must be different
++ if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right);
++ then {
++ Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left },
++ FixedOffsetVar { var: seqexpr_right, offset: offset_right }))
++ } else {
++ None
++ }
++ }
++ })
+ })
- if !big_sugg.is_empty() {
++ .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src)))
++ .collect::<Option<Vec<_>>>()
++ .filter(|v| !v.is_empty())
++ .map(|v| v.join("\n "));
+
++ if let Some(big_sugg) = big_sugg {
+ span_lint_and_sugg(
+ cx,
+ MANUAL_MEMCPY,
+ expr.span,
+ "it looks like you're manually copying between slices",
+ "try replacing the loop by",
+ big_sugg,
+ Applicability::Unspecified,
+ );
+ }
+ }
+ }
+}
+
+/// Checks for looping over a range and then indexing a sequence with it.
+/// The iteratee must be a range literal.
+#[allow(clippy::too_many_lines)]
+fn check_for_loop_range<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ pat: &'tcx Pat<'_>,
+ arg: &'tcx Expr<'_>,
+ body: &'tcx Expr<'_>,
+ expr: &'tcx Expr<'_>,
+) {
+ if let Some(higher::Range {
+ start: Some(start),
+ ref end,
+ limits,
+ }) = higher::range(cx, arg)
+ {
+ // the var must be a single name
+ if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
+ let mut visitor = VarVisitor {
+ cx,
+ var: canonical_id,
+ indexed_mut: FxHashSet::default(),
+ indexed_indirectly: FxHashMap::default(),
+ indexed_directly: FxHashMap::default(),
+ referenced: FxHashSet::default(),
+ nonindex: false,
+ prefer_mutable: false,
+ };
+ walk_expr(&mut visitor, body);
+
+ // linting condition: we only indexed one variable, and indexed it directly
+ if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
+ let (indexed, (indexed_extent, indexed_ty)) = visitor
+ .indexed_directly
+ .into_iter()
+ .next()
+ .expect("already checked that we have exactly 1 element");
+
+ // ensure that the indexed variable was declared before the loop, see #601
+ if let Some(indexed_extent) = indexed_extent {
+ let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
+ let parent_def_id = cx.tcx.hir().local_def_id(parent_id);
+ let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
+ let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id);
+ if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
+ return;
+ }
+ }
+
+ // don't lint if the container that is indexed does not have .iter() method
+ let has_iter = has_iter_method(cx, indexed_ty);
+ if has_iter.is_none() {
+ return;
+ }
+
+ // don't lint if the container that is indexed into is also used without
+ // indexing
+ if visitor.referenced.contains(&indexed) {
+ return;
+ }
+
+ let starts_at_zero = is_integer_const(cx, start, 0);
+
+ let skip = if starts_at_zero {
+ String::new()
+ } else {
+ format!(".skip({})", snippet(cx, start.span, ".."))
+ };
+
+ let mut end_is_start_plus_val = false;
+
+ let take = if let Some(end) = *end {
+ let mut take_expr = end;
+
+ if let ExprKind::Binary(ref op, ref left, ref right) = end.kind {
+ if let BinOpKind::Add = op.node {
+ let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
+ let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
+
+ if start_equal_left {
+ take_expr = right;
+ } else if start_equal_right {
+ take_expr = left;
+ }
+
+ end_is_start_plus_val = start_equal_left | start_equal_right;
+ }
+ }
+
+ if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
+ String::new()
+ } else {
+ match limits {
+ ast::RangeLimits::Closed => {
+ let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
+ format!(".take({})", take_expr + sugg::ONE)
+ },
+ ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")),
+ }
+ }
+ } else {
+ String::new()
+ };
+
+ let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
+ ("mut ", "iter_mut")
+ } else {
+ ("", "iter")
+ };
+
+ let take_is_empty = take.is_empty();
+ let mut method_1 = take;
+ let mut method_2 = skip;
+
+ if end_is_start_plus_val {
+ mem::swap(&mut method_1, &mut method_2);
+ }
+
+ if visitor.nonindex {
+ span_lint_and_then(
+ cx,
+ NEEDLESS_RANGE_LOOP,
+ expr.span,
+ &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
+ |diag| {
+ multispan_sugg(
+ diag,
+ "consider using an iterator".to_string(),
+ vec![
+ (pat.span, format!("({}, <item>)", ident.name)),
+ (
+ arg.span,
+ format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2),
+ ),
+ ],
+ );
+ },
+ );
+ } else {
+ let repl = if starts_at_zero && take_is_empty {
+ format!("&{}{}", ref_mut, indexed)
+ } else {
+ format!("{}.{}(){}{}", indexed, method, method_1, method_2)
+ };
+
+ span_lint_and_then(
+ cx,
+ NEEDLESS_RANGE_LOOP,
+ expr.span,
+ &format!(
+ "the loop variable `{}` is only used to index `{}`.",
+ ident.name, indexed
+ ),
+ |diag| {
+ multispan_sugg(
+ diag,
+ "consider using an iterator".to_string(),
+ vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
+ );
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
+fn is_len_call(expr: &Expr<'_>, var: Name) -> bool {
+ if_chain! {
+ if let ExprKind::MethodCall(ref method, _, ref len_args) = expr.kind;
+ if len_args.len() == 1;
+ if method.ident.name == sym!(len);
+ if let ExprKind::Path(QPath::Resolved(_, ref path)) = len_args[0].kind;
+ if path.segments.len() == 1;
+ if path.segments[0].ident.name == var;
+ then {
+ return true;
+ }
+ }
+
+ false
+}
+
+fn is_end_eq_array_len<'tcx>(
+ cx: &LateContext<'_, 'tcx>,
+ end: &Expr<'_>,
+ limits: ast::RangeLimits,
+ indexed_ty: Ty<'tcx>,
+) -> bool {
+ if_chain! {
+ if let ExprKind::Lit(ref lit) = end.kind;
+ if let ast::LitKind::Int(end_int, _) = lit.node;
+ if let ty::Array(_, arr_len_const) = indexed_ty.kind;
+ if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
+ then {
+ return match limits {
+ ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
+ ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
+ };
+ }
+ }
+
+ false
+}
+
+fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
+ // if this for loop is iterating over a two-sided range...
+ if let Some(higher::Range {
+ start: Some(start),
+ end: Some(end),
+ limits,
+ }) = higher::range(cx, arg)
+ {
+ // ...and both sides are compile-time constant integers...
+ if let Some((start_idx, _)) = constant(cx, cx.tables, start) {
+ if let Some((end_idx, _)) = constant(cx, cx.tables, end) {
+ // ...and the start index is greater than the end index,
+ // this loop will never run. This is often confusing for developers
+ // who think that this will iterate from the larger value to the
+ // smaller value.
+ let ty = cx.tables.expr_ty(start);
+ let (sup, eq) = match (start_idx, end_idx) {
+ (Constant::Int(start_idx), Constant::Int(end_idx)) => (
+ match ty.kind {
+ ty::Int(ity) => sext(cx.tcx, start_idx, ity) > sext(cx.tcx, end_idx, ity),
+ ty::Uint(_) => start_idx > end_idx,
+ _ => false,
+ },
+ start_idx == end_idx,
+ ),
+ _ => (false, false),
+ };
+
+ if sup {
+ let start_snippet = snippet(cx, start.span, "_");
+ let end_snippet = snippet(cx, end.span, "_");
+ let dots = if limits == ast::RangeLimits::Closed {
+ "..="
+ } else {
+ ".."
+ };
+
+ span_lint_and_then(
+ cx,
+ REVERSE_RANGE_LOOP,
+ expr.span,
+ "this range is empty so this for loop will never run",
+ |diag| {
+ diag.span_suggestion(
+ arg.span,
+ "consider using the following if you are attempting to iterate over this \
+ range in reverse",
+ format!(
+ "({end}{dots}{start}).rev()",
+ end = end_snippet,
+ dots = dots,
+ start = start_snippet
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ } else if eq && limits != ast::RangeLimits::Closed {
+ // if they are equal, it's also problematic - this loop
+ // will never run.
+ span_lint(
+ cx,
+ REVERSE_RANGE_LOOP,
+ expr.span,
+ "this range is empty so this for loop will never run",
+ );
+ }
+ }
+ }
+ }
+}
+
+fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
+ let mut applicability = Applicability::MachineApplicable;
+ let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+ let muta = if method_name == "iter_mut" { "mut " } else { "" };
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_ITER_LOOP,
+ arg.span,
+ "it is more concise to loop over references to containers instead of using explicit \
+ iteration methods",
+ "to write this more concisely, try",
+ format!("&{}{}", muta, object),
+ applicability,
+ )
+}
+
+fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
+ let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
+ if let ExprKind::MethodCall(ref method, _, ref args) = arg.kind {
+ // just the receiver, no arguments
+ if args.len() == 1 {
+ let method_name = &*method.ident.as_str();
+ // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
+ if method_name == "iter" || method_name == "iter_mut" {
+ if is_ref_iterable_type(cx, &args[0]) {
+ lint_iter_method(cx, args, arg, method_name);
+ }
+ } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
+ let receiver_ty = cx.tables.expr_ty(&args[0]);
+ let receiver_ty_adjusted = cx.tables.expr_ty_adjusted(&args[0]);
+ if same_tys(cx, receiver_ty, receiver_ty_adjusted) {
+ let mut applicability = Applicability::MachineApplicable;
+ let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_INTO_ITER_LOOP,
+ arg.span,
+ "it is more concise to loop over containers instead of using explicit \
+ iteration methods",
+ "to write this more concisely, try",
+ object.to_string(),
+ applicability,
+ );
+ } else {
+ let ref_receiver_ty = cx.tcx.mk_ref(
+ cx.tcx.lifetimes.re_erased,
+ ty::TypeAndMut {
+ ty: receiver_ty,
+ mutbl: Mutability::Not,
+ },
+ );
+ if same_tys(cx, receiver_ty_adjusted, ref_receiver_ty) {
+ lint_iter_method(cx, args, arg, method_name)
+ }
+ }
+ } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
+ span_lint(
+ cx,
+ ITER_NEXT_LOOP,
+ expr.span,
+ "you are iterating over `Iterator::next()` which is an Option; this will compile but is \
+ probably not what you want",
+ );
+ next_loop_linted = true;
+ }
+ }
+ }
+ if !next_loop_linted {
+ check_arg_type(cx, pat, arg);
+ }
+}
+
+/// Checks for `for` loops over `Option`s and `Result`s.
+fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) {
+ let ty = cx.tables.expr_ty(arg);
+ if is_type_diagnostic_item(cx, ty, sym!(option_type)) {
+ span_lint_and_help(
+ cx,
+ FOR_LOOP_OVER_OPTION,
+ arg.span,
+ &format!(
+ "for loop over `{0}`, which is an `Option`. This is more readably written as an \
+ `if let` statement.",
+ snippet(cx, arg.span, "_")
+ ),
+ None,
+ &format!(
+ "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
+ snippet(cx, pat.span, "_"),
+ snippet(cx, arg.span, "_")
+ ),
+ );
+ } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) {
+ span_lint_and_help(
+ cx,
+ FOR_LOOP_OVER_RESULT,
+ arg.span,
+ &format!(
+ "for loop over `{0}`, which is a `Result`. This is more readably written as an \
+ `if let` statement.",
+ snippet(cx, arg.span, "_")
+ ),
+ None,
+ &format!(
+ "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
+ snippet(cx, pat.span, "_"),
+ snippet(cx, arg.span, "_")
+ ),
+ );
+ }
+}
+
+fn check_for_loop_explicit_counter<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ pat: &'tcx Pat<'_>,
+ arg: &'tcx Expr<'_>,
+ body: &'tcx Expr<'_>,
+ expr: &'tcx Expr<'_>,
+) {
+ // Look for variables that are incremented once per loop iteration.
+ let mut visitor = IncrementVisitor {
+ cx,
+ states: FxHashMap::default(),
+ depth: 0,
+ done: false,
+ };
+ walk_expr(&mut visitor, body);
+
+ // For each candidate, check the parent block to see if
+ // it's initialized to zero at the start of the loop.
+ if let Some(block) = get_enclosing_block(&cx, expr.hir_id) {
+ for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) {
+ let mut visitor2 = InitializeVisitor {
+ cx,
+ end_expr: expr,
+ var_id: *id,
+ state: VarState::IncrOnce,
+ name: None,
+ depth: 0,
+ past_loop: false,
+ };
+ walk_block(&mut visitor2, block);
+
+ if visitor2.state == VarState::Warn {
+ if let Some(name) = visitor2.name {
+ let mut applicability = Applicability::MachineApplicable;
+
+ // for some reason this is the only way to get the `Span`
+ // of the entire `for` loop
+ let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind {
+ arms[0].body.span
+ } else {
+ unreachable!()
+ };
+
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_COUNTER_LOOP,
+ for_span.with_hi(arg.span.hi()),
+ &format!("the variable `{}` is used as a loop counter.", name),
+ "consider using",
+ format!(
+ "for ({}, {}) in {}.enumerate()",
+ name,
+ snippet_with_applicability(cx, pat.span, "item", &mut applicability),
+ make_iterator_snippet(cx, arg, &mut applicability),
+ ),
+ applicability,
+ );
+ }
+ }
+ }
+ }
+}
+
+/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
+/// actual `Iterator` that the loop uses.
+fn make_iterator_snippet(cx: &LateContext<'_, '_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
+ let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR)
+ .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(arg), id, &[]));
+ if impls_iterator {
+ format!(
+ "{}",
+ sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
+ )
+ } else {
+ // (&x).into_iter() ==> x.iter()
+ // (&mut x).into_iter() ==> x.iter_mut()
+ match &arg.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner)
+ if has_iter_method(cx, cx.tables.expr_ty(&arg_inner)).is_some() =>
+ {
+ let meth_name = match mutability {
+ Mutability::Mut => "iter_mut",
+ Mutability::Not => "iter",
+ };
+ format!(
+ "{}.{}()",
+ sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(),
+ meth_name,
+ )
+ }
+ _ => format!(
+ "{}.into_iter()",
+ sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
+ ),
+ }
+ }
+}
+
+/// Checks for the `FOR_KV_MAP` lint.
+fn check_for_loop_over_map_kv<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ pat: &'tcx Pat<'_>,
+ arg: &'tcx Expr<'_>,
+ body: &'tcx Expr<'_>,
+ expr: &'tcx Expr<'_>,
+) {
+ let pat_span = pat.span;
+
+ if let PatKind::Tuple(ref pat, _) = pat.kind {
+ if pat.len() == 2 {
+ let arg_span = arg.span;
+ let (new_pat_span, kind, ty, mutbl) = match cx.tables.expr_ty(arg).kind {
+ ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
+ (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl),
+ (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not),
+ _ => return,
+ },
+ _ => return,
+ };
+ let mutbl = match mutbl {
+ Mutability::Not => "",
+ Mutability::Mut => "_mut",
+ };
+ let arg = match arg.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) => &**expr,
+ _ => arg,
+ };
+
+ if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP) {
+ span_lint_and_then(
+ cx,
+ FOR_KV_MAP,
+ expr.span,
+ &format!("you seem to want to iterate on a map's {}s", kind),
+ |diag| {
+ let map = sugg::Sugg::hir(cx, arg, "map");
+ multispan_sugg(
+ diag,
+ "use the corresponding method".into(),
+ vec![
+ (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
+ (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
+ ],
+ );
+ },
+ );
+ }
+ }
+ }
+}
+
+struct MutatePairDelegate {
+ hir_id_low: Option<HirId>,
+ hir_id_high: Option<HirId>,
+ span_low: Option<Span>,
+ span_high: Option<Span>,
+}
+
+impl<'tcx> Delegate<'tcx> for MutatePairDelegate {
+ fn consume(&mut self, _: &Place<'tcx>, _: ConsumeMode) {}
+
+ fn borrow(&mut self, cmt: &Place<'tcx>, bk: ty::BorrowKind) {
+ if let ty::BorrowKind::MutBorrow = bk {
+ if let PlaceBase::Local(id) = cmt.base {
+ if Some(id) == self.hir_id_low {
+ self.span_low = Some(cmt.span)
+ }
+ if Some(id) == self.hir_id_high {
+ self.span_high = Some(cmt.span)
+ }
+ }
+ }
+ }
+
+ fn mutate(&mut self, cmt: &Place<'tcx>) {
+ if let PlaceBase::Local(id) = cmt.base {
+ if Some(id) == self.hir_id_low {
+ self.span_low = Some(cmt.span)
+ }
+ if Some(id) == self.hir_id_high {
+ self.span_high = Some(cmt.span)
+ }
+ }
+ }
+}
+
+impl<'tcx> MutatePairDelegate {
+ fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
+ (self.span_low, self.span_high)
+ }
+}
+
+fn check_for_mut_range_bound(cx: &LateContext<'_, '_>, arg: &Expr<'_>, body: &Expr<'_>) {
+ if let Some(higher::Range {
+ start: Some(start),
+ end: Some(end),
+ ..
+ }) = higher::range(cx, arg)
+ {
+ let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
+ if mut_ids[0].is_some() || mut_ids[1].is_some() {
+ let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
+ mut_warn_with_span(cx, span_low);
+ mut_warn_with_span(cx, span_high);
+ }
+ }
+}
+
+fn mut_warn_with_span(cx: &LateContext<'_, '_>, span: Option<Span>) {
+ if let Some(sp) = span {
+ span_lint(
+ cx,
+ MUT_RANGE_BOUND,
+ sp,
+ "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
+ );
+ }
+}
+
+fn check_for_mutability(cx: &LateContext<'_, '_>, bound: &Expr<'_>) -> Option<HirId> {
+ if_chain! {
+ if let ExprKind::Path(ref qpath) = bound.kind;
+ if let QPath::Resolved(None, _) = *qpath;
+ then {
+ let res = qpath_res(cx, qpath, bound.hir_id);
+ if let Res::Local(hir_id) = res {
+ let node_str = cx.tcx.hir().get(hir_id);
+ if_chain! {
+ if let Node::Binding(pat) = node_str;
+ if let PatKind::Binding(bind_ann, ..) = pat.kind;
+ if let BindingAnnotation::Mutable = bind_ann;
+ then {
+ return Some(hir_id);
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+fn check_for_mutation(
+ cx: &LateContext<'_, '_>,
+ body: &Expr<'_>,
+ bound_ids: &[Option<HirId>],
+) -> (Option<Span>, Option<Span>) {
+ let mut delegate = MutatePairDelegate {
+ hir_id_low: bound_ids[0],
+ hir_id_high: bound_ids[1],
+ span_low: None,
+ span_high: None,
+ };
+ let def_id = body.hir_id.owner.to_def_id();
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ ExprUseVisitor::new(&mut delegate, &infcx, def_id.expect_local(), cx.param_env, cx.tables).walk_expr(body);
+ });
+ delegate.mutation_span()
+}
+
+/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
+fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
+ match *pat {
+ PatKind::Wild => true,
+ PatKind::Binding(.., ident, None) if ident.as_str().starts_with('_') => is_unused(&ident, body),
+ _ => false,
+ }
+}
+
+struct LocalUsedVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>,
+ local: HirId,
+ used: bool,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if same_var(self.cx, expr, self.local) {
+ self.used = true;
+ } else {
+ walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+struct VarVisitor<'a, 'tcx> {
+ /// context reference
+ cx: &'a LateContext<'a, 'tcx>,
+ /// var name to look for as index
+ var: HirId,
+ /// indexed variables that are used mutably
+ indexed_mut: FxHashSet<Name>,
+ /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
+ indexed_indirectly: FxHashMap<Name, Option<region::Scope>>,
+ /// subset of `indexed` of vars that are indexed directly: `v[i]`
+ /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
+ indexed_directly: FxHashMap<Name, (Option<region::Scope>, Ty<'tcx>)>,
+ /// Any names that are used outside an index operation.
+ /// Used to detect things like `&mut vec` used together with `vec[i]`
+ referenced: FxHashSet<Name>,
+ /// has the loop variable been used in expressions other than the index of
+ /// an index op?
+ nonindex: bool,
+ /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
+ /// takes `&mut self`
+ prefer_mutable: bool,
+}
+
+impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
+ fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
+ if_chain! {
+ // the indexed container is referenced by a name
+ if let ExprKind::Path(ref seqpath) = seqexpr.kind;
+ if let QPath::Resolved(None, ref seqvar) = *seqpath;
+ if seqvar.segments.len() == 1;
+ then {
+ let index_used_directly = same_var(self.cx, idx, self.var);
+ let indexed_indirectly = {
+ let mut used_visitor = LocalUsedVisitor {
+ cx: self.cx,
+ local: self.var,
+ used: false,
+ };
+ walk_expr(&mut used_visitor, idx);
+ used_visitor.used
+ };
+
+ if indexed_indirectly || index_used_directly {
+ if self.prefer_mutable {
+ self.indexed_mut.insert(seqvar.segments[0].ident.name);
+ }
+ let res = qpath_res(self.cx, seqpath, seqexpr.hir_id);
+ match res {
+ Res::Local(hir_id) => {
+ let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
+ let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
+ let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
+ if indexed_indirectly {
+ self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
+ }
+ if index_used_directly {
+ self.indexed_directly.insert(
+ seqvar.segments[0].ident.name,
+ (Some(extent), self.cx.tables.node_type(seqexpr.hir_id)),
+ );
+ }
+ return false; // no need to walk further *on the variable*
+ }
+ Res::Def(DefKind::Static | DefKind::Const, ..) => {
+ if indexed_indirectly {
+ self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
+ }
+ if index_used_directly {
+ self.indexed_directly.insert(
+ seqvar.segments[0].ident.name,
+ (None, self.cx.tables.node_type(seqexpr.hir_id)),
+ );
+ }
+ return false; // no need to walk further *on the variable*
+ }
+ _ => (),
+ }
+ }
+ }
+ }
+ true
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if_chain! {
+ // a range index op
+ if let ExprKind::MethodCall(ref meth, _, ref args) = expr.kind;
+ if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX))
+ || (meth.ident.name == sym!(index_mut) && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
+ if !self.check(&args[1], &args[0], expr);
+ then { return }
+ }
+
+ if_chain! {
+ // an index op
+ if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind;
+ if !self.check(idx, seqexpr, expr);
+ then { return }
+ }
+
+ if_chain! {
+ // directly using a variable
+ if let ExprKind::Path(ref qpath) = expr.kind;
+ if let QPath::Resolved(None, ref path) = *qpath;
+ if path.segments.len() == 1;
+ then {
+ if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id) {
+ if local_id == self.var {
+ self.nonindex = true;
+ } else {
+ // not the correct variable, but still a variable
+ self.referenced.insert(path.segments[0].ident.name);
+ }
+ }
+ }
+ }
+
+ let old = self.prefer_mutable;
+ match expr.kind {
+ ExprKind::AssignOp(_, ref lhs, ref rhs) | ExprKind::Assign(ref lhs, ref rhs, _) => {
+ self.prefer_mutable = true;
+ self.visit_expr(lhs);
+ self.prefer_mutable = false;
+ self.visit_expr(rhs);
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, mutbl, ref expr) => {
+ if mutbl == Mutability::Mut {
+ self.prefer_mutable = true;
+ }
+ self.visit_expr(expr);
+ },
+ ExprKind::Call(ref f, args) => {
+ self.visit_expr(f);
+ for expr in args {
+ let ty = self.cx.tables.expr_ty_adjusted(expr);
+ self.prefer_mutable = false;
+ if let ty::Ref(_, _, mutbl) = ty.kind {
+ if mutbl == Mutability::Mut {
+ self.prefer_mutable = true;
+ }
+ }
+ self.visit_expr(expr);
+ }
+ },
+ ExprKind::MethodCall(_, _, args) => {
+ let def_id = self.cx.tables.type_dependent_def_id(expr.hir_id).unwrap();
+ for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) {
+ self.prefer_mutable = false;
+ if let ty::Ref(_, _, mutbl) = ty.kind {
+ if mutbl == Mutability::Mut {
+ self.prefer_mutable = true;
+ }
+ }
+ self.visit_expr(expr);
+ }
+ },
+ ExprKind::Closure(_, _, body_id, ..) => {
+ let body = self.cx.tcx.hir().body(body_id);
+ self.visit_expr(&body.value);
+ },
+ _ => walk_expr(self, expr),
+ }
+ self.prefer_mutable = old;
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+fn is_used_inside<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool {
+ let def_id = match var_def_id(cx, expr) {
+ Some(id) => id,
+ None => return false,
+ };
+ if let Some(used_mutably) = mutated_variables(container, cx) {
+ if used_mutably.contains(&def_id) {
+ return true;
+ }
+ }
+ false
+}
+
+fn is_iterator_used_after_while_let<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, iter_expr: &'tcx Expr<'_>) -> bool {
+ let def_id = match var_def_id(cx, iter_expr) {
+ Some(id) => id,
+ None => return false,
+ };
+ let mut visitor = VarUsedAfterLoopVisitor {
+ cx,
+ def_id,
+ iter_expr_id: iter_expr.hir_id,
+ past_while_let: false,
+ var_used_after_while_let: false,
+ };
+ if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
+ walk_block(&mut visitor, enclosing_block);
+ }
+ visitor.var_used_after_while_let
+}
+
+struct VarUsedAfterLoopVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>,
+ def_id: HirId,
+ iter_expr_id: HirId,
+ past_while_let: bool,
+ var_used_after_while_let: bool,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.past_while_let {
+ if Some(self.def_id) == var_def_id(self.cx, expr) {
+ self.var_used_after_while_let = true;
+ }
+ } else if self.iter_expr_id == expr.hir_id {
+ self.past_while_let = true;
+ }
+ walk_expr(self, expr);
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
+/// for `&T` and `&mut T`, such as `Vec`.
+#[rustfmt::skip]
+fn is_ref_iterable_type(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool {
+ // no walk_ptrs_ty: calling iter() on a reference can make sense because it
+ // will allow further borrows afterwards
+ let ty = cx.tables.expr_ty(e);
+ is_iterable_array(ty, cx) ||
+ is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
+ match_type(cx, ty, &paths::LINKED_LIST) ||
+ is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
+ is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
+ is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
+ match_type(cx, ty, &paths::BINARY_HEAP) ||
+ match_type(cx, ty, &paths::BTREEMAP) ||
+ match_type(cx, ty, &paths::BTREESET)
+}
+
+fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'_, 'tcx>) -> bool {
+ // IntoIterator is currently only implemented for array sizes <= 32 in rustc
+ match ty.kind {
+ ty::Array(_, n) => {
+ if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) {
+ (0..=32).contains(&val)
+ } else {
+ false
+ }
+ },
+ _ => false,
+ }
+}
+
+/// If a block begins with a statement (possibly a `let` binding) and has an
+/// expression, return it.
+fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ if block.stmts.is_empty() {
+ return None;
+ }
+ if let StmtKind::Local(ref local) = block.stmts[0].kind {
+ if let Some(expr) = local.init {
+ Some(expr)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+/// If a block begins with an expression (with or without semicolon), return it.
+fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ match block.expr {
+ Some(ref expr) if block.stmts.is_empty() => Some(expr),
+ None if !block.stmts.is_empty() => match block.stmts[0].kind {
+ StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr),
+ StmtKind::Local(..) | StmtKind::Item(..) => None,
+ },
+ _ => None,
+ }
+}
+
+/// Returns `true` if expr contains a single break expr without destination label
+/// and
+/// passed expression. The expression may be within a block.
+fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
+ match expr.kind {
+ ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
+ ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
+ _ => false,
+ }
+}
+
+// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
+// incremented exactly once in the loop body, and initialized to zero
+// at the start of the loop.
+#[derive(Debug, PartialEq)]
+enum VarState {
+ Initial, // Not examined yet
+ IncrOnce, // Incremented exactly once, may be a loop counter
+ Declared, // Declared but not (yet) initialized to zero
+ Warn,
+ DontWarn,
+}
+
+/// Scan a for loop for variables that are incremented exactly once.
+struct IncrementVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>, // context reference
+ states: FxHashMap<HirId, VarState>, // incremented variables
+ depth: u32, // depth of conditional expressions
+ done: bool,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.done {
+ return;
+ }
+
+ // If node is a variable
+ if let Some(def_id) = var_def_id(self.cx, expr) {
+ if let Some(parent) = get_parent_expr(self.cx, expr) {
+ let state = self.states.entry(def_id).or_insert(VarState::Initial);
+
+ match parent.kind {
+ ExprKind::AssignOp(op, ref lhs, ref rhs) => {
+ if lhs.hir_id == expr.hir_id {
+ if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) {
+ *state = match *state {
+ VarState::Initial if self.depth == 0 => VarState::IncrOnce,
+ _ => VarState::DontWarn,
+ };
+ } else {
+ // Assigned some other value
+ *state = VarState::DontWarn;
+ }
+ }
+ },
+ ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn,
+ ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
+ *state = VarState::DontWarn
+ },
+ _ => (),
+ }
+ }
+ } else if is_loop(expr) || is_conditional(expr) {
+ self.depth += 1;
+ walk_expr(self, expr);
+ self.depth -= 1;
+ return;
+ } else if let ExprKind::Continue(_) = expr.kind {
+ self.done = true;
+ return;
+ }
+ walk_expr(self, expr);
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Checks whether a variable is initialized to zero at the start of a loop.
+struct InitializeVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>, // context reference
+ end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here.
+ var_id: HirId,
+ state: VarState,
+ name: Option<Name>,
+ depth: u32, // depth of conditional expressions
+ past_loop: bool,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+ // Look for declarations of the variable
+ if let StmtKind::Local(ref local) = stmt.kind {
+ if local.pat.hir_id == self.var_id {
+ if let PatKind::Binding(.., ident, _) = local.pat.kind {
+ self.name = Some(ident.name);
+
+ self.state = if let Some(ref init) = local.init {
+ if is_integer_const(&self.cx, init, 0) {
+ VarState::Warn
+ } else {
+ VarState::Declared
+ }
+ } else {
+ VarState::Declared
+ }
+ }
+ }
+ }
+ walk_stmt(self, stmt);
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.state == VarState::DontWarn {
+ return;
+ }
+ if SpanlessEq::new(self.cx).eq_expr(&expr, self.end_expr) {
+ self.past_loop = true;
+ return;
+ }
+ // No need to visit expressions before the variable is
+ // declared
+ if self.state == VarState::IncrOnce {
+ return;
+ }
+
+ // If node is the desired variable, see how it's used
+ if var_def_id(self.cx, expr) == Some(self.var_id) {
+ if let Some(parent) = get_parent_expr(self.cx, expr) {
+ match parent.kind {
+ ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => {
+ self.state = VarState::DontWarn;
+ },
+ ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => {
+ self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 {
+ VarState::Warn
+ } else {
+ VarState::DontWarn
+ }
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
+ self.state = VarState::DontWarn
+ },
+ _ => (),
+ }
+ }
+
+ if self.past_loop {
+ self.state = VarState::DontWarn;
+ return;
+ }
+ } else if !self.past_loop && is_loop(expr) {
+ self.state = VarState::DontWarn;
+ return;
+ } else if is_conditional(expr) {
+ self.depth += 1;
+ walk_expr(self, expr);
+ self.depth -= 1;
+ return;
+ }
+ walk_expr(self, expr);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+}
+
+fn var_def_id(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<HirId> {
+ if let ExprKind::Path(ref qpath) = expr.kind {
+ let path_res = qpath_res(cx, qpath, expr.hir_id);
+ if let Res::Local(hir_id) = path_res {
+ return Some(hir_id);
+ }
+ }
+ None
+}
+
+fn is_loop(expr: &Expr<'_>) -> bool {
+ match expr.kind {
+ ExprKind::Loop(..) => true,
+ _ => false,
+ }
+}
+
+fn is_conditional(expr: &Expr<'_>) -> bool {
+ match expr.kind {
+ ExprKind::Match(..) => true,
+ _ => false,
+ }
+}
+
+fn is_nested(cx: &LateContext<'_, '_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
+ if_chain! {
+ if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id);
+ let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id);
+ if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node);
+ then {
+ return is_loop_nested(cx, loop_expr, iter_expr)
+ }
+ }
+ false
+}
+
+fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
+ let mut id = loop_expr.hir_id;
+ let iter_name = if let Some(name) = path_name(iter_expr) {
+ name
+ } else {
+ return true;
+ };
+ loop {
+ let parent = cx.tcx.hir().get_parent_node(id);
+ if parent == id {
+ return false;
+ }
+ match cx.tcx.hir().find(parent) {
+ Some(Node::Expr(expr)) => {
+ if let ExprKind::Loop(..) = expr.kind {
+ return true;
+ };
+ },
+ Some(Node::Block(block)) => {
+ let mut block_visitor = LoopNestVisitor {
+ hir_id: id,
+ iterator: iter_name,
+ nesting: Unknown,
+ };
+ walk_block(&mut block_visitor, block);
+ if block_visitor.nesting == RuledOut {
+ return false;
+ }
+ },
+ Some(Node::Stmt(_)) => (),
+ _ => {
+ return false;
+ },
+ }
+ id = parent;
+ }
+}
+
+#[derive(PartialEq, Eq)]
+enum Nesting {
+ Unknown, // no nesting detected yet
+ RuledOut, // the iterator is initialized or assigned within scope
+ LookFurther, // no nesting detected, no further walk required
+}
+
+use self::Nesting::{LookFurther, RuledOut, Unknown};
+
+struct LoopNestVisitor {
+ hir_id: HirId,
+ iterator: Name,
+ nesting: Nesting,
+}
+
+impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
+ type Map = Map<'tcx>;
+
+ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+ if stmt.hir_id == self.hir_id {
+ self.nesting = LookFurther;
+ } else if self.nesting == Unknown {
+ walk_stmt(self, stmt);
+ }
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.nesting != Unknown {
+ return;
+ }
+ if expr.hir_id == self.hir_id {
+ self.nesting = LookFurther;
+ return;
+ }
+ match expr.kind {
+ ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => {
+ if match_var(path, self.iterator) {
+ self.nesting = RuledOut;
+ }
+ },
+ _ => walk_expr(self, expr),
+ }
+ }
+
+ fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
+ if self.nesting != Unknown {
+ return;
+ }
+ if let PatKind::Binding(.., span_name, _) = pat.kind {
+ if self.iterator == span_name.name {
+ self.nesting = RuledOut;
+ return;
+ }
+ }
+ walk_pat(self, pat)
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+fn path_name(e: &Expr<'_>) -> Option<Name> {
+ if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind {
+ let segments = &path.segments;
+ if segments.len() == 1 {
+ return Some(segments[0].ident.name);
+ }
+ };
+ None
+}
+
+fn check_infinite_loop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
+ if constant(cx, cx.tables, cond).is_some() {
+ // A pure constant condition (e.g., `while false`) is not linted.
+ return;
+ }
+
+ let mut var_visitor = VarCollectorVisitor {
+ cx,
+ ids: FxHashSet::default(),
+ def_ids: FxHashMap::default(),
+ skip: false,
+ };
+ var_visitor.visit_expr(cond);
+ if var_visitor.skip {
+ return;
+ }
+ let used_in_condition = &var_visitor.ids;
+ let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) {
+ used_in_condition.is_disjoint(&used_mutably)
+ } else {
+ return;
+ };
+ let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v);
+
+ let mut has_break_or_return_visitor = HasBreakOrReturnVisitor {
+ has_break_or_return: false,
+ };
+ has_break_or_return_visitor.visit_expr(expr);
+ let has_break_or_return = has_break_or_return_visitor.has_break_or_return;
+
+ if no_cond_variable_mutated && !mutable_static_in_cond {
+ span_lint_and_then(
+ cx,
+ WHILE_IMMUTABLE_CONDITION,
+ cond.span,
+ "variables in the condition are not mutated in the loop body",
+ |diag| {
+ diag.note("this may lead to an infinite or to a never running loop");
+
+ if has_break_or_return {
+ diag.note("this loop contains `return`s or `break`s");
+ diag.help("rewrite it as `if cond { loop { } }`");
+ }
+ },
+ );
+ }
+}
+
+struct HasBreakOrReturnVisitor {
+ has_break_or_return: bool,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.has_break_or_return {
+ return;
+ }
+
+ match expr.kind {
+ ExprKind::Ret(_) | ExprKind::Break(_, _) => {
+ self.has_break_or_return = true;
+ return;
+ },
+ _ => {},
+ }
+
+ walk_expr(self, expr);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Collects the set of variables in an expression
+/// Stops analysis if a function call is found
+/// Note: In some cases such as `self`, there are no mutable annotation,
+/// All variables definition IDs are collected
+struct VarCollectorVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>,
+ ids: FxHashSet<HirId>,
+ def_ids: FxHashMap<def_id::DefId, bool>,
+ skip: bool,
+}
+
+impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
+ fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Path(ref qpath) = ex.kind;
+ if let QPath::Resolved(None, _) = *qpath;
+ let res = qpath_res(self.cx, qpath, ex.hir_id);
+ then {
+ match res {
+ Res::Local(hir_id) => {
+ self.ids.insert(hir_id);
+ },
+ Res::Def(DefKind::Static, def_id) => {
+ let mutable = self.cx.tcx.is_mutable_static(def_id);
+ self.def_ids.insert(def_id, mutable);
+ },
+ _ => {},
+ }
+ }
+ }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
+ match ex.kind {
+ ExprKind::Path(_) => self.insert_def_id(ex),
+ // If there is any function/method call… we just stop analysis
+ ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true,
+
+ _ => walk_expr(self, ex),
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
+
+fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, 'tcx>) {
+ if_chain! {
+ if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind;
+ if let ExprKind::MethodCall(ref chain_method, _, _) = args[0].kind;
+ if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR);
+ if let Some(ref generic_args) = chain_method.args;
+ if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
+ then {
+ let ty = cx.tables.node_type(ty.hir_id);
+ if is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
+ is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
+ match_type(cx, ty, &paths::BTREEMAP) ||
+ is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
+ if method.ident.name == sym!(len) {
+ let span = shorten_needless_collect_span(expr);
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_COLLECT,
+ span,
+ NEEDLESS_COLLECT_MSG,
+ "replace with",
+ ".count()".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ if method.ident.name == sym!(is_empty) {
+ let span = shorten_needless_collect_span(expr);
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_COLLECT,
+ span,
+ NEEDLESS_COLLECT_MSG,
+ "replace with",
+ ".next().is_none()".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ if method.ident.name == sym!(contains) {
+ let contains_arg = snippet(cx, args[1].span, "??");
+ let span = shorten_needless_collect_span(expr);
+ span_lint_and_then(
+ cx,
+ NEEDLESS_COLLECT,
+ span,
+ NEEDLESS_COLLECT_MSG,
+ |diag| {
+ let (arg, pred) = if contains_arg.starts_with('&') {
+ ("x", &contains_arg[1..])
+ } else {
+ ("&x", &*contains_arg)
+ };
+ diag.span_suggestion(
+ span,
+ "replace with",
+ format!(
+ ".any(|{}| x == {})",
+ arg, pred
+ ),
+ Applicability::MachineApplicable,
+ );
+ }
+ );
+ }
+ }
+ }
+ }
+}
+
+fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
+ if_chain! {
+ if let ExprKind::MethodCall(_, _, ref args) = expr.kind;
+ if let ExprKind::MethodCall(_, ref span, _) = args[0].kind;
+ then {
+ return expr.span.with_lo(span.lo() - BytePos(1));
+ }
+ }
+ unreachable!()
+}
--- /dev/null
--- /dev/null
++use crate::utils::paths::FUTURE_FROM_GENERATOR;
++use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{
++ AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync,
++ ItemKind, TraitRef, Ty, TyKind, TypeBindingKind,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** It checks for manual implementations of `async` functions.
++ ///
++ /// **Why is this bad?** It's more idiomatic to use the dedicated syntax.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// use std::future::Future;
++ ///
++ /// fn foo() -> impl Future<Output = i32> { async { 42 } }
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// use std::future::Future;
++ ///
++ /// async fn foo() -> i32 { 42 }
++ /// ```
++ pub MANUAL_ASYNC_FN,
++ style,
++ "manual implementations of `async` functions can be simplified using the dedicated syntax"
++}
++
++declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ kind: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ span: Span,
++ _: HirId,
++ ) {
++ if_chain! {
++ if let Some(header) = kind.header();
++ if let IsAsync::NotAsync = header.asyncness;
++ // Check that this function returns `impl Future`
++ if let FnRetTy::Return(ret_ty) = decl.output;
++ if let Some(trait_ref) = future_trait_ref(cx, ret_ty);
++ if let Some(output) = future_output_ty(trait_ref);
++ // Check that the body of the function consists of one async block
++ if let ExprKind::Block(block, _) = body.value.kind;
++ if block.stmts.is_empty();
++ if let Some(closure_body) = desugared_async_block(cx, block);
++ then {
++ let header_span = span.with_hi(ret_ty.span.hi());
++
++ span_lint_and_then(
++ cx,
++ MANUAL_ASYNC_FN,
++ header_span,
++ "this function can be simplified using the `async fn` syntax",
++ |diag| {
++ if_chain! {
++ if let Some(header_snip) = snippet_opt(cx, header_span);
++ if let Some(ret_pos) = header_snip.rfind("->");
++ if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
++ then {
++ let help = format!("make the function `async` and {}", ret_sugg);
++ diag.span_suggestion(
++ header_span,
++ &help,
++ format!("async {}{}", &header_snip[..ret_pos], ret_snip),
++ Applicability::MachineApplicable
++ );
++
++ let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
++ diag.span_suggestion(
++ block.span,
++ "move the body of the async block to the enclosing function",
++ body_snip.to_string(),
++ Applicability::MachineApplicable
++ );
++ }
++ }
++ },
++ );
++ }
++ }
++ }
++}
++
++fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
++ if_chain! {
++ if let TyKind::Def(item_id, _) = ty.kind;
++ let item = cx.tcx.hir().item(item_id.id);
++ if let ItemKind::OpaqueTy(opaque) = &item.kind;
++ if opaque.bounds.len() == 1;
++ if let GenericBound::Trait(poly, _) = &opaque.bounds[0];
++ if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
++ then {
++ return Some(&poly.trait_ref);
++ }
++ }
++
++ None
++}
++
++fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
++ if_chain! {
++ if let Some(segment) = trait_ref.path.segments.last();
++ if let Some(args) = segment.args;
++ if args.bindings.len() == 1;
++ let binding = &args.bindings[0];
++ if binding.ident.as_str() == "Output";
++ if let TypeBindingKind::Equality{ty: output} = binding.kind;
++ then {
++ return Some(output)
++ }
++ }
++
++ None
++}
++
++fn desugared_async_block<'tcx>(cx: &LateContext<'_, 'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
++ if_chain! {
++ if let Some(block_expr) = block.expr;
++ if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR);
++ if args.len() == 1;
++ if let Expr{kind: ExprKind::Closure(_, _, body_id, ..), ..} = args[0];
++ let closure_body = cx.tcx.hir().body(body_id);
++ if let Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) = closure_body.generator_kind;
++ then {
++ return Some(closure_body);
++ }
++ }
++
++ None
++}
++
++fn suggested_ret(cx: &LateContext<'_, '_>, output: &Ty<'_>) -> Option<(&'static str, String)> {
++ match output.kind {
++ TyKind::Tup(tys) if tys.is_empty() => {
++ let sugg = "remove the return type";
++ Some((sugg, "".into()))
++ },
++ _ => {
++ let sugg = "return the output of the future directly";
++ snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip)))
++ },
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{snippet_opt, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
++use rustc_attr as attr;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
++ ///
++ /// **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent
++ /// and allows possible optimizations when applied to enums.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// struct S {
++ /// pub a: i32,
++ /// pub b: i32,
++ /// _c: (),
++ /// }
++ ///
++ /// enum E {
++ /// A,
++ /// B,
++ /// #[doc(hidden)]
++ /// _C,
++ /// }
++ ///
++ /// struct T(pub i32, pub i32, ());
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// #[non_exhaustive]
++ /// struct S {
++ /// pub a: i32,
++ /// pub b: i32,
++ /// }
++ ///
++ /// #[non_exhaustive]
++ /// enum E {
++ /// A,
++ /// B,
++ /// }
++ ///
++ /// #[non_exhaustive]
++ /// struct T(pub i32, pub i32);
++ /// ```
++ pub MANUAL_NON_EXHAUSTIVE,
++ style,
++ "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
++}
++
++declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
++
++impl EarlyLintPass for ManualNonExhaustive {
++ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++ match &item.kind {
++ ItemKind::Enum(def, _) => {
++ check_manual_non_exhaustive_enum(cx, item, &def.variants);
++ },
++ ItemKind::Struct(variant_data, _) => {
++ if let VariantData::Unit(..) = variant_data {
++ return;
++ }
++
++ check_manual_non_exhaustive_struct(cx, item, variant_data);
++ },
++ _ => {},
++ }
++ }
++}
++
++fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
++ fn is_non_exhaustive_marker(variant: &Variant) -> bool {
++ matches!(variant.data, VariantData::Unit(_))
++ && variant.ident.as_str().starts_with('_')
++ && variant.attrs.iter().any(|a| is_doc_hidden(a))
++ }
++
++ fn is_doc_hidden(attr: &Attribute) -> bool {
++ attr.check_name(sym!(doc))
++ && match attr.meta_item_list() {
++ Some(l) => attr::list_contains_name(&l, sym!(hidden)),
++ None => false,
++ }
++ }
++
++ if_chain! {
++ let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
++ if let Some(marker) = markers.next();
++ if markers.count() == 0 && variants.len() > 1;
++ then {
++ span_lint_and_then(
++ cx,
++ MANUAL_NON_EXHAUSTIVE,
++ item.span,
++ "this seems like a manual implementation of the non-exhaustive pattern",
++ |diag| {
++ if_chain! {
++ if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
++ let header_span = cx.sess.source_map().span_until_char(item.span, '{');
++ if let Some(snippet) = snippet_opt(cx, header_span);
++ then {
++ diag.span_suggestion(
++ header_span,
++ "add the attribute",
++ format!("#[non_exhaustive] {}", snippet),
++ Applicability::Unspecified,
++ );
++ }
++ }
++ diag.span_help(marker.span, "remove this variant");
++ });
++ }
++ }
++}
++
++fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
++ fn is_private(field: &StructField) -> bool {
++ matches!(field.vis.node, VisibilityKind::Inherited)
++ }
++
++ fn is_non_exhaustive_marker(field: &StructField) -> bool {
++ is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
++ }
++
++ fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
++ let delimiter = match data {
++ VariantData::Struct(..) => '{',
++ VariantData::Tuple(..) => '(',
++ VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
++ };
++
++ cx.sess.source_map().span_until_char(item.span, delimiter)
++ }
++
++ let fields = data.fields();
++ let private_fields = fields.iter().filter(|f| is_private(f)).count();
++ let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count();
++
++ if_chain! {
++ if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
++ if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f));
++ then {
++ span_lint_and_then(
++ cx,
++ MANUAL_NON_EXHAUSTIVE,
++ item.span,
++ "this seems like a manual implementation of the non-exhaustive pattern",
++ |diag| {
++ if_chain! {
++ if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
++ let header_span = find_header_span(cx, item, data);
++ if let Some(snippet) = snippet_opt(cx, header_span);
++ then {
++ diag.span_suggestion(
++ header_span,
++ "add the attribute",
++ format!("#[non_exhaustive] {}", snippet),
++ Applicability::Unspecified,
++ );
++ }
++ }
++ diag.span_help(marker.span, "remove this field");
++ });
++ }
++ }
++}
--- /dev/null
- use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
++use crate::utils::{self, is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, MatchSource};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.
+ ///
+ /// **Why is this bad?** This can panic at runtime.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust, no_run
+ /// let arr = vec![0, 1, 2, 3];
+ /// let idx = 1;
+ ///
+ /// // Bad
+ /// match arr[idx] {
+ /// 0 => println!("{}", 0),
+ /// 1 => println!("{}", 3),
+ /// _ => {},
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust, no_run
+ /// let arr = vec![0, 1, 2, 3];
+ /// let idx = 1;
+ ///
+ /// // Good
+ /// match arr.get(idx) {
+ /// Some(0) => println!("{}", 0),
+ /// Some(1) => println!("{}", 3),
+ /// _ => {},
+ /// }
+ /// ```
+ pub MATCH_ON_VEC_ITEMS,
- correctness,
++ pedantic,
+ "matching on vector elements can panic"
+}
+
+declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchOnVecItems {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
+ if_chain! {
+ if !in_external_macro(cx.sess(), expr.span);
+ if let ExprKind::Match(ref match_expr, _, MatchSource::Normal) = expr.kind;
+ if let Some(idx_expr) = is_vec_indexing(cx, match_expr);
+ if let ExprKind::Index(vec, idx) = idx_expr.kind;
+
+ then {
+ // FIXME: could be improved to suggest surrounding every pattern with Some(_),
+ // but only when `or_patterns` are stabilized.
+ span_lint_and_sugg(
+ cx,
+ MATCH_ON_VEC_ITEMS,
+ match_expr.span,
+ "indexing into a vector may panic",
+ "try this",
+ format!(
+ "{}.get({})",
+ snippet(cx, vec.span, ".."),
+ snippet(cx, idx.span, "..")
+ ),
+ Applicability::MaybeIncorrect
+ );
+ }
+ }
+ }
+}
+
+fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ if_chain! {
- if let ExprKind::Index(ref array, _) = expr.kind;
- let ty = cx.tables.expr_ty(array);
- let ty = walk_ptrs_ty(ty);
- if is_type_diagnostic_item(cx, ty, sym!(vec_type));
++ if let ExprKind::Index(ref array, ref index) = expr.kind;
++ if is_vector(cx, array);
++ if !is_full_range(cx, index);
+
+ then {
+ return Some(expr);
+ }
+ }
+
+ None
+}
++
++fn is_vector(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ let ty = cx.tables.expr_ty(expr);
++ let ty = walk_ptrs_ty(ty);
++ is_type_diagnostic_item(cx, ty, sym!(vec_type))
++}
++
++fn is_full_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ let ty = cx.tables.expr_ty(expr);
++ let ty = walk_ptrs_ty(ty);
++ match_type(cx, ty, &utils::paths::RANGE_FULL)
++}
--- /dev/null
- /// ```ignore
- /// let { a: _, b: ref b, c: _ } = ..
+use crate::utils::{
+ constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg,
+ span_lint_and_then,
+};
+use if_chain::if_chain;
+use rustc_ast::ast::{
+ BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability,
+ NodeId, Pat, PatKind, StmtKind, UnOp,
+};
+use rustc_ast::visit::{walk_expr, FnKind, Visitor};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for structure field patterns bound to wildcards.
+ ///
+ /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
+ /// the fields that are actually bound.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
++ /// ```rust
++ /// # struct Foo {
++ /// # a: i32,
++ /// # b: i32,
++ /// # c: i32,
++ /// # }
++ /// let f = Foo { a: 0, b: 0, c: 0 };
++ ///
++ /// // Bad
++ /// match f {
++ /// Foo { a: _, b: 0, .. } => {},
++ /// Foo { a: _, b: _, c: _ } => {},
++ /// }
++ ///
++ /// // Good
++ /// match f {
++ /// Foo { b: 0, .. } => {},
++ /// Foo { .. } => {},
++ /// }
+ /// ```
+ pub UNNEEDED_FIELD_PATTERN,
+ restriction,
+ "struct fields bound to a wildcard instead of using `..`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for function arguments having the similar names
+ /// differing by an underscore.
+ ///
+ /// **Why is this bad?** It affects code readability.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// fn foo(a: i32, _a: i32) {}
+ /// ```
+ pub DUPLICATE_UNDERSCORE_ARGUMENT,
+ style,
+ "function arguments having names which only differ by an underscore"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Detects closures called in the same expression where they
+ /// are defined.
+ ///
+ /// **Why is this bad?** It is unnecessarily adding to the expression's
+ /// complexity.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// (|| 42)()
+ /// ```
+ pub REDUNDANT_CLOSURE_CALL,
+ complexity,
+ "throwaway closures called in the expression they are defined"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Detects expressions of the form `--x`.
+ ///
+ /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
+ /// decremented.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let mut x = 3;
+ /// --x;
+ /// ```
+ pub DOUBLE_NEG,
+ style,
+ "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Warns on hexadecimal literals with mixed-case letter
+ /// digits.
+ ///
+ /// **Why is this bad?** It looks confusing.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let y = 0x1a9BAcD;
+ /// ```
+ pub MIXED_CASE_HEX_LITERALS,
+ style,
+ "hex literals whose letter digits are not consistently upper- or lowercased"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Warns if literal suffixes are not separated by an
+ /// underscore.
+ ///
+ /// **Why is this bad?** It is much less readable.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let y = 123832i32;
+ /// ```
+ pub UNSEPARATED_LITERAL_SUFFIX,
+ pedantic,
+ "literals whose suffix is not separated by an underscore"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Warns if an integral constant literal starts with `0`.
+ ///
+ /// **Why is this bad?** In some languages (including the infamous C language
+ /// and most of its
+ /// family), this marks an octal constant. In Rust however, this is a decimal
+ /// constant. This could
+ /// be confusing for both the writer and a reader of the constant.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// In Rust:
+ /// ```rust
+ /// fn main() {
+ /// let a = 0123;
+ /// println!("{}", a);
+ /// }
+ /// ```
+ ///
+ /// prints `123`, while in C:
+ ///
+ /// ```c
+ /// #include <stdio.h>
+ ///
+ /// int main() {
+ /// int a = 0123;
+ /// printf("%d\n", a);
+ /// }
+ /// ```
+ ///
+ /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
+ pub ZERO_PREFIXED_LITERAL,
+ complexity,
+ "integer literals starting with `0`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Warns if a generic shadows a built-in type.
+ ///
+ /// **Why is this bad?** This gives surprising type errors.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```ignore
+ /// impl<u32> Foo<u32> {
+ /// fn impl_func(&self) -> u32 {
+ /// 42
+ /// }
+ /// }
+ /// ```
+ pub BUILTIN_TYPE_SHADOW,
+ style,
+ "shadowing a builtin type"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for patterns in the form `name @ _`.
+ ///
+ /// **Why is this bad?** It's almost always more readable to just use direct
+ /// bindings.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let v = Some("abc");
+ ///
+ /// match v {
+ /// Some(x) => (),
+ /// y @ _ => (), // easier written as `y`,
+ /// }
+ /// ```
+ pub REDUNDANT_PATTERN,
+ style,
+ "using `name @ _` in a pattern"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for tuple patterns with a wildcard
+ /// pattern (`_`) is next to a rest pattern (`..`).
+ ///
+ /// _NOTE_: While `_, ..` means there is at least one element left, `..`
+ /// means there are 0 or more elements left. This can make a difference
+ /// when refactoring, but shouldn't result in errors in the refactored code,
+ /// since the wildcard pattern isn't used anyway.
+ /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
+ /// can match that element as well.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # struct TupleStruct(u32, u32, u32);
+ /// # let t = TupleStruct(1, 2, 3);
+ ///
+ /// match t {
+ /// TupleStruct(0, .., _) => (),
+ /// _ => (),
+ /// }
+ /// ```
+ /// can be written as
+ /// ```rust
+ /// # struct TupleStruct(u32, u32, u32);
+ /// # let t = TupleStruct(1, 2, 3);
+ ///
+ /// match t {
+ /// TupleStruct(0, ..) => (),
+ /// _ => (),
+ /// }
+ /// ```
+ pub UNNEEDED_WILDCARD_PATTERN,
+ complexity,
+ "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
+}
+
+declare_lint_pass!(MiscEarlyLints => [
+ UNNEEDED_FIELD_PATTERN,
+ DUPLICATE_UNDERSCORE_ARGUMENT,
+ REDUNDANT_CLOSURE_CALL,
+ DOUBLE_NEG,
+ MIXED_CASE_HEX_LITERALS,
+ UNSEPARATED_LITERAL_SUFFIX,
+ ZERO_PREFIXED_LITERAL,
+ BUILTIN_TYPE_SHADOW,
+ REDUNDANT_PATTERN,
+ UNNEEDED_WILDCARD_PATTERN,
+]);
+
+// Used to find `return` statements or equivalents e.g., `?`
+struct ReturnVisitor {
+ found_return: bool,
+}
+
+impl ReturnVisitor {
+ #[must_use]
+ fn new() -> Self {
+ Self { found_return: false }
+ }
+}
+
+impl<'ast> Visitor<'ast> for ReturnVisitor {
+ fn visit_expr(&mut self, ex: &'ast Expr) {
+ if let ExprKind::Ret(_) = ex.kind {
+ self.found_return = true;
+ } else if let ExprKind::Try(_) = ex.kind {
+ self.found_return = true;
+ }
+
+ walk_expr(self, ex)
+ }
+}
+
+impl EarlyLintPass for MiscEarlyLints {
+ fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
+ for param in &gen.params {
+ if let GenericParamKind::Type { .. } = param.kind {
+ let name = param.ident.as_str();
+ if constants::BUILTIN_TYPES.contains(&&*name) {
+ span_lint(
+ cx,
+ BUILTIN_TYPE_SHADOW,
+ param.ident.span,
+ &format!("This generic shadows the built-in type `{}`", name),
+ );
+ }
+ }
+ }
+ }
+
+ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
+ if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
+ let mut wilds = 0;
+ let type_name = npat
+ .segments
+ .last()
+ .expect("A path must have at least one segment")
+ .ident
+ .name;
+
+ for field in pfields {
+ if let PatKind::Wild = field.pat.kind {
+ wilds += 1;
+ }
+ }
+ if !pfields.is_empty() && wilds == pfields.len() {
+ span_lint_and_help(
+ cx,
+ UNNEEDED_FIELD_PATTERN,
+ pat.span,
+ "All the struct fields are matched to a wildcard pattern, consider using `..`.",
+ None,
+ &format!("Try with `{} {{ .. }}` instead", type_name),
+ );
+ return;
+ }
+ if wilds > 0 {
+ for field in pfields {
+ if let PatKind::Wild = field.pat.kind {
+ wilds -= 1;
+ if wilds > 0 {
+ span_lint(
+ cx,
+ UNNEEDED_FIELD_PATTERN,
+ field.span,
+ "You matched a field with a wildcard pattern. Consider using `..` instead",
+ );
+ } else {
+ let mut normal = vec![];
+
+ for field in pfields {
+ match field.pat.kind {
+ PatKind::Wild => {},
+ _ => {
+ if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
+ normal.push(n);
+ }
+ },
+ }
+ }
+
+ span_lint_and_help(
+ cx,
+ UNNEEDED_FIELD_PATTERN,
+ field.span,
+ "You matched a field with a wildcard pattern. Consider using `..` \
+ instead",
+ None,
+ &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
+ );
+ }
+ }
+ }
+ }
+ }
+
+ if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
+ let left_binding = match left {
+ BindingMode::ByRef(Mutability::Mut) => "ref mut ",
+ BindingMode::ByRef(Mutability::Not) => "ref ",
+ _ => "",
+ };
+
+ if let PatKind::Wild = right.kind {
+ span_lint_and_sugg(
+ cx,
+ REDUNDANT_PATTERN,
+ pat.span,
+ &format!(
+ "the `{} @ _` pattern can be written as just `{}`",
+ ident.name, ident.name,
+ ),
+ "try",
+ format!("{}{}", left_binding, ident.name),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ check_unneeded_wildcard_pattern(cx, pat);
+ }
+
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
+ let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
+
+ for arg in &fn_kind.decl().inputs {
+ if let PatKind::Ident(_, ident, None) = arg.pat.kind {
+ let arg_name = ident.to_string();
+
+ if arg_name.starts_with('_') {
+ if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
+ span_lint(
+ cx,
+ DUPLICATE_UNDERSCORE_ARGUMENT,
+ *correspondence,
+ &format!(
+ "`{}` already exists, having another argument having almost the same \
+ name makes code comprehension and documentation more difficult",
+ arg_name[1..].to_owned()
+ ),
+ );
+ }
+ } else {
+ registered_names.insert(arg_name, arg.pat.span);
+ }
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+ match expr.kind {
+ ExprKind::Call(ref paren, _) => {
+ if let ExprKind::Paren(ref closure) = paren.kind {
+ if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind {
+ let mut visitor = ReturnVisitor::new();
+ visitor.visit_expr(block);
+ if !visitor.found_return {
+ span_lint_and_then(
+ cx,
+ REDUNDANT_CLOSURE_CALL,
+ expr.span,
+ "Try not to call a closure in the expression where it is declared.",
+ |diag| {
+ if decl.inputs.is_empty() {
+ let mut app = Applicability::MachineApplicable;
+ let hint =
+ snippet_with_applicability(cx, block.span, "..", &mut app).into_owned();
+ diag.span_suggestion(expr.span, "Try doing something like: ", hint, app);
+ }
+ },
+ );
+ }
+ }
+ }
+ },
+ ExprKind::Unary(UnOp::Neg, ref inner) => {
+ if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
+ span_lint(
+ cx,
+ DOUBLE_NEG,
+ expr.span,
+ "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
+ );
+ }
+ },
+ ExprKind::Lit(ref lit) => Self::check_lit(cx, lit),
+ _ => (),
+ }
+ }
+
+ fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
+ for w in block.stmts.windows(2) {
+ if_chain! {
+ if let StmtKind::Local(ref local) = w[0].kind;
+ if let Option::Some(ref t) = local.init;
+ if let ExprKind::Closure(..) = t.kind;
+ if let PatKind::Ident(_, ident, _) = local.pat.kind;
+ if let StmtKind::Semi(ref second) = w[1].kind;
+ if let ExprKind::Assign(_, ref call, _) = second.kind;
+ if let ExprKind::Call(ref closure, _) = call.kind;
+ if let ExprKind::Path(_, ref path) = closure.kind;
+ then {
+ if ident == path.segments[0].ident {
+ span_lint(
+ cx,
+ REDUNDANT_CLOSURE_CALL,
+ second.span,
+ "Closure called just once immediately after it was declared",
+ );
+ }
+ }
+ }
+ }
+ }
+}
+
+impl MiscEarlyLints {
+ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
+ // We test if first character in snippet is a number, because the snippet could be an expansion
+ // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
+ // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
+ // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
+ // FIXME: Find a better way to detect those cases.
+ let lit_snip = match snippet_opt(cx, lit.span) {
+ Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
+ _ => return,
+ };
+
+ if let LitKind::Int(value, lit_int_type) = lit.kind {
+ let suffix = match lit_int_type {
+ LitIntType::Signed(ty) => ty.name_str(),
+ LitIntType::Unsigned(ty) => ty.name_str(),
+ LitIntType::Unsuffixed => "",
+ };
+
+ let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
+ val
+ } else {
+ return; // It's useless so shouldn't lint.
+ };
+ // Do not lint when literal is unsuffixed.
+ if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
+ span_lint_and_sugg(
+ cx,
+ UNSEPARATED_LITERAL_SUFFIX,
+ lit.span,
+ "integer type suffix should be separated by an underscore",
+ "add an underscore",
+ format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ if lit_snip.starts_with("0x") {
+ if maybe_last_sep_idx <= 2 {
+ // It's meaningless or causes range error.
+ return;
+ }
+ let mut seen = (false, false);
+ for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
+ match ch {
+ b'a'..=b'f' => seen.0 = true,
+ b'A'..=b'F' => seen.1 = true,
+ _ => {},
+ }
+ if seen.0 && seen.1 {
+ span_lint(
+ cx,
+ MIXED_CASE_HEX_LITERALS,
+ lit.span,
+ "inconsistent casing in hexadecimal literal",
+ );
+ break;
+ }
+ }
+ } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
+ /* nothing to do */
+ } else if value != 0 && lit_snip.starts_with('0') {
+ span_lint_and_then(
+ cx,
+ ZERO_PREFIXED_LITERAL,
+ lit.span,
+ "this is a decimal constant",
+ |diag| {
+ diag.span_suggestion(
+ lit.span,
+ "if you mean to use a decimal constant, remove the `0` to avoid confusion",
+ lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ diag.span_suggestion(
+ lit.span,
+ "if you mean to use an octal constant, use `0o`",
+ format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
+ } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
+ let suffix = float_ty.name_str();
+ let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
+ val
+ } else {
+ return; // It's useless so shouldn't lint.
+ };
+ if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
+ span_lint_and_sugg(
+ cx,
+ UNSEPARATED_LITERAL_SUFFIX,
+ lit.span,
+ "float type suffix should be separated by an underscore",
+ "add an underscore",
+ format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
+
+fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
+ if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
+ fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
+ span_lint_and_sugg(
+ cx,
+ UNNEEDED_WILDCARD_PATTERN,
+ span,
+ if only_one {
+ "this pattern is unneeded as the `..` pattern can match that element"
+ } else {
+ "these patterns are unneeded as the `..` pattern can match those elements"
+ },
+ if only_one { "remove it" } else { "remove them" },
+ "".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
+ if let PatKind::Wild = pat.kind {
+ true
+ } else {
+ false
+ }
+ }
+
+ if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
+ if let Some((left_index, left_pat)) = patterns[..rest_index]
+ .iter()
+ .rev()
+ .take_while(is_wild)
+ .enumerate()
+ .last()
+ {
+ span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
+ }
+
+ if let Some((right_index, right_pat)) =
+ patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
+ {
+ span_lint(
+ cx,
+ patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
+ right_index == 0,
+ );
+ }
+ }
+ }
+}
--- /dev/null
- use crate::utils::{higher::if_block, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated};
++use crate::utils::{
++ differing_macro_contexts, higher::if_block, is_type_diagnostic_item, span_lint_and_then,
++ usage::is_potentially_mutated,
++};
+use if_chain::if_chain;
+use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
+ ///
+ /// **Why is this bad?** Using `if let` or `match` is more idiomatic.
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let option = Some(0);
+ /// # fn do_something_with(_x: usize) {}
+ /// if option.is_some() {
+ /// do_something_with(option.unwrap())
+ /// }
+ /// ```
+ ///
+ /// Could be written:
+ ///
+ /// ```rust
+ /// # let option = Some(0);
+ /// # fn do_something_with(_x: usize) {}
+ /// if let Some(value) = option {
+ /// do_something_with(value)
+ /// }
+ /// ```
+ pub UNNECESSARY_UNWRAP,
+ complexity,
+ "checks for calls of `unwrap[_err]()` that cannot fail"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.
+ ///
+ /// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
+ ///
+ /// **Known problems:** This lint only checks `if` conditions not assignments.
+ /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let option = Some(0);
+ /// # fn do_something_with(_x: usize) {}
+ /// if option.is_none() {
+ /// do_something_with(option.unwrap())
+ /// }
+ /// ```
+ ///
+ /// This code will always panic. The if condition should probably be inverted.
+ pub PANICKING_UNWRAP,
+ correctness,
+ "checks for calls of `unwrap[_err]()` that will always fail"
+}
+
+/// Visitor that keeps track of which variables are unwrappable.
+struct UnwrappableVariablesVisitor<'a, 'tcx> {
+ unwrappables: Vec<UnwrapInfo<'tcx>>,
+ cx: &'a LateContext<'a, 'tcx>,
+}
+/// Contains information about whether a variable can be unwrapped.
+#[derive(Copy, Clone, Debug)]
+struct UnwrapInfo<'tcx> {
+ /// The variable that is checked
+ ident: &'tcx Path<'tcx>,
+ /// The check, like `x.is_ok()`
+ check: &'tcx Expr<'tcx>,
++ /// The branch where the check takes place, like `if x.is_ok() { .. }`
++ branch: &'tcx Expr<'tcx>,
+ /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
+ safe_to_unwrap: bool,
+}
+
+/// Collects the information about unwrappable variables from an if condition
+/// The `invert` argument tells us whether the condition is negated.
+fn collect_unwrap_info<'a, 'tcx>(
+ cx: &'a LateContext<'a, 'tcx>,
+ expr: &'tcx Expr<'_>,
++ branch: &'tcx Expr<'_>,
+ invert: bool,
+) -> Vec<UnwrapInfo<'tcx>> {
+ if let ExprKind::Binary(op, left, right) = &expr.kind {
+ match (invert, op.node) {
+ (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => {
- let mut unwrap_info = collect_unwrap_info(cx, left, invert);
- unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
++ let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert);
++ unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert));
+ return unwrap_info;
+ },
+ _ => (),
+ }
+ } else if let ExprKind::Unary(UnOp::UnNot, expr) = &expr.kind {
- return collect_unwrap_info(cx, expr, !invert);
++ return collect_unwrap_info(cx, expr, branch, !invert);
+ } else {
+ if_chain! {
+ if let ExprKind::MethodCall(method_name, _, args) = &expr.kind;
+ if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind;
+ let ty = cx.tables.expr_ty(&args[0]);
+ if is_type_diagnostic_item(cx, ty, sym!(option_type)) || is_type_diagnostic_item(cx, ty, sym!(result_type));
+ let name = method_name.ident.as_str();
+ if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name);
+ then {
+ assert!(args.len() == 1);
+ let unwrappable = match name.as_ref() {
+ "is_some" | "is_ok" => true,
+ "is_err" | "is_none" => false,
+ _ => unreachable!(),
+ };
+ let safe_to_unwrap = unwrappable != invert;
- return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
++ return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }];
+ }
+ }
+ }
+ Vec::new()
+}
+
+impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
+ fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) {
+ let prev_len = self.unwrappables.len();
- for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
++ for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) {
+ if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
+ || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
+ {
+ // if the variable is mutated, we don't know whether it can be unwrapped:
+ continue;
+ }
+ self.unwrappables.push(unwrap_info);
+ }
+ walk_expr(self, branch);
+ self.unwrappables.truncate(prev_len);
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ // Shouldn't lint when `expr` is in macro.
+ if in_external_macro(self.cx.tcx.sess, expr.span) {
+ return;
+ }
+ if let Some((cond, then, els)) = if_block(&expr) {
+ walk_expr(self, cond);
+ self.visit_branch(cond, then, false);
+ if let Some(els) = els {
+ self.visit_branch(cond, els, true);
+ }
+ } else {
+ // find `unwrap[_err]()` calls:
+ if_chain! {
+ if let ExprKind::MethodCall(ref method_name, _, ref args) = expr.kind;
+ if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind;
+ if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name);
+ let call_to_unwrap = method_name.ident.name == sym!(unwrap);
+ if let Some(unwrappable) = self.unwrappables.iter()
+ .find(|u| u.ident.res == path.res);
++ // Span contexts should not differ with the conditional branch
++ if !differing_macro_contexts(unwrappable.branch.span, expr.span);
++ if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span);
+ then {
+ if call_to_unwrap == unwrappable.safe_to_unwrap {
+ span_lint_and_then(
+ self.cx,
+ UNNECESSARY_UNWRAP,
+ expr.span,
+ &format!("You checked before that `{}()` cannot fail. \
+ Instead of checking and unwrapping, it's better to use `if let` or `match`.",
+ method_name.ident.name),
+ |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
+ );
+ } else {
+ span_lint_and_then(
+ self.cx,
+ PANICKING_UNWRAP,
+ expr.span,
+ &format!("This call to `{}()` will always panic.",
+ method_name.ident.name),
+ |diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
+ );
+ }
+ }
+ }
+ walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+}
+
+declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unwrap {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'a, 'tcx>,
+ kind: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ span: Span,
+ fn_id: HirId,
+ ) {
+ if span.from_expansion() {
+ return;
+ }
+
+ let mut v = UnwrappableVariablesVisitor {
+ cx,
+ unwrappables: Vec::new(),
+ };
+
+ walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
+ }
+}
--- /dev/null
+//! Read configurations files.
+
+#![deny(clippy::missing_docs_in_private_items)]
+
+use lazy_static::lazy_static;
+use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
+use rustc_span::source_map;
+use source_map::Span;
+use std::path::{Path, PathBuf};
+use std::sync::Mutex;
+use std::{env, fmt, fs, io};
+
+/// Gets the configuration file from arguments.
+pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
+ for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
+ if arg.check_name(sym!(conf_file)) {
+ return match arg.kind {
+ MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
+ MetaItemKind::NameValue(ref value) => {
+ if let LitKind::Str(ref file, _) = value.kind {
+ Ok(Some(file.to_string().into()))
+ } else {
+ Err(("`conf_file` value must be a string", value.span))
+ }
+ },
+ };
+ }
+ }
+
+ Ok(None)
+}
+
+/// Error from reading a configuration file.
+#[derive(Debug)]
+pub enum Error {
+ /// An I/O error.
+ Io(io::Error),
+ /// Not valid toml or doesn't fit the expected config format
+ Toml(String),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Io(err) => err.fmt(f),
+ Self::Toml(err) => err.fmt(f),
+ }
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(e: io::Error) -> Self {
+ Self::Io(e)
+ }
+}
+
+lazy_static! {
+ static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
+}
+
+macro_rules! define_Conf {
+ ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
+ mod helpers {
+ use serde::Deserialize;
+ /// Type used to store lint configuration.
+ #[derive(Deserialize)]
+ #[serde(rename_all = "kebab-case", deny_unknown_fields)]
+ pub struct Conf {
+ $(
+ #[$doc]
+ #[serde(default = $config_str)]
+ #[serde(with = $config_str)]
+ pub $config: $Ty,
+ )+
+ #[allow(dead_code)]
+ #[serde(default)]
+ third_party: Option<::toml::Value>,
+ }
+
+ $(
+ mod $config {
+ use serde::Deserialize;
+ pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
+ use super::super::{ERRORS, Error};
+ Ok(
+ <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
+ ERRORS
+ .lock()
+ .expect("no threading here")
+ .push(Error::Toml(e.to_string()));
+ super::$config()
+ })
+ )
+ }
+ }
+
+ #[must_use]
+ fn $config() -> $Ty {
+ let x = $default;
+ x
+ }
+ )+
+ }
+ };
+}
+
+pub use self::helpers::Conf;
+define_Conf! {
+ /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
+ (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()),
+ /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
+ (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
+ /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
+ (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
+ /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
+ (doc_valid_idents, "doc_valid_idents": Vec<String>, [
+ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
+ "DirectX",
+ "ECMAScript",
+ "GPLv2", "GPLv3",
+ "GitHub", "GitLab",
+ "IPv4", "IPv6",
+ "JavaScript",
+ "NaN", "NaNs",
+ "OAuth",
+ "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap",
+ "TrueType",
+ "iOS", "macOS",
+ "TeX", "LaTeX", "BibTeX", "BibLaTeX",
+ "MinGW",
+ "CamelCase",
+ ].iter().map(ToString::to_string).collect()),
+ /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
+ (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
+ /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
+ (type_complexity_threshold, "type_complexity_threshold": u64, 250),
+ /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
+ (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
+ /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
+ (too_large_for_stack, "too_large_for_stack": u64, 200),
+ /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
+ (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
+ /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
+ (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200),
+ /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
+ (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
+ /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
+ (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
+ /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
+ (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
+ /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
+ (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
+ /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
+ (array_size_threshold, "array_size_threshold": u64, 512_000),
+ /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
+ (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
+ /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
+ (max_struct_bools, "max_struct_bools": u64, 3),
+ /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
+ (max_fn_params_bools, "max_fn_params_bools": u64, 3),
++ /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
++ (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
+}
+
+impl Default for Conf {
+ #[must_use]
+ fn default() -> Self {
+ toml::from_str("").expect("we never error on empty config files")
+ }
+}
+
+/// Search for the configuration file.
+pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
+ /// Possible filename to search for.
+ const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
+
+ // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
+ // If neither of those exist, use ".".
+ let mut current = env::var_os("CLIPPY_CONF_DIR")
+ .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
+ .map_or_else(|| PathBuf::from("."), PathBuf::from);
+ loop {
+ for config_file_name in &CONFIG_FILE_NAMES {
+ let config_file = current.join(config_file_name);
+ match fs::metadata(&config_file) {
+ // Only return if it's a file to handle the unlikely situation of a directory named
+ // `clippy.toml`.
+ Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
+ // Return the error if it's something other than `NotFound`; otherwise we didn't
+ // find the project file yet, and continue searching.
+ Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
+ _ => {},
+ }
+ }
+
+ // If the current directory has no parent, we're done searching.
+ if !current.pop() {
+ return Ok(None);
+ }
+ }
+}
+
+/// Produces a `Conf` filled with the default values and forwards the errors
+///
+/// Used internally for convenience
+fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
+ (Conf::default(), errors)
+}
+
+/// Read the `toml` configuration file.
+///
+/// In case of error, the function tries to continue as much as possible.
+pub fn read(path: &Path) -> (Conf, Vec<Error>) {
+ let content = match fs::read_to_string(path) {
+ Ok(content) => content,
+ Err(err) => return default(vec![err.into()]),
+ };
+
+ assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
+ match toml::from_str(&content) {
+ Ok(toml) => {
+ let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
+
+ let toml_ref: &Conf = &toml;
+
+ let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;
+
+ if cyc_field.is_some() {
+ let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
+ errors.push(Error::Toml(cyc_err));
+ }
+
+ (toml, errors)
+ },
+ Err(e) => {
+ let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
+ errors.push(Error::Toml(e.to_string()));
+
+ default(errors)
+ },
+ }
+}
--- /dev/null
- PatKind::Binding(..) | PatKind::Wild => false,
+#[macro_use]
+pub mod sym;
+
+pub mod attrs;
+pub mod author;
+pub mod camel_case;
+pub mod comparisons;
+pub mod conf;
+pub mod constants;
+mod diagnostics;
+pub mod higher;
+mod hir_utils;
+pub mod inspector;
+pub mod internal_lints;
+pub mod numeric_literal;
+pub mod paths;
+pub mod ptr;
+pub mod sugg;
+pub mod usage;
+pub use self::attrs::*;
+pub use self::diagnostics::*;
+pub use self::hir_utils::{SpanlessEq, SpanlessHash};
+
+use std::borrow::Cow;
+use std::mem;
+
+use if_chain::if_chain;
+use rustc_ast::ast::{self, Attribute, LitKind};
+use rustc_attr as attr;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::Node;
+use rustc_hir::{
+ def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
+ MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety,
+};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::{LateContext, Level, Lint, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::traits;
+use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable};
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::source_map::original_sp;
+use rustc_span::symbol::{self, kw, Symbol};
+use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
+use rustc_target::abi::Integer;
+use rustc_trait_selection::traits::predicate_for_trait_def;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::query::normalize::AtExt;
+use smallvec::SmallVec;
+
+use crate::consts::{constant, Constant};
+use crate::reexport::Name;
+
+/// Returns `true` if the two spans come from differing expansions (i.e., one is
+/// from a macro and one isn't).
+#[must_use]
+pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
+ rhs.ctxt() != lhs.ctxt()
+}
+
+/// Returns `true` if the given `NodeId` is inside a constant context
+///
+/// # Example
+///
+/// ```rust,ignore
+/// if in_constant(cx, expr.hir_id) {
+/// // Do something
+/// }
+/// ```
+pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool {
+ let parent_id = cx.tcx.hir().get_parent_item(id);
+ match cx.tcx.hir().get(parent_id) {
+ Node::Item(&Item {
+ kind: ItemKind::Const(..),
+ ..
+ })
+ | Node::TraitItem(&TraitItem {
+ kind: TraitItemKind::Const(..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Const(..),
+ ..
+ })
+ | Node::AnonConst(_)
+ | Node::Item(&Item {
+ kind: ItemKind::Static(..),
+ ..
+ }) => true,
+ Node::Item(&Item {
+ kind: ItemKind::Fn(ref sig, ..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(ref sig, _),
+ ..
+ }) => sig.header.constness == Constness::Const,
+ _ => false,
+ }
+}
+
+/// Returns `true` if this `span` was expanded by any macro.
+#[must_use]
+pub fn in_macro(span: Span) -> bool {
+ if span.from_expansion() {
+ if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind {
+ false
+ } else {
+ true
+ }
+ } else {
+ false
+ }
+}
+// If the snippet is empty, it's an attribute that was inserted during macro
+// expansion and we want to ignore those, because they could come from external
+// sources that the user has no control over.
+// For some reason these attributes don't have any expansion info on them, so
+// we have to check it this way until there is a better way.
+pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
+ if let Some(snippet) = snippet_opt(cx, span) {
+ if snippet.is_empty() {
+ return false;
+ }
+ }
+ true
+}
+
+/// Checks if given pattern is a wildcard (`_`)
+pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
+ match pat.kind {
+ PatKind::Wild => true,
+ _ => false,
+ }
+}
+
+/// Checks if type is struct, enum or union type with the given def path.
+pub fn match_type(cx: &LateContext<'_, '_>, ty: Ty<'_>, path: &[&str]) -> bool {
+ match ty.kind {
+ ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
+ _ => false,
+ }
+}
+
+/// Checks if the type is equal to a diagnostic item
+pub fn is_type_diagnostic_item(cx: &LateContext<'_, '_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+ match ty.kind {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
+ _ => false,
+ }
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr<'_>, path: &[&str]) -> bool {
+ let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap();
+ let trt_id = cx.tcx.trait_of_item(def_id);
+ if let Some(trt_id) = trt_id {
+ match_def_path(cx, trt_id, path)
+ } else {
+ false
+ }
+}
+
+/// Checks if an expression references a variable of the given name.
+pub fn match_var(expr: &Expr<'_>, var: Name) -> bool {
+ if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
+ if path.segments.len() == 1 && path.segments[0].ident.name == var {
+ return true;
+ }
+ }
+ false
+}
+
+pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
+ match *path {
+ QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
+ QPath::TypeRelative(_, ref seg) => seg,
+ }
+}
+
+pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+ match *path {
+ QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]),
+ QPath::Resolved(..) => None,
+ QPath::TypeRelative(_, ref seg) => Some(seg),
+ }
+}
+
+/// Matches a `QPath` against a slice of segment string literals.
+///
+/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
+/// `rustc_hir::QPath`.
+///
+/// # Examples
+/// ```rust,ignore
+/// match_qpath(path, &["std", "rt", "begin_unwind"])
+/// ```
+pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
+ match *path {
+ QPath::Resolved(_, ref path) => match_path(path, segments),
+ QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
+ TyKind::Path(ref inner_path) => {
+ !segments.is_empty()
+ && match_qpath(inner_path, &segments[..(segments.len() - 1)])
+ && segment.ident.name.as_str() == segments[segments.len() - 1]
+ },
+ _ => false,
+ },
+ }
+}
+
+/// Matches a `Path` against a slice of segment string literals.
+///
+/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
+/// `rustc_hir::Path`.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// if match_path(&trait_ref.path, &paths::HASH) {
+/// // This is the `std::hash::Hash` trait.
+/// }
+///
+/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
+/// // This is a `rustc_middle::lint::Lint`.
+/// }
+/// ```
+pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
+ path.segments
+ .iter()
+ .rev()
+ .zip(segments.iter().rev())
+ .all(|(a, b)| a.ident.name.as_str() == *b)
+}
+
+/// Matches a `Path` against a slice of segment string literals, e.g.
+///
+/// # Examples
+/// ```rust,ignore
+/// match_path_ast(path, &["std", "rt", "begin_unwind"])
+/// ```
+pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
+ path.segments
+ .iter()
+ .rev()
+ .zip(segments.iter().rev())
+ .all(|(a, b)| a.ident.name.as_str() == *b)
+}
+
+/// Gets the definition associated to a path.
+pub fn path_to_res(cx: &LateContext<'_, '_>, path: &[&str]) -> Option<def::Res> {
+ let crates = cx.tcx.crates();
+ let krate = crates
+ .iter()
+ .find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]);
+ if let Some(krate) = krate {
+ let krate = DefId {
+ krate: *krate,
+ index: CRATE_DEF_INDEX,
+ };
+ let mut items = cx.tcx.item_children(krate);
+ let mut path_it = path.iter().skip(1).peekable();
+
+ loop {
+ let segment = match path_it.next() {
+ Some(segment) => segment,
+ None => return None,
+ };
+
+ let result = SmallVec::<[_; 8]>::new();
+ for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
+ if item.ident.name.as_str() == *segment {
+ if path_it.peek().is_none() {
+ return Some(item.res);
+ }
+
+ items = cx.tcx.item_children(item.res.def_id());
+ break;
+ }
+ }
+ }
+ } else {
+ None
+ }
+}
+
+pub fn qpath_res(cx: &LateContext<'_, '_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
+ match qpath {
+ hir::QPath::Resolved(_, path) => path.res,
+ hir::QPath::TypeRelative(..) => {
+ if cx.tcx.has_typeck_tables(id.owner.to_def_id()) {
+ cx.tcx
+ .typeck_tables_of(id.owner.to_def_id().expect_local())
+ .qpath_res(qpath, id)
+ } else {
+ Res::Err
+ }
+ },
+ }
+}
+
+/// Convenience function to get the `DefId` of a trait by path.
+/// It could be a trait or trait alias.
+pub fn get_trait_def_id(cx: &LateContext<'_, '_>, path: &[&str]) -> Option<DefId> {
+ let res = match path_to_res(cx, path) {
+ Some(res) => res,
+ None => return None,
+ };
+
+ match res {
+ Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
+ Res::Err => unreachable!("this trait resolution is impossible: {:?}", &path),
+ _ => None,
+ }
+}
+
+/// Checks whether a type implements a trait.
+/// See also `get_trait_def_id`.
+pub fn implements_trait<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ ty: Ty<'tcx>,
+ trait_id: DefId,
+ ty_params: &[GenericArg<'tcx>],
+) -> bool {
+ let ty = cx.tcx.erase_regions(&ty);
+ let obligation = predicate_for_trait_def(
+ cx.tcx,
+ cx.param_env,
+ traits::ObligationCause::dummy(),
+ trait_id,
+ 0,
+ ty,
+ ty_params,
+ );
+ cx.tcx
+ .infer_ctxt()
+ .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+}
+
+/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
+///
+/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
+///
+/// ```rust
+/// struct Point(isize, isize);
+///
+/// impl std::ops::Add for Point {
+/// type Output = Self;
+///
+/// fn add(self, other: Self) -> Self {
+/// Point(0, 0)
+/// }
+/// }
+/// ```
+pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
+ // Get the implemented trait for the current function
+ let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
+ if_chain! {
+ if parent_impl != hir::CRATE_HIR_ID;
+ if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
+ if let hir::ItemKind::Impl{ of_trait: trait_ref, .. } = &item.kind;
+ then { return trait_ref.as_ref(); }
+ }
+ None
+}
+
+/// Checks whether this type implements `Drop`.
+pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.ty_adt_def() {
+ Some(def) => def.has_dtor(cx.tcx),
+ _ => false,
+ }
+}
+
+/// Returns the method names and argument list of nested method call expressions that make up
+/// `expr`. method/span lists are sorted with the most recent call first.
+pub fn method_calls<'tcx>(
+ expr: &'tcx Expr<'tcx>,
+ max_depth: usize,
+) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
+ let mut method_names = Vec::with_capacity(max_depth);
+ let mut arg_lists = Vec::with_capacity(max_depth);
+ let mut spans = Vec::with_capacity(max_depth);
+
+ let mut current = expr;
+ for _ in 0..max_depth {
+ if let ExprKind::MethodCall(path, span, args) = ¤t.kind {
+ if args.iter().any(|e| e.span.from_expansion()) {
+ break;
+ }
+ method_names.push(path.ident.name);
+ arg_lists.push(&**args);
+ spans.push(*span);
+ current = &args[0];
+ } else {
+ break;
+ }
+ }
+
+ (method_names, arg_lists, spans)
+}
+
+/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
+///
+/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
+/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec`
+/// containing the `Expr`s for
+/// `.bar()` and `.baz()`
+pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
+ let mut current = expr;
+ let mut matched = Vec::with_capacity(methods.len());
+ for method_name in methods.iter().rev() {
+ // method chains are stored last -> first
+ if let ExprKind::MethodCall(ref path, _, ref args) = current.kind {
+ if path.ident.name.as_str() == *method_name {
+ if args.iter().any(|e| e.span.from_expansion()) {
+ return None;
+ }
+ matched.push(&**args); // build up `matched` backwards
+ current = &args[0] // go to parent expression
+ } else {
+ return None;
+ }
+ } else {
+ return None;
+ }
+ }
+ // Reverse `matched` so that it is in the same order as `methods`.
+ matched.reverse();
+ Some(matched)
+}
+
+/// Returns `true` if the provided `def_id` is an entrypoint to a program.
+pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
+ cx.tcx
+ .entry_fn(LOCAL_CRATE)
+ .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id())
+}
+
+/// Gets the name of the item the expression is in, if available.
+pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<Name> {
+ let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
+ match cx.tcx.hir().find(parent_id) {
+ Some(
+ Node::Item(Item { ident, .. })
+ | Node::TraitItem(TraitItem { ident, .. })
+ | Node::ImplItem(ImplItem { ident, .. }),
+ ) => Some(ident.name),
+ _ => None,
+ }
+}
+
+/// Gets the name of a `Pat`, if any.
+pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
+ match pat.kind {
+ PatKind::Binding(.., ref spname, _) => Some(spname.name),
+ PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
+ PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p),
+ _ => None,
+ }
+}
+
+struct ContainsName {
+ name: Name,
+ result: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for ContainsName {
+ type Map = Map<'tcx>;
+
+ fn visit_name(&mut self, _: Span, name: Name) {
+ if self.name == name {
+ self.result = true;
+ }
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Checks if an `Expr` contains a certain name.
+pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool {
+ let mut cn = ContainsName { name, result: false };
+ cn.visit_expr(expr);
+ cn.result
+}
+
+/// Converts a span to a code snippet if available, otherwise use default.
+///
+/// This is useful if you want to provide suggestions for your lint or more generally, if you want
+/// to convert a given `Span` to a `str`.
+///
+/// # Example
+/// ```rust,ignore
+/// snippet(cx, expr.span, "..")
+/// ```
+pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
+ snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
+}
+
+/// Same as `snippet`, but it adapts the applicability level by following rules:
+///
+/// - Applicability level `Unspecified` will never be changed.
+/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
+/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
+/// `HasPlaceholders`
+pub fn snippet_with_applicability<'a, T: LintContext>(
+ cx: &T,
+ span: Span,
+ default: &'a str,
+ applicability: &mut Applicability,
+) -> Cow<'a, str> {
+ if *applicability != Applicability::Unspecified && span.from_expansion() {
+ *applicability = Applicability::MaybeIncorrect;
+ }
+ snippet_opt(cx, span).map_or_else(
+ || {
+ if *applicability == Applicability::MachineApplicable {
+ *applicability = Applicability::HasPlaceholders;
+ }
+ Cow::Borrowed(default)
+ },
+ From::from,
+ )
+}
+
+/// Same as `snippet`, but should only be used when it's clear that the input span is
+/// not a macro argument.
+pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
+ snippet(cx, span.source_callsite(), default)
+}
+
+/// Converts a span to a code snippet. Returns `None` if not available.
+pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+ cx.sess().source_map().span_to_snippet(span).ok()
+}
+
+/// Converts a span (from a block) to a code snippet if available, otherwise use default.
+///
+/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
+/// things which need to be printed as such.
+///
+/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
+/// resulting snippet of the given span.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", None)
+/// // where, `block` is the block of the if expr
+/// if x {
+/// y;
+/// }
+/// // will return the snippet
+/// {
+/// y;
+/// }
+/// ```
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", Some(if_expr.span))
+/// // where, `block` is the block of the if expr
+/// if x {
+/// y;
+/// }
+/// // will return the snippet
+/// {
+/// y;
+/// } // aligned with `if`
+/// ```
+/// Note that the first line of the snippet always has 0 indentation.
+pub fn snippet_block<'a, T: LintContext>(
+ cx: &T,
+ span: Span,
+ default: &'a str,
+ indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+ let snip = snippet(cx, span, default);
+ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+ trim_multiline(snip, true, indent)
+}
+
+/// Same as `snippet_block`, but adapts the applicability level by the rules of
+/// `snippet_with_applicabiliy`.
+pub fn snippet_block_with_applicability<'a, T: LintContext>(
+ cx: &T,
+ span: Span,
+ default: &'a str,
+ indent_relative_to: Option<Span>,
+ applicability: &mut Applicability,
+) -> Cow<'a, str> {
+ let snip = snippet_with_applicability(cx, span, default, applicability);
+ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+ trim_multiline(snip, true, indent)
+}
+
+/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
+/// line.
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^
+/// // will be converted to
+/// let x = ();
+/// // ^^^^^^^^^^
+/// ```
+pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
+ if let Some(first_char_pos) = first_char_in_first_line(cx, span) {
+ span.with_lo(first_char_pos)
+ } else {
+ span
+ }
+}
+
+fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
+ let line_span = line_span(cx, span);
+ if let Some(snip) = snippet_opt(cx, line_span) {
+ snip.find(|c: char| !c.is_whitespace())
+ .map(|pos| line_span.lo() + BytePos::from_usize(pos))
+ } else {
+ None
+ }
+}
+
+/// Returns the indentation of the line of a span
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^ -- will return 0
+/// let x = ();
+/// // ^^ -- will return 4
+/// ```
+pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
+ if let Some(snip) = snippet_opt(cx, line_span(cx, span)) {
+ snip.find(|c: char| !c.is_whitespace())
+ } else {
+ None
+ }
+}
+
+/// Extends the span to the beginning of the spans line, incl. whitespaces.
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^
+/// // will be converted to
+/// let x = ();
+/// // ^^^^^^^^^^^^^^
+/// ```
+fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
+ let span = original_sp(span, DUMMY_SP);
+ let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
+ let line_no = source_map_and_line.line;
+ let line_start = source_map_and_line.sf.lines[line_no];
+ Span::new(line_start, span.hi(), span.ctxt())
+}
+
+/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
+/// Also takes an `Option<String>` which can be put inside the braces.
+pub fn expr_block<'a, T: LintContext>(
+ cx: &T,
+ expr: &Expr<'_>,
+ option: Option<String>,
+ default: &'a str,
+ indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+ let code = snippet_block(cx, expr.span, default, indent_relative_to);
+ let string = option.unwrap_or_default();
+ if expr.span.from_expansion() {
+ Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
+ } else if let ExprKind::Block(_, _) = expr.kind {
+ Cow::Owned(format!("{}{}", code, string))
+ } else if string.is_empty() {
+ Cow::Owned(format!("{{ {} }}", code))
+ } else {
+ Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
+ }
+}
+
+/// Trim indentation from a multiline string with possibility of ignoring the
+/// first line.
+fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
+ let s_space = trim_multiline_inner(s, ignore_first, indent, ' ');
+ let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t');
+ trim_multiline_inner(s_tab, ignore_first, indent, ' ')
+}
+
+fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>, ch: char) -> Cow<'_, str> {
+ let mut x = s
+ .lines()
+ .skip(ignore_first as usize)
+ .filter_map(|l| {
+ if l.is_empty() {
+ None
+ } else {
+ // ignore empty lines
+ Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
+ }
+ })
+ .min()
+ .unwrap_or(0);
+ if let Some(indent) = indent {
+ x = x.saturating_sub(indent);
+ }
+ if x > 0 {
+ Cow::Owned(
+ s.lines()
+ .enumerate()
+ .map(|(i, l)| {
+ if (ignore_first && i == 0) || l.is_empty() {
+ l
+ } else {
+ l.split_at(x).1
+ }
+ })
+ .collect::<Vec<_>>()
+ .join("\n"),
+ )
+ } else {
+ s
+ }
+}
+
+/// Gets the parent expression, if any –- this is useful to constrain a lint.
+pub fn get_parent_expr<'c>(cx: &'c LateContext<'_, '_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> {
+ let map = &cx.tcx.hir();
+ let hir_id = e.hir_id;
+ let parent_id = map.get_parent_node(hir_id);
+ if hir_id == parent_id {
+ return None;
+ }
+ map.find(parent_id).and_then(|node| {
+ if let Node::Expr(parent) = node {
+ Some(parent)
+ } else {
+ None
+ }
+ })
+}
+
+pub fn get_enclosing_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
+ let map = &cx.tcx.hir();
+ let enclosing_node = map
+ .get_enclosing_scope(hir_id)
+ .and_then(|enclosing_id| map.find(enclosing_id));
+ if let Some(node) = enclosing_node {
+ match node {
+ Node::Block(block) => Some(block),
+ Node::Item(&Item {
+ kind: ItemKind::Fn(_, _, eid),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(_, eid),
+ ..
+ }) => match cx.tcx.hir().body(eid).value.kind {
+ ExprKind::Block(ref block, _) => Some(block),
+ _ => None,
+ },
+ _ => None,
+ }
+ } else {
+ None
+ }
+}
+
+/// Returns the base type for HIR references and pointers.
+pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
+ match ty.kind {
+ TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
+ _ => ty,
+ }
+}
+
+/// Returns the base type for references and raw pointers.
+pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> {
+ match ty.kind {
+ ty::Ref(_, ty, _) => walk_ptrs_ty(ty),
+ _ => ty,
+ }
+}
+
+/// Returns the base type for references and raw pointers, and count reference
+/// depth.
+pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
+ fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
+ match ty.kind {
+ ty::Ref(_, ty, _) => inner(ty, depth + 1),
+ _ => (ty, depth),
+ }
+ }
+ inner(ty, 0)
+}
+
+/// Checks whether the given expression is a constant integer of the given value.
+/// unlike `is_integer_literal`, this version does const folding
+pub fn is_integer_const(cx: &LateContext<'_, '_>, e: &Expr<'_>, value: u128) -> bool {
+ if is_integer_literal(e, value) {
+ return true;
+ }
+ let map = cx.tcx.hir();
+ let parent_item = map.get_parent_item(e.hir_id);
+ if let Some((Constant::Int(v), _)) = map
+ .maybe_body_owned_by(parent_item)
+ .and_then(|body_id| constant(cx, cx.tcx.body_tables(body_id), e))
+ {
+ value == v
+ } else {
+ false
+ }
+}
+
+/// Checks whether the given expression is a constant literal of the given value.
+pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
+ // FIXME: use constant folding
+ if let ExprKind::Lit(ref spanned) = expr.kind {
+ if let LitKind::Int(v, _) = spanned.node {
+ return v == value;
+ }
+ }
+ false
+}
+
+/// Returns `true` if the given `Expr` has been coerced before.
+///
+/// Examples of coercions can be found in the Nomicon at
+/// <https://doc.rust-lang.org/nomicon/coercions.html>.
+///
+/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
+/// information on adjustments and coercions.
+pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool {
+ cx.tables.adjustments().get(e.hir_id).is_some()
+}
+
+/// Returns the pre-expansion span if is this comes from an expansion of the
+/// macro `name`.
+/// See also `is_direct_expn_of`.
+#[must_use]
+pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
+ loop {
+ if span.from_expansion() {
+ let data = span.ctxt().outer_expn_data();
+ let new_span = data.call_site;
+
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+ if mac_name.as_str() == name {
+ return Some(new_span);
+ }
+ }
+
+ span = new_span;
+ } else {
+ return None;
+ }
+ }
+}
+
+/// Returns the pre-expansion span if the span directly comes from an expansion
+/// of the macro `name`.
+/// The difference with `is_expn_of` is that in
+/// ```rust,ignore
+/// foo!(bar!(42));
+/// ```
+/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
+/// `bar!` by
+/// `is_direct_expn_of`.
+#[must_use]
+pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
+ if span.from_expansion() {
+ let data = span.ctxt().outer_expn_data();
+ let new_span = data.call_site;
+
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+ if mac_name.as_str() == name {
+ return Some(new_span);
+ }
+ }
+ }
+
+ None
+}
+
+/// Convenience function to get the return type of a function.
+pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
+ let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
+ let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
+ cx.tcx.erase_late_bound_regions(&ret_ty)
+}
+
+/// Checks if two types are the same.
+///
+/// This discards any lifetime annotations, too.
+//
+// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` ==
+// `for <'b> Foo<'b>`, but not for type parameters).
+pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+ let a = cx.tcx.erase_late_bound_regions(&Binder::bind(a));
+ let b = cx.tcx.erase_late_bound_regions(&Binder::bind(b));
+ cx.tcx
+ .infer_ctxt()
+ .enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok())
+}
+
+/// Returns `true` if the given type is an `unsafe` function.
+pub fn type_is_unsafe_function<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind {
+ ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
+ _ => false,
+ }
+}
+
+pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
+ ty.is_copy_modulo_regions(cx.tcx, cx.param_env, DUMMY_SP)
+}
+
+/// Checks if an expression is constructing a tuple-like enum variant or struct
+pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
+ if let ExprKind::Call(ref fun, _) = expr.kind {
+ if let ExprKind::Path(ref qp) = fun.kind {
+ let res = cx.tables.qpath_res(qp, fun.hir_id);
+ return match res {
+ def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
+ def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
+ _ => false,
+ };
+ }
+ }
+ false
+}
+
+/// Returns `true` if a pattern is refutable.
++// TODO: should be implemented using rustc/mir_build/hair machinery
+pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
+ fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool {
+ matches!(
+ cx.tables.qpath_res(qpath, id),
+ def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
+ )
+ }
+
+ fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_, '_>, mut i: I) -> bool {
+ i.any(|pat| is_refutable(cx, pat))
+ }
+
+ match pat.kind {
- PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
++ PatKind::Wild => false,
++ PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
+ PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
+ PatKind::Lit(..) | PatKind::Range(..) => true,
+ PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
- if is_enum_variant(cx, qpath, pat.hir_id) {
- true
- } else {
- are_refutable(cx, fields.iter().map(|field| &*field.pat))
- }
++ PatKind::Or(ref pats) => {
++ // TODO: should be the honest check, that pats is exhaustive set
++ are_refutable(cx, pats.iter().map(|pat| &**pat))
++ },
++ PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
+ PatKind::Struct(ref qpath, ref fields, _) => {
- if is_enum_variant(cx, qpath, pat.hir_id) {
- true
- } else {
- are_refutable(cx, pats.iter().map(|pat| &**pat))
- }
++ is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
+ },
+ PatKind::TupleStruct(ref qpath, ref pats, _) => {
- are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
++ is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
+ },
+ PatKind::Slice(ref head, ref middle, ref tail) => {
++ match &cx.tables.node_type(pat.hir_id).kind {
++ ty::Slice(..) => {
++ // [..] is the only irrefutable slice pattern.
++ !head.is_empty() || middle.is_none() || !tail.is_empty()
++ },
++ ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)),
++ _ => {
++ // unreachable!()
++ true
++ },
++ }
+ },
+ }
+}
+
+/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
+/// implementations have.
+pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
+ attr::contains_name(attrs, sym!(automatically_derived))
+}
+
+/// Remove blocks around an expression.
+///
+/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
+/// themselves.
+pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
+ while let ExprKind::Block(ref block, ..) = expr.kind {
+ match (block.stmts.is_empty(), block.expr.as_ref()) {
+ (true, Some(e)) => expr = e,
+ _ => break,
+ }
+ }
+ expr
+}
+
+pub fn is_self(slf: &Param<'_>) -> bool {
+ if let PatKind::Binding(.., name, _) = slf.pat.kind {
+ name.name == kw::SelfLower
+ } else {
+ false
+ }
+}
+
+pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
+ if_chain! {
+ if let TyKind::Path(ref qp) = slf.kind;
+ if let QPath::Resolved(None, ref path) = *qp;
+ if let Res::SelfTy(..) = path.res;
+ then {
+ return true
+ }
+ }
+ false
+}
+
+pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
+ (0..decl.inputs.len()).map(move |i| &body.params[i])
+}
+
+/// Checks if a given expression is a match expression expanded from the `?`
+/// operator or the `try` macro.
+pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ fn is_ok(arm: &Arm<'_>) -> bool {
+ if_chain! {
+ if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
+ if match_qpath(path, &paths::RESULT_OK[1..]);
+ if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
+ if let ExprKind::Path(QPath::Resolved(None, ref path)) = arm.body.kind;
+ if let Res::Local(lid) = path.res;
+ if lid == hir_id;
+ then {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn is_err(arm: &Arm<'_>) -> bool {
+ if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
+ match_qpath(path, &paths::RESULT_ERR[1..])
+ } else {
+ false
+ }
+ }
+
+ if let ExprKind::Match(_, ref arms, ref source) = expr.kind {
+ // desugared from a `?` operator
+ if let MatchSource::TryDesugar = *source {
+ return Some(expr);
+ }
+
+ if_chain! {
+ if arms.len() == 2;
+ if arms[0].guard.is_none();
+ if arms[1].guard.is_none();
+ if (is_ok(&arms[0]) && is_err(&arms[1])) ||
+ (is_ok(&arms[1]) && is_err(&arms[0]));
+ then {
+ return Some(expr);
+ }
+ }
+ }
+
+ None
+}
+
+/// Returns `true` if the lint is allowed in the current context
+///
+/// Useful for skipping long running code when it's unnecessary
+pub fn is_allowed(cx: &LateContext<'_, '_>, lint: &'static Lint, id: HirId) -> bool {
+ cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
+}
+
+pub fn get_arg_name(pat: &Pat<'_>) -> Option<Name> {
+ match pat.kind {
+ PatKind::Binding(.., ident, None) => Some(ident.name),
+ PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
+ _ => None,
+ }
+}
+
+pub fn int_bits(tcx: TyCtxt<'_>, ity: ast::IntTy) -> u64 {
+ Integer::from_attr(&tcx, attr::IntType::SignedInt(ity)).size().bits()
+}
+
+#[allow(clippy::cast_possible_wrap)]
+/// Turn a constant int byte representation into an i128
+pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ast::IntTy) -> i128 {
+ let amt = 128 - int_bits(tcx, ity);
+ ((u as i128) << amt) >> amt
+}
+
+#[allow(clippy::cast_sign_loss)]
+/// clip unused bytes
+pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ast::IntTy) -> u128 {
+ let amt = 128 - int_bits(tcx, ity);
+ ((u as u128) << amt) >> amt
+}
+
+/// clip unused bytes
+pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ast::UintTy) -> u128 {
+ let bits = Integer::from_attr(&tcx, attr::IntType::UnsignedInt(ity)).size().bits();
+ let amt = 128 - bits;
+ (u << amt) >> amt
+}
+
+/// Removes block comments from the given `Vec` of lines.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// without_block_comments(vec!["/*", "foo", "*/"]);
+/// // => vec![]
+///
+/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
+/// // => vec!["bar"]
+/// ```
+pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
+ let mut without = vec![];
+
+ let mut nest_level = 0;
+
+ for line in lines {
+ if line.contains("/*") {
+ nest_level += 1;
+ continue;
+ } else if line.contains("*/") {
+ nest_level -= 1;
+ continue;
+ }
+
+ if nest_level == 0 {
+ without.push(line);
+ }
+ }
+
+ without
+}
+
+pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+ let map = &tcx.hir();
+ let mut prev_enclosing_node = None;
+ let mut enclosing_node = node;
+ while Some(enclosing_node) != prev_enclosing_node {
+ if is_automatically_derived(map.attrs(enclosing_node)) {
+ return true;
+ }
+ prev_enclosing_node = Some(enclosing_node);
+ enclosing_node = map.get_parent_item(enclosing_node);
+ }
+ false
+}
+
+/// Returns true if ty has `iter` or `iter_mut` methods
+pub fn has_iter_method(cx: &LateContext<'_, '_>, probably_ref_ty: Ty<'_>) -> Option<&'static str> {
+ // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
+ // exists and has the desired signature. Unfortunately FnCtxt is not exported
+ // so we can't use its `lookup_method` method.
+ let into_iter_collections: [&[&str]; 13] = [
+ &paths::VEC,
+ &paths::OPTION,
+ &paths::RESULT,
+ &paths::BTREESET,
+ &paths::BTREEMAP,
+ &paths::VEC_DEQUE,
+ &paths::LINKED_LIST,
+ &paths::BINARY_HEAP,
+ &paths::HASHSET,
+ &paths::HASHMAP,
+ &paths::PATH_BUF,
+ &paths::PATH,
+ &paths::RECEIVER,
+ ];
+
+ let ty_to_check = match probably_ref_ty.kind {
+ ty::Ref(_, ty_to_check, _) => ty_to_check,
+ _ => probably_ref_ty,
+ };
+
+ let def_id = match ty_to_check.kind {
+ ty::Array(..) => return Some("array"),
+ ty::Slice(..) => return Some("slice"),
+ ty::Adt(adt, _) => adt.did,
+ _ => return None,
+ };
+
+ for path in &into_iter_collections {
+ if match_def_path(cx, def_id, path) {
+ return Some(*path.last().unwrap());
+ }
+ }
+ None
+}
+
+/// Matches a function call with the given path and returns the arguments.
+///
+/// Usage:
+///
+/// ```rust,ignore
+/// if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
+/// ```
+pub fn match_function_call<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ expr: &'tcx Expr<'_>,
+ path: &[&str],
+) -> Option<&'tcx [Expr<'tcx>]> {
+ if_chain! {
+ if let ExprKind::Call(ref fun, ref args) = expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(fun_def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id();
+ if match_def_path(cx, fun_def_id, path);
+ then {
+ return Some(&args)
+ }
+ };
+ None
+}
+
+/// Checks if `Ty` is normalizable. This function is useful
+/// to avoid crashes on `layout_of`.
+pub fn is_normalizable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ let cause = rustc_middle::traits::ObligationCause::dummy();
+ infcx.at(&cause, param_env).normalize(&ty).is_ok()
+ })
+}
+
+pub fn match_def_path<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, did: DefId, syms: &[&str]) -> bool {
+ // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
+ // accepts only that. We should probably move to Symbols in Clippy as well.
+ let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
+ cx.match_def_path(did, &syms)
+}
+
+/// Returns the list of condition expressions and the list of blocks in a
+/// sequence of `if/else`.
+/// E.g., this returns `([a, b], [c, d, e])` for the expression
+/// `if a { c } else if b { d } else { e }`.
+pub fn if_sequence<'tcx>(
+ mut expr: &'tcx Expr<'tcx>,
+) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) {
+ let mut conds = SmallVec::new();
+ let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new();
+
+ while let Some((ref cond, ref then_expr, ref else_expr)) = higher::if_block(&expr) {
+ conds.push(&**cond);
+ if let ExprKind::Block(ref block, _) = then_expr.kind {
+ blocks.push(block);
+ } else {
+ panic!("ExprKind::If node is not an ExprKind::Block");
+ }
+
+ if let Some(ref else_expr) = *else_expr {
+ expr = else_expr;
+ } else {
+ break;
+ }
+ }
+
+ // final `else {..}`
+ if !blocks.is_empty() {
+ if let ExprKind::Block(ref block, _) = expr.kind {
+ blocks.push(&**block);
+ }
+ }
+
+ (conds, blocks)
+}
+
+pub fn parent_node_is_if_expr<'a, 'b>(expr: &Expr<'_>, cx: &LateContext<'a, 'b>) -> bool {
+ let map = cx.tcx.hir();
+ let parent_id = map.get_parent_node(expr.hir_id);
+ let parent_node = map.get(parent_id);
+
+ match parent_node {
+ Node::Expr(e) => higher::if_block(&e).is_some(),
+ Node::Arm(e) => higher::if_block(&e.body).is_some(),
+ _ => false,
+ }
+}
+
+// Finds the attribute with the given name, if any
+pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> {
+ attrs
+ .iter()
+ .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name))
+}
+
+// Finds the `#[must_use]` attribute, if any
+pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
+ attr_by_name(attrs, "must_use")
+}
+
+// Returns whether the type has #[must_use] attribute
+pub fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind {
+ ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
+ ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
+ ty::Slice(ref ty)
+ | ty::Array(ref ty, _)
+ | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
+ | ty::Ref(_, ref ty, _) => {
+ // for the Array case we don't need to care for the len == 0 case
+ // because we don't want to lint functions returning empty arrays
+ is_must_use_ty(cx, *ty)
+ },
+ ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
+ ty::Opaque(ref def_id, _) => {
+ for (predicate, _) in cx.tcx.predicates_of(*def_id).predicates {
+ if let ty::Predicate::Trait(ref poly_trait_predicate, _) = predicate {
+ if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ ty::Dynamic(binder, _) => {
+ for predicate in binder.skip_binder().iter() {
+ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
+ if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ _ => false,
+ }
+}
+
+// check if expr is calling method or function with #[must_use] attribyte
+pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
+ let did = match expr.kind {
+ ExprKind::Call(ref path, _) => if_chain! {
+ if let ExprKind::Path(ref qpath) = path.kind;
+ if let def::Res::Def(_, did) = cx.tables.qpath_res(qpath, path.hir_id);
+ then {
+ Some(did)
+ } else {
+ None
+ }
+ },
+ ExprKind::MethodCall(_, _, _) => cx.tables.type_dependent_def_id(expr.hir_id),
+ _ => None,
+ };
+
+ if let Some(did) = did {
+ must_use_attr(&cx.tcx.get_attrs(did)).is_some()
+ } else {
+ false
+ }
+}
+
+pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
+ krate.item.attrs.iter().any(|attr| {
+ if let ast::AttrKind::Normal(ref attr) = attr.kind {
+ attr.path == symbol::sym::no_std
+ } else {
+ false
+ }
+ })
+}
+
+/// Check if parent of a hir node is a trait implementation block.
+/// For example, `f` in
+/// ```rust,ignore
+/// impl Trait for S {
+/// fn f() {}
+/// }
+/// ```
+pub fn is_trait_impl_item(cx: &LateContext<'_, '_>, hir_id: HirId) -> bool {
+ if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
+ matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. })
+ } else {
+ false
+ }
+}
+
+/// Check if it's even possible to satisfy the `where` clause for the item.
+///
+/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
+///
+/// ```ignore
+/// fn foo() where i32: Iterator {
+/// for _ in 2i32 {}
+/// }
+/// ```
+pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_, '_>, did: DefId) -> bool {
+ use rustc_trait_selection::traits;
+ let predicates =
+ cx.tcx
+ .predicates_of(did)
+ .predicates
+ .iter()
+ .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
+ !traits::normalize_and_test_predicates(
+ cx.tcx,
+ traits::elaborate_predicates(cx.tcx, predicates)
+ .map(|o| o.predicate)
+ .collect::<Vec<_>>(),
+ )
+}
+
+pub fn run_lints(cx: &LateContext<'_, '_>, lints: &[&'static Lint], id: HirId) -> bool {
+ lints.iter().any(|lint| {
+ matches!(
+ cx.tcx.lint_level_at_node(lint, id),
+ (Level::Forbid | Level::Deny | Level::Warn, _)
+ )
+ })
+}
+
+#[cfg(test)]
+mod test {
+ use super::{trim_multiline, without_block_comments};
+
+ #[test]
+ fn test_trim_multiline_single_line() {
+ assert_eq!("", trim_multiline("".into(), false, None));
+ assert_eq!("...", trim_multiline("...".into(), false, None));
+ assert_eq!("...", trim_multiline(" ...".into(), false, None));
+ assert_eq!("...", trim_multiline("\t...".into(), false, None));
+ assert_eq!("...", trim_multiline("\t\t...".into(), false, None));
+ }
+
+ #[test]
+ #[rustfmt::skip]
+ fn test_trim_multiline_block() {
+ assert_eq!("\
+ if x {
+ y
+ } else {
+ z
+ }", trim_multiline(" if x {
+ y
+ } else {
+ z
+ }".into(), false, None));
+ assert_eq!("\
+ if x {
+ \ty
+ } else {
+ \tz
+ }", trim_multiline(" if x {
+ \ty
+ } else {
+ \tz
+ }".into(), false, None));
+ }
+
+ #[test]
+ #[rustfmt::skip]
+ fn test_trim_multiline_empty_line() {
+ assert_eq!("\
+ if x {
+ y
+
+ } else {
+ z
+ }", trim_multiline(" if x {
+ y
+
+ } else {
+ z
+ }".into(), false, None));
+ }
+
+ #[test]
+ fn test_without_block_comments_lines_without_block_comments() {
+ let result = without_block_comments(vec!["/*", "", "*/"]);
+ println!("result: {:?}", result);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
+ assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
+
+ let result = without_block_comments(vec!["/* rust", "", "*/"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["/* one-line comment */"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["foo", "bar", "baz"]);
+ assert_eq!(result, vec!["foo", "bar", "baz"]);
+ }
+}
--- /dev/null
- pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
+//! This module contains paths to types and functions Clippy needs to know
+//! about.
+//!
+//! Whenever possible, please consider diagnostic items over hardcoded paths.
+//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
+
+pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
+pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
+pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
+pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
+pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
+pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
+pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
+pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"];
+pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
+pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
+pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
+pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
+pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
+pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
+pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
+pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
+pub const CSTRING: [&str; 4] = ["std", "ffi", "c_str", "CString"];
+pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
+pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
+pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
+pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
+pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
+pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
+pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
+pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
+pub const DROP: [&str; 3] = ["core", "mem", "drop"];
+pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"];
+pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
+pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
+pub const EXIT: [&str; 3] = ["std", "process", "exit"];
+pub const FILE: [&str; 3] = ["std", "fs", "File"];
+pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
+pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
+pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
+pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
+pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
+pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
++pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
+pub const HASH: [&str; 2] = ["hash", "Hash"];
+pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
+pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
+pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
+pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
+pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
+pub const INTO: [&str; 3] = ["core", "convert", "Into"];
+pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
+pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
+pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
+pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
+pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
+pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
+pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"];
+pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
+pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
+pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
+pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
+pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
+pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
+pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
+pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
+pub const OPTION: [&str; 3] = ["core", "option", "Option"];
+pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
+pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
+pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
+pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
+pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
+pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
+pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
+pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
+pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
+pub const PATH: [&str; 3] = ["std", "path", "Path"];
+pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
+pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
+pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
+pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
+pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
+pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
+pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
+pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
+pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
+pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
++pub const RANGE_FULL: [&str; 4] = ["core", "ops", "range", "RangeFull"];
+pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
+pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
+pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
+pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
+pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
+pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
+pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
+pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
+pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
+pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
+pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
+pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
+pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
+pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
+pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
+pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
+pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
+pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
+pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
+pub const RESULT: [&str; 3] = ["core", "result", "Result"];
+pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
+pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
+pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
+pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
+pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
+pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
+pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
+pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
+pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
+pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
+pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
+pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
+pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
+pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
+pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
+pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
+pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
+pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
+pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
+pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
+pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
+pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
+pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
+pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
+pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
+pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
+pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
+pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
+pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
+pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
--- /dev/null
- Item, ItemKind, UseKind,
+use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{
+ def::{DefKind, Res},
- use rustc_session::{declare_lint_pass, declare_tool_lint};
++ Item, ItemKind, PathSegment, UseKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
- /// Note that this will not warn about wildcard imports from modules named `prelude`; many
- /// crates (including the standard library) provide modules named "prelude" specifically
- /// designed for wildcard import.
++use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::BytePos;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `use Enum::*`.
+ ///
+ /// **Why is this bad?** It is usually better style to use the prefixed name of
+ /// an enumeration variant, rather than importing variants.
+ ///
+ /// **Known problems:** Old-style enumerations that prefix the variants are
+ /// still around.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// use std::cmp::Ordering::*;
+ /// ```
+ pub ENUM_GLOB_USE,
+ pedantic,
+ "use items that import all variants of an enum"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for wildcard imports `use _::*`.
+ ///
+ /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if
+ /// you try to import something through a wildcard, that already has been imported by name from
+ /// a different source:
+ ///
+ /// ```rust,ignore
+ /// use crate1::foo; // Imports a function named foo
+ /// use crate2::*; // Has a function named foo
+ ///
+ /// foo(); // Calls crate1::foo
+ /// ```
+ ///
+ /// This can lead to confusing error messages at best and to unexpected behavior at worst.
+ ///
- declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
++ /// **Exceptions:**
++ ///
++ /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
++ /// provide modules named "prelude" specifically designed for wildcard import.
++ ///
++ /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
++ ///
++ /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.
+ ///
+ /// **Known problems:** If macros are imported through the wildcard, this macro is not included
+ /// by the suggestion and has to be added by hand.
+ ///
+ /// Applying the suggestion when explicit imports of the things imported with a glob import
+ /// exist, may result in `unused_imports` warnings.
+ ///
+ /// **Example:**
+ ///
+ /// Bad:
+ /// ```rust,ignore
+ /// use crate1::*;
+ ///
+ /// foo();
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// use crate1::foo;
+ ///
+ /// foo();
+ /// ```
+ pub WILDCARD_IMPORTS,
+ pedantic,
+ "lint `use _::*` statements"
+}
+
- if !in_macro(item.span);
++#[derive(Default)]
++pub struct WildcardImports {
++ warn_on_all: bool,
++ test_modules_deep: u32,
++}
++
++impl WildcardImports {
++ pub fn new(warn_on_all: bool) -> Self {
++ Self {
++ warn_on_all,
++ test_modules_deep: 0,
++ }
++ }
++}
++
++impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
+
+impl LateLintPass<'_, '_> for WildcardImports {
+ fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
++ if is_test_module_or_function(item) {
++ self.test_modules_deep = self.test_modules_deep.saturating_add(1);
++ }
+ if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
+ return;
+ }
+ if_chain! {
- // don't lint prelude glob imports
- if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
+ if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
- span,
- false,
++ if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
+ let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner);
+ if !used_imports.is_empty(); // Already handled by `unused_imports`
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
+ let (span, braced_glob) = if import_source_snippet.is_empty() {
+ // This is a `_::{_, *}` import
+ // In this case `use_path.span` is empty and ends directly in front of the `*`,
+ // so we need to extend it by one byte.
+ (
+ use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
+ true,
+ )
+ } else {
+ // In this case, the `use_path.span` ends right before the `::*`, so we need to
+ // extend it up to the `*`. Since it is hard to find the `*` in weird
+ // formattings like `use _ :: *;`, we extend it up to, but not including the
+ // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
+ // can just use the end of the item span
+ let mut span = use_path.span.with_hi(item.span.hi());
+ if snippet(cx, span, "").ends_with(';') {
+ span = use_path.span.with_hi(item.span.hi() - BytePos(1));
+ }
+ (
++ span, false,
+ )
+ };
+
+ let imports_string = if used_imports.len() == 1 {
+ used_imports.iter().next().unwrap().to_string()
+ } else {
+ let mut imports = used_imports
+ .iter()
+ .map(ToString::to_string)
+ .collect::<Vec<_>>();
+ imports.sort();
+ if braced_glob {
+ imports.join(", ")
+ } else {
+ format!("{{{}}}", imports.join(", "))
+ }
+ };
+
+ let sugg = if braced_glob {
+ imports_string
+ } else {
+ format!("{}::{}", import_source_snippet, imports_string)
+ };
+
+ let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
+ (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
+ } else {
+ (WILDCARD_IMPORTS, "usage of wildcard import")
+ };
+
+ span_lint_and_sugg(
+ cx,
+ lint,
+ span,
+ message,
+ "try",
+ sugg,
+ applicability,
+ );
+ }
+ }
+ }
++
++ fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) {
++ if is_test_module_or_function(item) {
++ self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
++ }
++ }
++}
++
++impl WildcardImports {
++ fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
++ in_macro(item.span)
++ || is_prelude_import(segments)
++ || (is_super_only_import(segments) && self.test_modules_deep > 0)
++ }
++}
++
++// Allow "...prelude::*" imports.
++// Many crates have a prelude, and it is imported as a glob by design.
++fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
++ segments
++ .iter()
++ .last()
++ .map_or(false, |ps| ps.ident.as_str() == "prelude")
++}
++
++// Allow "super::*" imports in tests.
++fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
++ segments.len() == 1 && segments[0].ident.as_str() == "super"
++}
++
++fn is_test_module_or_function(item: &Item<'_>) -> bool {
++ matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
+}
--- /dev/null
- group: "correctness",
+//! This file is managed by `cargo dev update_lints`. Do not edit.
+
+use lazy_static::lazy_static;
+
+pub mod lint;
+pub use lint::Level;
+pub use lint::Lint;
+pub use lint::LINT_LEVELS;
+
+lazy_static! {
+// begin lint list, do not remove this comment, it’s used in `update_lints`
+pub static ref ALL_LINTS: Vec<Lint> = vec![
+ Lint {
+ name: "absurd_extreme_comparisons",
+ group: "correctness",
+ desc: "a comparison with a maximum or minimum value that is always true or false",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "almost_swapped",
+ group: "correctness",
+ desc: "`foo = bar; bar = foo` sequence",
+ deprecation: None,
+ module: "swap",
+ },
+ Lint {
+ name: "approx_constant",
+ group: "correctness",
+ desc: "the approximate of a known float constant (in `std::fXX::consts`)",
+ deprecation: None,
+ module: "approx_const",
+ },
+ Lint {
+ name: "as_conversions",
+ group: "restriction",
+ desc: "using a potentially dangerous silent `as` conversion",
+ deprecation: None,
+ module: "as_conversions",
+ },
+ Lint {
+ name: "assertions_on_constants",
+ group: "style",
+ desc: "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`",
+ deprecation: None,
+ module: "assertions_on_constants",
+ },
+ Lint {
+ name: "assign_op_pattern",
+ group: "style",
+ desc: "assigning the result of an operation on a variable to that same variable",
+ deprecation: None,
+ module: "assign_ops",
+ },
+ Lint {
+ name: "await_holding_lock",
+ group: "pedantic",
+ desc: "Inside an async function, holding a MutexGuard while calling await",
+ deprecation: None,
+ module: "await_holding_lock",
+ },
+ Lint {
+ name: "bad_bit_mask",
+ group: "correctness",
+ desc: "expressions of the form `_ & mask == select` that will only ever return `true` or `false`",
+ deprecation: None,
+ module: "bit_mask",
+ },
+ Lint {
+ name: "blacklisted_name",
+ group: "style",
+ desc: "usage of a blacklisted/placeholder name",
+ deprecation: None,
+ module: "blacklisted_name",
+ },
+ Lint {
+ name: "block_in_if_condition_expr",
+ group: "style",
+ desc: "braces that can be eliminated in conditions, e.g., `if { true } ...`",
+ deprecation: None,
+ module: "block_in_if_condition",
+ },
+ Lint {
+ name: "block_in_if_condition_stmt",
+ group: "style",
+ desc: "complex blocks in conditions, e.g., `if { let x = true; x } ...`",
+ deprecation: None,
+ module: "block_in_if_condition",
+ },
+ Lint {
+ name: "bool_comparison",
+ group: "complexity",
+ desc: "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`",
+ deprecation: None,
+ module: "needless_bool",
+ },
+ Lint {
+ name: "borrow_interior_mutable_const",
+ group: "correctness",
+ desc: "referencing `const` with interior mutability",
+ deprecation: None,
+ module: "non_copy_const",
+ },
+ Lint {
+ name: "borrowed_box",
+ group: "complexity",
+ desc: "a borrow of a boxed type",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "box_vec",
+ group: "perf",
+ desc: "usage of `Box<Vec<T>>`, vector elements are already on the heap",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "boxed_local",
+ group: "perf",
+ desc: "using `Box<T>` where unnecessary",
+ deprecation: None,
+ module: "escape",
+ },
+ Lint {
+ name: "builtin_type_shadow",
+ group: "style",
+ desc: "shadowing a builtin type",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "cargo_common_metadata",
+ group: "cargo",
+ desc: "common metadata is defined in `Cargo.toml`",
+ deprecation: None,
+ module: "cargo_common_metadata",
+ },
+ Lint {
+ name: "cast_lossless",
+ group: "pedantic",
+ desc: "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "cast_possible_truncation",
+ group: "pedantic",
+ desc: "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "cast_possible_wrap",
+ group: "pedantic",
+ desc: "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "cast_precision_loss",
+ group: "pedantic",
+ desc: "casts that cause loss of precision, e.g., `x as f32` where `x: u64`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "cast_ptr_alignment",
+ group: "correctness",
+ desc: "cast from a pointer to a more-strictly-aligned pointer",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "cast_ref_to_mut",
+ group: "correctness",
+ desc: "a cast of reference to a mutable pointer",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "cast_sign_loss",
+ group: "pedantic",
+ desc: "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "char_lit_as_u8",
+ group: "complexity",
+ desc: "casting a character literal to `u8` truncates",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "chars_last_cmp",
+ group: "style",
+ desc: "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "chars_next_cmp",
+ group: "style",
+ desc: "using `.chars().next()` to check if a string starts with a char",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "checked_conversions",
+ group: "pedantic",
+ desc: "`try_from` could replace manual bounds checking when casting",
+ deprecation: None,
+ module: "checked_conversions",
+ },
+ Lint {
+ name: "clone_double_ref",
+ group: "correctness",
+ desc: "using `clone` on `&&T`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "clone_on_copy",
+ group: "complexity",
+ desc: "using `clone` on a `Copy` type",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "clone_on_ref_ptr",
+ group: "restriction",
+ desc: "using \'clone\' on a ref-counted pointer",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "cmp_nan",
+ group: "correctness",
+ desc: "comparisons to `NAN`, which will always return false, probably not intended",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "cmp_null",
+ group: "style",
+ desc: "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead.",
+ deprecation: None,
+ module: "ptr",
+ },
+ Lint {
+ name: "cmp_owned",
+ group: "perf",
+ desc: "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "cognitive_complexity",
+ group: "nursery",
+ desc: "functions that should be split up into multiple functions",
+ deprecation: None,
+ module: "cognitive_complexity",
+ },
+ Lint {
+ name: "collapsible_if",
+ group: "style",
+ desc: "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)",
+ deprecation: None,
+ module: "collapsible_if",
+ },
+ Lint {
+ name: "comparison_chain",
+ group: "style",
+ desc: "`if`s that can be rewritten with `match` and `cmp`",
+ deprecation: None,
+ module: "comparison_chain",
+ },
+ Lint {
+ name: "copy_iterator",
+ group: "pedantic",
+ desc: "implementing `Iterator` on a `Copy` type",
+ deprecation: None,
+ module: "copy_iterator",
+ },
+ Lint {
+ name: "crosspointer_transmute",
+ group: "complexity",
+ desc: "transmutes that have to or from types that are a pointer to the other",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "dbg_macro",
+ group: "restriction",
+ desc: "`dbg!` macro is intended as a debugging tool",
+ deprecation: None,
+ module: "dbg_macro",
+ },
+ Lint {
+ name: "debug_assert_with_mut_call",
+ group: "nursery",
+ desc: "mutable arguments in `debug_assert{,_ne,_eq}!`",
+ deprecation: None,
+ module: "mutable_debug_assertion",
+ },
+ Lint {
+ name: "decimal_literal_representation",
+ group: "restriction",
+ desc: "using decimal representation when hexadecimal would be better",
+ deprecation: None,
+ module: "literal_representation",
+ },
+ Lint {
+ name: "declare_interior_mutable_const",
+ group: "correctness",
+ desc: "declaring `const` with interior mutability",
+ deprecation: None,
+ module: "non_copy_const",
+ },
+ Lint {
+ name: "default_trait_access",
+ group: "pedantic",
+ desc: "checks for literal calls to `Default::default()`",
+ deprecation: None,
+ module: "default_trait_access",
+ },
+ Lint {
+ name: "deprecated_cfg_attr",
+ group: "complexity",
+ desc: "usage of `cfg_attr(rustfmt)` instead of tool attributes",
+ deprecation: None,
+ module: "attrs",
+ },
+ Lint {
+ name: "deprecated_semver",
+ group: "correctness",
+ desc: "use of `#[deprecated(since = \"x\")]` where x is not semver",
+ deprecation: None,
+ module: "attrs",
+ },
+ Lint {
+ name: "deref_addrof",
+ group: "complexity",
+ desc: "use of `*&` or `*&mut` in an expression",
+ deprecation: None,
+ module: "reference",
+ },
+ Lint {
+ name: "derive_hash_xor_eq",
+ group: "correctness",
+ desc: "deriving `Hash` but implementing `PartialEq` explicitly",
+ deprecation: None,
+ module: "derive",
+ },
+ Lint {
+ name: "diverging_sub_expression",
+ group: "complexity",
+ desc: "whether an expression contains a diverging sub expression",
+ deprecation: None,
+ module: "eval_order_dependence",
+ },
+ Lint {
+ name: "doc_markdown",
+ group: "pedantic",
+ desc: "presence of `_`, `::` or camel-case outside backticks in documentation",
+ deprecation: None,
+ module: "doc",
+ },
+ Lint {
+ name: "double_comparisons",
+ group: "complexity",
+ desc: "unnecessary double comparisons that can be simplified",
+ deprecation: None,
+ module: "double_comparison",
+ },
+ Lint {
+ name: "double_must_use",
+ group: "style",
+ desc: "`#[must_use]` attribute on a `#[must_use]`-returning function / method",
+ deprecation: None,
+ module: "functions",
+ },
+ Lint {
+ name: "double_neg",
+ group: "style",
+ desc: "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "double_parens",
+ group: "complexity",
+ desc: "Warn on unnecessary double parentheses",
+ deprecation: None,
+ module: "double_parens",
+ },
+ Lint {
+ name: "drop_bounds",
+ group: "correctness",
+ desc: "Bounds of the form `T: Drop` are useless",
+ deprecation: None,
+ module: "drop_bounds",
+ },
+ Lint {
+ name: "drop_copy",
+ group: "correctness",
+ desc: "calls to `std::mem::drop` with a value that implements Copy",
+ deprecation: None,
+ module: "drop_forget_ref",
+ },
+ Lint {
+ name: "drop_ref",
+ group: "correctness",
+ desc: "calls to `std::mem::drop` with a reference instead of an owned value",
+ deprecation: None,
+ module: "drop_forget_ref",
+ },
+ Lint {
+ name: "duplicate_underscore_argument",
+ group: "style",
+ desc: "function arguments having names which only differ by an underscore",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "duration_subsec",
+ group: "complexity",
+ desc: "checks for calculation of subsecond microseconds or milliseconds",
+ deprecation: None,
+ module: "duration_subsec",
+ },
+ Lint {
+ name: "else_if_without_else",
+ group: "restriction",
+ desc: "`if` expression with an `else if`, but without a final `else` branch",
+ deprecation: None,
+ module: "else_if_without_else",
+ },
+ Lint {
+ name: "empty_enum",
+ group: "pedantic",
+ desc: "enum with no variants",
+ deprecation: None,
+ module: "empty_enum",
+ },
+ Lint {
+ name: "empty_line_after_outer_attr",
+ group: "nursery",
+ desc: "empty line after outer attribute",
+ deprecation: None,
+ module: "attrs",
+ },
+ Lint {
+ name: "empty_loop",
+ group: "style",
+ desc: "empty `loop {}`, which should block or sleep",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "enum_clike_unportable_variant",
+ group: "correctness",
+ desc: "C-like enums that are `repr(isize/usize)` and have values that don\'t fit into an `i32`",
+ deprecation: None,
+ module: "enum_clike",
+ },
+ Lint {
+ name: "enum_glob_use",
+ group: "pedantic",
+ desc: "use items that import all variants of an enum",
+ deprecation: None,
+ module: "wildcard_imports",
+ },
+ Lint {
+ name: "enum_variant_names",
+ group: "style",
+ desc: "enums where all variants share a prefix/postfix",
+ deprecation: None,
+ module: "enum_variants",
+ },
+ Lint {
+ name: "eq_op",
+ group: "correctness",
+ desc: "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)",
+ deprecation: None,
+ module: "eq_op",
+ },
+ Lint {
+ name: "erasing_op",
+ group: "correctness",
+ desc: "using erasing operations, e.g., `x * 0` or `y & 0`",
+ deprecation: None,
+ module: "erasing_op",
+ },
+ Lint {
+ name: "eval_order_dependence",
+ group: "complexity",
+ desc: "whether a variable read occurs before a write depends on sub-expression evaluation order",
+ deprecation: None,
+ module: "eval_order_dependence",
+ },
+ Lint {
+ name: "excessive_precision",
+ group: "style",
+ desc: "excessive precision for float literal",
+ deprecation: None,
+ module: "float_literal",
+ },
+ Lint {
+ name: "exit",
+ group: "restriction",
+ desc: "`std::process::exit` is called, terminating the program",
+ deprecation: None,
+ module: "exit",
+ },
+ Lint {
+ name: "expect_fun_call",
+ group: "perf",
+ desc: "using any `expect` method with a function call",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "expl_impl_clone_on_copy",
+ group: "pedantic",
+ desc: "implementing `Clone` explicitly on `Copy` types",
+ deprecation: None,
+ module: "derive",
+ },
+ Lint {
+ name: "explicit_counter_loop",
+ group: "complexity",
+ desc: "for-looping with an explicit counter when `_.enumerate()` would do",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "explicit_deref_methods",
+ group: "pedantic",
+ desc: "Explicit use of deref or deref_mut method while not in a method chain.",
+ deprecation: None,
+ module: "dereference",
+ },
+ Lint {
+ name: "explicit_into_iter_loop",
+ group: "pedantic",
+ desc: "for-looping over `_.into_iter()` when `_` would do",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "explicit_iter_loop",
+ group: "pedantic",
+ desc: "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "explicit_write",
+ group: "complexity",
+ desc: "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work",
+ deprecation: None,
+ module: "explicit_write",
+ },
+ Lint {
+ name: "extra_unused_lifetimes",
+ group: "complexity",
+ desc: "unused lifetimes in function definitions",
+ deprecation: None,
+ module: "lifetimes",
+ },
+ Lint {
+ name: "fallible_impl_from",
+ group: "nursery",
+ desc: "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`",
+ deprecation: None,
+ module: "fallible_impl_from",
+ },
+ Lint {
+ name: "filetype_is_file",
+ group: "restriction",
+ desc: "`FileType::is_file` is not recommended to test for readable file type",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "filter_map",
+ group: "pedantic",
+ desc: "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "filter_map_next",
+ group: "pedantic",
+ desc: "using combination of `filter_map` and `next` which can usually be written as a single method call",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "filter_next",
+ group: "complexity",
+ desc: "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "find_map",
+ group: "pedantic",
+ desc: "using a combination of `find` and `map` can usually be written as a single method call",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "flat_map_identity",
+ group: "complexity",
+ desc: "call to `flat_map` where `flatten` is sufficient",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "float_arithmetic",
+ group: "restriction",
+ desc: "any floating-point arithmetic statement",
+ deprecation: None,
+ module: "arithmetic",
+ },
+ Lint {
+ name: "float_cmp",
+ group: "correctness",
+ desc: "using `==` or `!=` on float values instead of comparing difference with an epsilon",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "float_cmp_const",
+ group: "restriction",
+ desc: "using `==` or `!=` on float constants instead of comparing difference with an epsilon",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "fn_address_comparisons",
+ group: "correctness",
+ desc: "comparison with an address of a function item",
+ deprecation: None,
+ module: "unnamed_address",
+ },
+ Lint {
+ name: "fn_params_excessive_bools",
+ group: "pedantic",
+ desc: "using too many bools in function parameters",
+ deprecation: None,
+ module: "excessive_bools",
+ },
+ Lint {
+ name: "fn_to_numeric_cast",
+ group: "style",
+ desc: "casting a function pointer to a numeric type other than usize",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "fn_to_numeric_cast_with_truncation",
+ group: "style",
+ desc: "casting a function pointer to a numeric type not wide enough to store the address",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "for_kv_map",
+ group: "style",
+ desc: "looping on a map using `iter` when `keys` or `values` would do",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "for_loop_over_option",
+ group: "correctness",
+ desc: "for-looping over an `Option`, which is more clearly expressed as an `if let`",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "for_loop_over_result",
+ group: "correctness",
+ desc: "for-looping over a `Result`, which is more clearly expressed as an `if let`",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "forget_copy",
+ group: "correctness",
+ desc: "calls to `std::mem::forget` with a value that implements Copy",
+ deprecation: None,
+ module: "drop_forget_ref",
+ },
+ Lint {
+ name: "forget_ref",
+ group: "correctness",
+ desc: "calls to `std::mem::forget` with a reference instead of an owned value",
+ deprecation: None,
+ module: "drop_forget_ref",
+ },
+ Lint {
+ name: "future_not_send",
+ group: "nursery",
+ desc: "public Futures must be Send",
+ deprecation: None,
+ module: "future_not_send",
+ },
+ Lint {
+ name: "get_last_with_len",
+ group: "complexity",
+ desc: "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler",
+ deprecation: None,
+ module: "get_last_with_len",
+ },
+ Lint {
+ name: "get_unwrap",
+ group: "restriction",
+ desc: "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "identity_conversion",
+ group: "complexity",
+ desc: "using always-identical `Into`/`From`/`IntoIter` conversions",
+ deprecation: None,
+ module: "identity_conversion",
+ },
+ Lint {
+ name: "identity_op",
+ group: "complexity",
+ desc: "using identity operations, e.g., `x + 0` or `y / 1`",
+ deprecation: None,
+ module: "identity_op",
+ },
+ Lint {
+ name: "if_let_mutex",
+ group: "correctness",
+ desc: "locking a `Mutex` in an `if let` block can cause deadlocks",
+ deprecation: None,
+ module: "if_let_mutex",
+ },
+ Lint {
+ name: "if_let_some_result",
+ group: "style",
+ desc: "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead",
+ deprecation: None,
+ module: "if_let_some_result",
+ },
+ Lint {
+ name: "if_not_else",
+ group: "pedantic",
+ desc: "`if` branches that could be swapped so no negation operation is necessary on the condition",
+ deprecation: None,
+ module: "if_not_else",
+ },
+ Lint {
+ name: "if_same_then_else",
+ group: "correctness",
+ desc: "`if` with the same `then` and `else` blocks",
+ deprecation: None,
+ module: "copies",
+ },
+ Lint {
+ name: "ifs_same_cond",
+ group: "correctness",
+ desc: "consecutive `if`s with the same condition",
+ deprecation: None,
+ module: "copies",
+ },
+ Lint {
+ name: "implicit_hasher",
+ group: "pedantic",
+ desc: "missing generalization over different hashers",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "implicit_return",
+ group: "restriction",
+ desc: "use a return statement like `return expr` instead of an expression",
+ deprecation: None,
+ module: "implicit_return",
+ },
+ Lint {
+ name: "implicit_saturating_sub",
+ group: "pedantic",
+ desc: "Perform saturating subtraction instead of implicitly checking lower bound of data type",
+ deprecation: None,
+ module: "implicit_saturating_sub",
+ },
+ Lint {
+ name: "imprecise_flops",
+ group: "nursery",
+ desc: "usage of imprecise floating point operations",
+ deprecation: None,
+ module: "floating_point_arithmetic",
+ },
+ Lint {
+ name: "inconsistent_digit_grouping",
+ group: "style",
+ desc: "integer literals with digits grouped inconsistently",
+ deprecation: None,
+ module: "literal_representation",
+ },
+ Lint {
+ name: "indexing_slicing",
+ group: "restriction",
+ desc: "indexing/slicing usage",
+ deprecation: None,
+ module: "indexing_slicing",
+ },
+ Lint {
+ name: "ineffective_bit_mask",
+ group: "correctness",
+ desc: "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`",
+ deprecation: None,
+ module: "bit_mask",
+ },
+ Lint {
+ name: "inefficient_to_string",
+ group: "pedantic",
+ desc: "using `to_string` on `&&T` where `T: ToString`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "infallible_destructuring_match",
+ group: "style",
+ desc: "a `match` statement with a single infallible arm instead of a `let`",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "infinite_iter",
+ group: "correctness",
+ desc: "infinite iteration",
+ deprecation: None,
+ module: "infinite_iter",
+ },
+ Lint {
+ name: "inherent_to_string",
+ group: "style",
+ desc: "type implements inherent method `to_string()`, but should instead implement the `Display` trait",
+ deprecation: None,
+ module: "inherent_to_string",
+ },
+ Lint {
+ name: "inherent_to_string_shadow_display",
+ group: "correctness",
+ desc: "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait",
+ deprecation: None,
+ module: "inherent_to_string",
+ },
+ Lint {
+ name: "inline_always",
+ group: "pedantic",
+ desc: "use of `#[inline(always)]`",
+ deprecation: None,
+ module: "attrs",
+ },
+ Lint {
+ name: "inline_fn_without_body",
+ group: "correctness",
+ desc: "use of `#[inline]` on trait methods without bodies",
+ deprecation: None,
+ module: "inline_fn_without_body",
+ },
+ Lint {
+ name: "int_plus_one",
+ group: "complexity",
+ desc: "instead of using `x >= y + 1`, use `x > y`",
+ deprecation: None,
+ module: "int_plus_one",
+ },
+ Lint {
+ name: "integer_arithmetic",
+ group: "restriction",
+ desc: "any integer arithmetic expression which could overflow or panic",
+ deprecation: None,
+ module: "arithmetic",
+ },
+ Lint {
+ name: "integer_division",
+ group: "restriction",
+ desc: "integer division may cause loss of precision",
+ deprecation: None,
+ module: "integer_division",
+ },
+ Lint {
+ name: "into_iter_on_ref",
+ group: "style",
+ desc: "using `.into_iter()` on a reference",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "invalid_atomic_ordering",
+ group: "correctness",
+ desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences",
+ deprecation: None,
+ module: "atomic_ordering",
+ },
+ Lint {
+ name: "invalid_regex",
+ group: "correctness",
+ desc: "invalid regular expressions",
+ deprecation: None,
+ module: "regex",
+ },
+ Lint {
+ name: "invalid_upcast_comparisons",
+ group: "pedantic",
+ desc: "a comparison involving an upcast which is always true or false",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "items_after_statements",
+ group: "pedantic",
+ desc: "blocks where an item comes after a statement",
+ deprecation: None,
+ module: "items_after_statements",
+ },
+ Lint {
+ name: "iter_cloned_collect",
+ group: "style",
+ desc: "using `.cloned().collect()` on slice to create a `Vec`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "iter_next_loop",
+ group: "correctness",
+ desc: "for-looping over `_.next()` which is probably not intended",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "iter_nth",
+ group: "perf",
+ desc: "using `.iter().nth()` on a standard library type with O(1) element access",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "iter_nth_zero",
+ group: "style",
+ desc: "replace `iter.nth(0)` with `iter.next()`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "iter_skip_next",
+ group: "style",
+ desc: "using `.skip(x).next()` on an iterator",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "iterator_step_by_zero",
+ group: "correctness",
+ desc: "using `Iterator::step_by(0)`, which will panic at runtime",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "just_underscores_and_digits",
+ group: "style",
+ desc: "unclear name",
+ deprecation: None,
+ module: "non_expressive_names",
+ },
+ Lint {
+ name: "large_const_arrays",
+ group: "perf",
+ desc: "large non-scalar const array may cause performance overhead",
+ deprecation: None,
+ module: "large_const_arrays",
+ },
+ Lint {
+ name: "large_digit_groups",
+ group: "pedantic",
+ desc: "grouping digits into groups that are too large",
+ deprecation: None,
+ module: "literal_representation",
+ },
+ Lint {
+ name: "large_enum_variant",
+ group: "perf",
+ desc: "large size difference between variants on an enum",
+ deprecation: None,
+ module: "large_enum_variant",
+ },
+ Lint {
+ name: "large_stack_arrays",
+ group: "pedantic",
+ desc: "allocating large arrays on stack may cause stack overflow",
+ deprecation: None,
+ module: "large_stack_arrays",
+ },
+ Lint {
+ name: "len_without_is_empty",
+ group: "style",
+ desc: "traits or impls with a public `len` method but no corresponding `is_empty` method",
+ deprecation: None,
+ module: "len_zero",
+ },
+ Lint {
+ name: "len_zero",
+ group: "style",
+ desc: "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead",
+ deprecation: None,
+ module: "len_zero",
+ },
+ Lint {
+ name: "let_and_return",
+ group: "style",
+ desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block",
+ deprecation: None,
+ module: "returns",
+ },
+ Lint {
+ name: "let_underscore_lock",
+ group: "correctness",
+ desc: "non-binding let on a synchronization lock",
+ deprecation: None,
+ module: "let_underscore",
+ },
+ Lint {
+ name: "let_underscore_must_use",
+ group: "restriction",
+ desc: "non-binding let on a `#[must_use]` expression",
+ deprecation: None,
+ module: "let_underscore",
+ },
+ Lint {
+ name: "let_unit_value",
+ group: "pedantic",
+ desc: "creating a `let` binding to a value of unit type, which usually can\'t be used afterwards",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "linkedlist",
+ group: "pedantic",
+ desc: "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "logic_bug",
+ group: "correctness",
+ desc: "boolean expressions that contain terminals which can be eliminated",
+ deprecation: None,
+ module: "booleans",
+ },
+ Lint {
+ name: "lossy_float_literal",
+ group: "restriction",
+ desc: "lossy whole number float literals",
+ deprecation: None,
+ module: "float_literal",
+ },
+ Lint {
+ name: "macro_use_imports",
+ group: "pedantic",
+ desc: "#[macro_use] is no longer needed",
+ deprecation: None,
+ module: "macro_use",
+ },
+ Lint {
+ name: "main_recursion",
+ group: "style",
+ desc: "recursion using the entrypoint",
+ deprecation: None,
+ module: "main_recursion",
+ },
++ Lint {
++ name: "manual_async_fn",
++ group: "style",
++ desc: "manual implementations of `async` functions can be simplified using the dedicated syntax",
++ deprecation: None,
++ module: "manual_async_fn",
++ },
+ Lint {
+ name: "manual_memcpy",
+ group: "perf",
+ desc: "manually copying items between slices",
+ deprecation: None,
+ module: "loops",
+ },
++ Lint {
++ name: "manual_non_exhaustive",
++ group: "style",
++ desc: "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]",
++ deprecation: None,
++ module: "manual_non_exhaustive",
++ },
+ Lint {
+ name: "manual_saturating_arithmetic",
+ group: "style",
+ desc: "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "manual_swap",
+ group: "complexity",
+ desc: "manual swap of two variables",
+ deprecation: None,
+ module: "swap",
+ },
+ Lint {
+ name: "many_single_char_names",
+ group: "style",
+ desc: "too many single character bindings",
+ deprecation: None,
+ module: "non_expressive_names",
+ },
+ Lint {
+ name: "map_clone",
+ group: "style",
+ desc: "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types",
+ deprecation: None,
+ module: "map_clone",
+ },
+ Lint {
+ name: "map_entry",
+ group: "perf",
+ desc: "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`",
+ deprecation: None,
+ module: "entry",
+ },
+ Lint {
+ name: "map_flatten",
+ group: "pedantic",
+ desc: "using combinations of `flatten` and `map` which can usually be written as a single method call",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "match_as_ref",
+ group: "complexity",
+ desc: "a `match` on an Option value instead of using `as_ref()` or `as_mut`",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "match_bool",
+ group: "pedantic",
+ desc: "a `match` on a boolean expression instead of an `if..else` block",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "match_on_vec_items",
++ group: "pedantic",
+ desc: "matching on vector elements can panic",
+ deprecation: None,
+ module: "match_on_vec_items",
+ },
+ Lint {
+ name: "match_overlapping_arm",
+ group: "style",
+ desc: "a `match` with overlapping arms",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "match_ref_pats",
+ group: "style",
+ desc: "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "match_same_arms",
+ group: "pedantic",
+ desc: "`match` with identical arm bodies",
+ deprecation: None,
+ module: "copies",
+ },
+ Lint {
+ name: "match_single_binding",
+ group: "complexity",
+ desc: "a match with a single binding instead of using `let` statement",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "match_wild_err_arm",
+ group: "style",
+ desc: "a `match` with `Err(_)` arm and take drastic actions",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "maybe_infinite_iter",
+ group: "pedantic",
+ desc: "possible infinite iteration",
+ deprecation: None,
+ module: "infinite_iter",
+ },
+ Lint {
+ name: "mem_discriminant_non_enum",
+ group: "correctness",
+ desc: "calling `mem::descriminant` on non-enum type",
+ deprecation: None,
+ module: "mem_discriminant",
+ },
+ Lint {
+ name: "mem_forget",
+ group: "restriction",
+ desc: "`mem::forget` usage on `Drop` types, likely to cause memory leaks",
+ deprecation: None,
+ module: "mem_forget",
+ },
+ Lint {
+ name: "mem_replace_option_with_none",
+ group: "style",
+ desc: "replacing an `Option` with `None` instead of `take()`",
+ deprecation: None,
+ module: "mem_replace",
+ },
+ Lint {
+ name: "mem_replace_with_default",
+ group: "style",
+ desc: "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`",
+ deprecation: None,
+ module: "mem_replace",
+ },
+ Lint {
+ name: "mem_replace_with_uninit",
+ group: "correctness",
+ desc: "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`",
+ deprecation: None,
+ module: "mem_replace",
+ },
+ Lint {
+ name: "min_max",
+ group: "correctness",
+ desc: "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant",
+ deprecation: None,
+ module: "minmax",
+ },
+ Lint {
+ name: "mismatched_target_os",
+ group: "correctness",
+ desc: "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`",
+ deprecation: None,
+ module: "attrs",
+ },
+ Lint {
+ name: "misrefactored_assign_op",
+ group: "complexity",
+ desc: "having a variable on both sides of an assign op",
+ deprecation: None,
+ module: "assign_ops",
+ },
+ Lint {
+ name: "missing_const_for_fn",
+ group: "nursery",
+ desc: "Lint functions definitions that could be made `const fn`",
+ deprecation: None,
+ module: "missing_const_for_fn",
+ },
+ Lint {
+ name: "missing_docs_in_private_items",
+ group: "restriction",
+ desc: "detects missing documentation for public and private members",
+ deprecation: None,
+ module: "missing_doc",
+ },
+ Lint {
+ name: "missing_errors_doc",
+ group: "pedantic",
+ desc: "`pub fn` returns `Result` without `# Errors` in doc comment",
+ deprecation: None,
+ module: "doc",
+ },
+ Lint {
+ name: "missing_inline_in_public_items",
+ group: "restriction",
+ desc: "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)",
+ deprecation: None,
+ module: "missing_inline",
+ },
+ Lint {
+ name: "missing_safety_doc",
+ group: "style",
+ desc: "`pub unsafe fn` without `# Safety` docs",
+ deprecation: None,
+ module: "doc",
+ },
+ Lint {
+ name: "mistyped_literal_suffixes",
+ group: "correctness",
+ desc: "mistyped literal suffix",
+ deprecation: None,
+ module: "literal_representation",
+ },
+ Lint {
+ name: "mixed_case_hex_literals",
+ group: "style",
+ desc: "hex literals whose letter digits are not consistently upper- or lowercased",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "module_inception",
+ group: "style",
+ desc: "modules that have the same name as their parent module",
+ deprecation: None,
+ module: "enum_variants",
+ },
+ Lint {
+ name: "module_name_repetitions",
+ group: "pedantic",
+ desc: "type names prefixed/postfixed with their containing module\'s name",
+ deprecation: None,
+ module: "enum_variants",
+ },
+ Lint {
+ name: "modulo_arithmetic",
+ group: "restriction",
+ desc: "any modulo arithmetic statement",
+ deprecation: None,
+ module: "modulo_arithmetic",
+ },
+ Lint {
+ name: "modulo_one",
+ group: "correctness",
+ desc: "taking a number modulo 1, which always returns 0",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "multiple_crate_versions",
+ group: "cargo",
+ desc: "multiple versions of the same crate being used",
+ deprecation: None,
+ module: "multiple_crate_versions",
+ },
+ Lint {
+ name: "multiple_inherent_impl",
+ group: "restriction",
+ desc: "Multiple inherent impl that could be grouped",
+ deprecation: None,
+ module: "inherent_impl",
+ },
+ Lint {
+ name: "must_use_candidate",
+ group: "pedantic",
+ desc: "function or method that could take a `#[must_use]` attribute",
+ deprecation: None,
+ module: "functions",
+ },
+ Lint {
+ name: "must_use_unit",
+ group: "style",
+ desc: "`#[must_use]` attribute on a unit-returning function / method",
+ deprecation: None,
+ module: "functions",
+ },
+ Lint {
+ name: "mut_from_ref",
+ group: "correctness",
+ desc: "fns that create mutable refs from immutable ref args",
+ deprecation: None,
+ module: "ptr",
+ },
+ Lint {
+ name: "mut_mut",
+ group: "pedantic",
+ desc: "usage of double-mut refs, e.g., `&mut &mut ...`",
+ deprecation: None,
+ module: "mut_mut",
+ },
+ Lint {
+ name: "mut_range_bound",
+ group: "complexity",
+ desc: "for loop over a range where one of the bounds is a mutable variable",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "mutable_key_type",
+ group: "correctness",
+ desc: "Check for mutable `Map`/`Set` key type",
+ deprecation: None,
+ module: "mut_key",
+ },
+ Lint {
+ name: "mutex_atomic",
+ group: "perf",
+ desc: "using a mutex where an atomic value could be used instead",
+ deprecation: None,
+ module: "mutex_atomic",
+ },
+ Lint {
+ name: "mutex_integer",
+ group: "nursery",
+ desc: "using a mutex for an integer type",
+ deprecation: None,
+ module: "mutex_atomic",
+ },
+ Lint {
+ name: "naive_bytecount",
+ group: "perf",
+ desc: "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values",
+ deprecation: None,
+ module: "bytecount",
+ },
+ Lint {
+ name: "needless_bool",
+ group: "complexity",
+ desc: "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`",
+ deprecation: None,
+ module: "needless_bool",
+ },
+ Lint {
+ name: "needless_borrow",
+ group: "nursery",
+ desc: "taking a reference that is going to be automatically dereferenced",
+ deprecation: None,
+ module: "needless_borrow",
+ },
+ Lint {
+ name: "needless_borrowed_reference",
+ group: "complexity",
+ desc: "taking a needless borrowed reference",
+ deprecation: None,
+ module: "needless_borrowed_ref",
+ },
+ Lint {
+ name: "needless_collect",
+ group: "perf",
+ desc: "collecting an iterator when collect is not needed",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "needless_continue",
+ group: "pedantic",
+ desc: "`continue` statements that can be replaced by a rearrangement of code",
+ deprecation: None,
+ module: "needless_continue",
+ },
+ Lint {
+ name: "needless_doctest_main",
+ group: "style",
+ desc: "presence of `fn main() {` in code examples",
+ deprecation: None,
+ module: "doc",
+ },
+ Lint {
+ name: "needless_lifetimes",
+ group: "complexity",
+ desc: "using explicit lifetimes for references in function arguments when elision rules would allow omitting them",
+ deprecation: None,
+ module: "lifetimes",
+ },
+ Lint {
+ name: "needless_pass_by_value",
+ group: "pedantic",
+ desc: "functions taking arguments by value, but not consuming them in its body",
+ deprecation: None,
+ module: "needless_pass_by_value",
+ },
+ Lint {
+ name: "needless_range_loop",
+ group: "style",
+ desc: "for-looping over a range of indices where an iterator over items would do",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "needless_return",
+ group: "style",
+ desc: "using a return statement like `return expr;` where an expression would suffice",
+ deprecation: None,
+ module: "returns",
+ },
+ Lint {
+ name: "needless_update",
+ group: "complexity",
+ desc: "using `Foo { ..base }` when there are no missing fields",
+ deprecation: None,
+ module: "needless_update",
+ },
+ Lint {
+ name: "neg_cmp_op_on_partial_ord",
+ group: "complexity",
+ desc: "The use of negated comparison operators on partially ordered types may produce confusing code.",
+ deprecation: None,
+ module: "neg_cmp_op_on_partial_ord",
+ },
+ Lint {
+ name: "neg_multiply",
+ group: "style",
+ desc: "multiplying integers with `-1`",
+ deprecation: None,
+ module: "neg_multiply",
+ },
+ Lint {
+ name: "never_loop",
+ group: "correctness",
+ desc: "any loop that will always `break` or `return`",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "new_ret_no_self",
+ group: "style",
+ desc: "not returning type containing `Self` in a `new` method",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "new_without_default",
+ group: "style",
+ desc: "`fn new() -> Self` method without `Default` implementation",
+ deprecation: None,
+ module: "new_without_default",
+ },
+ Lint {
+ name: "no_effect",
+ group: "complexity",
+ desc: "statements with no effect",
+ deprecation: None,
+ module: "no_effect",
+ },
+ Lint {
+ name: "non_ascii_literal",
+ group: "pedantic",
+ desc: "using any literal non-ASCII chars in a string literal instead of using the `\\\\u` escape",
+ deprecation: None,
+ module: "unicode",
+ },
+ Lint {
+ name: "nonminimal_bool",
+ group: "complexity",
+ desc: "boolean expressions that can be written more concisely",
+ deprecation: None,
+ module: "booleans",
+ },
+ Lint {
+ name: "nonsensical_open_options",
+ group: "correctness",
+ desc: "nonsensical combination of options for opening a file",
+ deprecation: None,
+ module: "open_options",
+ },
+ Lint {
+ name: "not_unsafe_ptr_arg_deref",
+ group: "correctness",
+ desc: "public functions dereferencing raw pointer arguments but not marked `unsafe`",
+ deprecation: None,
+ module: "functions",
+ },
+ Lint {
+ name: "ok_expect",
+ group: "style",
+ desc: "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "op_ref",
+ group: "style",
+ desc: "taking a reference to satisfy the type constraints on `==`",
+ deprecation: None,
+ module: "eq_op",
+ },
+ Lint {
+ name: "option_and_then_some",
+ group: "complexity",
+ desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "option_as_ref_deref",
+ group: "complexity",
+ desc: "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "option_env_unwrap",
+ group: "correctness",
+ desc: "using `option_env!(...).unwrap()` to get environment variable",
+ deprecation: None,
+ module: "option_env_unwrap",
+ },
+ Lint {
+ name: "option_expect_used",
+ group: "restriction",
+ desc: "using `Option.expect()`, which might be better handled",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "option_map_or_none",
+ group: "style",
+ desc: "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "option_map_unit_fn",
+ group: "complexity",
+ desc: "using `option.map(f)`, where `f` is a function or closure that returns `()`",
+ deprecation: None,
+ module: "map_unit_fn",
+ },
+ Lint {
+ name: "option_map_unwrap_or",
+ group: "pedantic",
+ desc: "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "option_map_unwrap_or_else",
+ group: "pedantic",
+ desc: "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "option_option",
+ group: "pedantic",
+ desc: "usage of `Option<Option<T>>`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "option_unwrap_used",
+ group: "restriction",
+ desc: "using `Option.unwrap()`, which should at least get a better message using `expect()`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "or_fun_call",
+ group: "perf",
+ desc: "using any `*or` method with a function call, which suggests `*or_else`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "out_of_bounds_indexing",
+ group: "correctness",
+ desc: "out of bounds constant indexing",
+ deprecation: None,
+ module: "indexing_slicing",
+ },
+ Lint {
+ name: "overflow_check_conditional",
+ group: "complexity",
+ desc: "overflow checks inspired by C which are likely to panic",
+ deprecation: None,
+ module: "overflow_check_conditional",
+ },
+ Lint {
+ name: "panic",
+ group: "restriction",
+ desc: "usage of the `panic!` macro",
+ deprecation: None,
+ module: "panic_unimplemented",
+ },
+ Lint {
+ name: "panic_params",
+ group: "style",
+ desc: "missing parameters in `panic!` calls",
+ deprecation: None,
+ module: "panic_unimplemented",
+ },
+ Lint {
+ name: "panicking_unwrap",
+ group: "correctness",
+ desc: "checks for calls of `unwrap[_err]()` that will always fail",
+ deprecation: None,
+ module: "unwrap",
+ },
+ Lint {
+ name: "partialeq_ne_impl",
+ group: "complexity",
+ desc: "re-implementing `PartialEq::ne`",
+ deprecation: None,
+ module: "partialeq_ne_impl",
+ },
+ Lint {
+ name: "path_buf_push_overwrite",
+ group: "nursery",
+ desc: "calling `push` with file system root on `PathBuf` can overwrite it",
+ deprecation: None,
+ module: "path_buf_push_overwrite",
+ },
+ Lint {
+ name: "possible_missing_comma",
+ group: "correctness",
+ desc: "possible missing comma in array",
+ deprecation: None,
+ module: "formatting",
+ },
+ Lint {
+ name: "precedence",
+ group: "complexity",
+ desc: "operations where precedence may be unclear",
+ deprecation: None,
+ module: "precedence",
+ },
+ Lint {
+ name: "print_literal",
+ group: "style",
+ desc: "printing a literal with a format string",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "print_stdout",
+ group: "restriction",
+ desc: "printing on stdout",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "print_with_newline",
+ group: "style",
+ desc: "using `print!()` with a format string that ends in a single newline",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "println_empty_string",
+ group: "style",
+ desc: "using `println!(\"\")` with an empty string",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "ptr_arg",
+ group: "style",
+ desc: "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively",
+ deprecation: None,
+ module: "ptr",
+ },
+ Lint {
+ name: "ptr_offset_with_cast",
+ group: "complexity",
+ desc: "unneeded pointer offset cast",
+ deprecation: None,
+ module: "ptr_offset_with_cast",
+ },
+ Lint {
+ name: "pub_enum_variant_names",
+ group: "pedantic",
+ desc: "enums where all variants share a prefix/postfix",
+ deprecation: None,
+ module: "enum_variants",
+ },
+ Lint {
+ name: "question_mark",
+ group: "style",
+ desc: "checks for expressions that could be replaced by the question mark operator",
+ deprecation: None,
+ module: "question_mark",
+ },
+ Lint {
+ name: "range_minus_one",
+ group: "complexity",
+ desc: "`x..=(y-1)` reads better as `x..y`",
+ deprecation: None,
+ module: "ranges",
+ },
+ Lint {
+ name: "range_plus_one",
+ group: "pedantic",
+ desc: "`x..(y+1)` reads better as `x..=y`",
+ deprecation: None,
+ module: "ranges",
+ },
+ Lint {
+ name: "range_zip_with_len",
+ group: "complexity",
+ desc: "zipping iterator with a range when `enumerate()` would do",
+ deprecation: None,
+ module: "ranges",
+ },
+ Lint {
+ name: "redundant_allocation",
+ group: "perf",
+ desc: "redundant allocation",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "redundant_clone",
+ group: "perf",
+ desc: "`clone()` of an owned value that is going to be dropped immediately",
+ deprecation: None,
+ module: "redundant_clone",
+ },
+ Lint {
+ name: "redundant_closure",
+ group: "style",
+ desc: "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)",
+ deprecation: None,
+ module: "eta_reduction",
+ },
+ Lint {
+ name: "redundant_closure_call",
+ group: "complexity",
+ desc: "throwaway closures called in the expression they are defined",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "redundant_closure_for_method_calls",
+ group: "pedantic",
+ desc: "redundant closures for method calls",
+ deprecation: None,
+ module: "eta_reduction",
+ },
+ Lint {
+ name: "redundant_field_names",
+ group: "style",
+ desc: "checks for fields in struct literals where shorthands could be used",
+ deprecation: None,
+ module: "redundant_field_names",
+ },
+ Lint {
+ name: "redundant_pattern",
+ group: "style",
+ desc: "using `name @ _` in a pattern",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "redundant_pattern_matching",
+ group: "style",
+ desc: "use the proper utility function avoiding an `if let`",
+ deprecation: None,
+ module: "redundant_pattern_matching",
+ },
+ Lint {
+ name: "redundant_pub_crate",
+ group: "nursery",
+ desc: "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them.",
+ deprecation: None,
+ module: "redundant_pub_crate",
+ },
+ Lint {
+ name: "redundant_static_lifetimes",
+ group: "style",
+ desc: "Using explicit `\'static` lifetime for constants or statics when elision rules would allow omitting them.",
+ deprecation: None,
+ module: "redundant_static_lifetimes",
+ },
+ Lint {
+ name: "ref_in_deref",
+ group: "complexity",
+ desc: "Use of reference in auto dereference expression.",
+ deprecation: None,
+ module: "reference",
+ },
+ Lint {
+ name: "regex_macro",
+ group: "style",
+ desc: "use of `regex!(_)` instead of `Regex::new(_)`",
+ deprecation: None,
+ module: "regex",
+ },
+ Lint {
+ name: "rest_pat_in_fully_bound_structs",
+ group: "restriction",
+ desc: "a match on a struct that binds all fields but still uses the wildcard pattern",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "result_expect_used",
+ group: "restriction",
+ desc: "using `Result.expect()`, which might be better handled",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "result_map_or_into_option",
+ group: "style",
+ desc: "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "result_map_unit_fn",
+ group: "complexity",
+ desc: "using `result.map(f)`, where `f` is a function or closure that returns `()`",
+ deprecation: None,
+ module: "map_unit_fn",
+ },
+ Lint {
+ name: "result_map_unwrap_or_else",
+ group: "pedantic",
+ desc: "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "result_unwrap_used",
+ group: "restriction",
+ desc: "using `Result.unwrap()`, which might be better handled",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "reverse_range_loop",
+ group: "correctness",
+ desc: "iteration over an empty range, such as `10..0` or `5..5`",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "same_functions_in_if_condition",
+ group: "pedantic",
+ desc: "consecutive `if`s with the same function call",
+ deprecation: None,
+ module: "copies",
+ },
+ Lint {
+ name: "search_is_some",
+ group: "complexity",
+ desc: "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "serde_api_misuse",
+ group: "correctness",
+ desc: "various things that will negatively affect your serde experience",
+ deprecation: None,
+ module: "serde_api",
+ },
+ Lint {
+ name: "shadow_reuse",
+ group: "restriction",
+ desc: "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`",
+ deprecation: None,
+ module: "shadow",
+ },
+ Lint {
+ name: "shadow_same",
+ group: "restriction",
+ desc: "rebinding a name to itself, e.g., `let mut x = &mut x`",
+ deprecation: None,
+ module: "shadow",
+ },
+ Lint {
+ name: "shadow_unrelated",
+ group: "pedantic",
+ desc: "rebinding a name without even using the original value",
+ deprecation: None,
+ module: "shadow",
+ },
+ Lint {
+ name: "short_circuit_statement",
+ group: "complexity",
+ desc: "using a short circuit boolean condition as a statement",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "should_implement_trait",
+ group: "style",
+ desc: "defining a method that should be implementing a std trait",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "similar_names",
+ group: "pedantic",
+ desc: "similarly named items and bindings",
+ deprecation: None,
+ module: "non_expressive_names",
+ },
+ Lint {
+ name: "single_char_pattern",
+ group: "perf",
+ desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "single_component_path_imports",
+ group: "style",
+ desc: "imports with single component path are redundant",
+ deprecation: None,
+ module: "single_component_path_imports",
+ },
+ Lint {
+ name: "single_match",
+ group: "style",
+ desc: "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "single_match_else",
+ group: "pedantic",
+ desc: "a `match` statement with two arms where the second arm\'s pattern is a placeholder instead of a specific match pattern",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "skip_while_next",
+ group: "complexity",
+ desc: "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "slow_vector_initialization",
+ group: "perf",
+ desc: "slow vector initialization",
+ deprecation: None,
+ module: "slow_vector_initialization",
+ },
+ Lint {
+ name: "string_add",
+ group: "restriction",
+ desc: "using `x + ..` where x is a `String` instead of `push_str()`",
+ deprecation: None,
+ module: "strings",
+ },
+ Lint {
+ name: "string_add_assign",
+ group: "pedantic",
+ desc: "using `x = x + ..` where x is a `String` instead of `push_str()`",
+ deprecation: None,
+ module: "strings",
+ },
+ Lint {
+ name: "string_extend_chars",
+ group: "style",
+ desc: "using `x.extend(s.chars())` where s is a `&str` or `String`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "string_lit_as_bytes",
+ group: "style",
+ desc: "calling `as_bytes` on a string literal instead of using a byte string literal",
+ deprecation: None,
+ module: "strings",
+ },
+ Lint {
+ name: "struct_excessive_bools",
+ group: "pedantic",
+ desc: "using too many bools in a struct",
+ deprecation: None,
+ module: "excessive_bools",
+ },
+ Lint {
+ name: "suboptimal_flops",
+ group: "nursery",
+ desc: "usage of sub-optimal floating point operations",
+ deprecation: None,
+ module: "floating_point_arithmetic",
+ },
+ Lint {
+ name: "suspicious_arithmetic_impl",
+ group: "correctness",
+ desc: "suspicious use of operators in impl of arithmetic trait",
+ deprecation: None,
+ module: "suspicious_trait_impl",
+ },
+ Lint {
+ name: "suspicious_assignment_formatting",
+ group: "style",
+ desc: "suspicious formatting of `*=`, `-=` or `!=`",
+ deprecation: None,
+ module: "formatting",
+ },
+ Lint {
+ name: "suspicious_else_formatting",
+ group: "style",
+ desc: "suspicious formatting of `else`",
+ deprecation: None,
+ module: "formatting",
+ },
+ Lint {
+ name: "suspicious_map",
+ group: "complexity",
+ desc: "suspicious usage of map",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "suspicious_op_assign_impl",
+ group: "correctness",
+ desc: "suspicious use of operators in impl of OpAssign trait",
+ deprecation: None,
+ module: "suspicious_trait_impl",
+ },
+ Lint {
+ name: "suspicious_unary_op_formatting",
+ group: "style",
+ desc: "suspicious formatting of unary `-` or `!` on the RHS of a BinOp",
+ deprecation: None,
+ module: "formatting",
+ },
+ Lint {
+ name: "tabs_in_doc_comments",
+ group: "style",
+ desc: "using tabs in doc comments is not recommended",
+ deprecation: None,
+ module: "tabs_in_doc_comments",
+ },
+ Lint {
+ name: "temporary_assignment",
+ group: "complexity",
+ desc: "assignments to temporaries",
+ deprecation: None,
+ module: "temporary_assignment",
+ },
+ Lint {
+ name: "temporary_cstring_as_ptr",
+ group: "correctness",
+ desc: "getting the inner pointer of a temporary `CString`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "to_digit_is_some",
+ group: "style",
+ desc: "`char.is_digit()` is clearer",
+ deprecation: None,
+ module: "to_digit_is_some",
+ },
+ Lint {
+ name: "todo",
+ group: "restriction",
+ desc: "`todo!` should not be present in production code",
+ deprecation: None,
+ module: "panic_unimplemented",
+ },
+ Lint {
+ name: "too_many_arguments",
+ group: "complexity",
+ desc: "functions with too many arguments",
+ deprecation: None,
+ module: "functions",
+ },
+ Lint {
+ name: "too_many_lines",
+ group: "pedantic",
+ desc: "functions with too many lines",
+ deprecation: None,
+ module: "functions",
+ },
+ Lint {
+ name: "toplevel_ref_arg",
+ group: "style",
+ desc: "an entire binding declared as `ref`, in a function argument or a `let` statement",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "transmute_bytes_to_str",
+ group: "complexity",
+ desc: "transmutes from a `&[u8]` to a `&str`",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "transmute_float_to_int",
+ group: "complexity",
+ desc: "transmutes from a float to an integer",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "transmute_int_to_bool",
+ group: "complexity",
+ desc: "transmutes from an integer to a `bool`",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "transmute_int_to_char",
+ group: "complexity",
+ desc: "transmutes from an integer to a `char`",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "transmute_int_to_float",
+ group: "complexity",
+ desc: "transmutes from an integer to a float",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "transmute_ptr_to_ptr",
+ group: "complexity",
+ desc: "transmutes from a pointer to a pointer / a reference to a reference",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "transmute_ptr_to_ref",
+ group: "complexity",
+ desc: "transmutes from a pointer to a reference type",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "transmuting_null",
+ group: "correctness",
+ desc: "transmutes from a null pointer to a reference, which is undefined behavior",
+ deprecation: None,
+ module: "transmuting_null",
+ },
+ Lint {
+ name: "trivial_regex",
+ group: "style",
+ desc: "trivial regular expressions",
+ deprecation: None,
+ module: "regex",
+ },
+ Lint {
+ name: "trivially_copy_pass_by_ref",
+ group: "pedantic",
+ desc: "functions taking small copyable arguments by reference",
+ deprecation: None,
+ module: "trivially_copy_pass_by_ref",
+ },
+ Lint {
+ name: "try_err",
+ group: "style",
+ desc: "return errors explicitly rather than hiding them behind a `?`",
+ deprecation: None,
+ module: "try_err",
+ },
+ Lint {
+ name: "type_complexity",
+ group: "complexity",
+ desc: "usage of very complex types that might be better factored into `type` definitions",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "type_repetition_in_bounds",
+ group: "pedantic",
+ desc: "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`",
+ deprecation: None,
+ module: "trait_bounds",
+ },
+ Lint {
+ name: "unicode_not_nfc",
+ group: "pedantic",
+ desc: "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)",
+ deprecation: None,
+ module: "unicode",
+ },
+ Lint {
+ name: "unimplemented",
+ group: "restriction",
+ desc: "`unimplemented!` should not be present in production code",
+ deprecation: None,
+ module: "panic_unimplemented",
+ },
+ Lint {
+ name: "uninit_assumed_init",
+ group: "correctness",
+ desc: "`MaybeUninit::uninit().assume_init()`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "unit_arg",
+ group: "complexity",
+ desc: "passing unit to a function",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "unit_cmp",
+ group: "correctness",
+ desc: "comparing unit values",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "unknown_clippy_lints",
+ group: "style",
+ desc: "unknown_lints for scoped Clippy lints",
+ deprecation: None,
+ module: "attrs",
+ },
+ Lint {
+ name: "unnecessary_cast",
+ group: "complexity",
+ desc: "cast to the same type, e.g., `x as i32` where `x: i32`",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "unnecessary_filter_map",
+ group: "complexity",
+ desc: "using `filter_map` when a more succinct alternative exists",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "unnecessary_fold",
+ group: "style",
+ desc: "using `fold` when a more succinct alternative exists",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "unnecessary_mut_passed",
+ group: "style",
+ desc: "an argument passed as a mutable reference although the callee only demands an immutable reference",
+ deprecation: None,
+ module: "mut_reference",
+ },
+ Lint {
+ name: "unnecessary_operation",
+ group: "complexity",
+ desc: "outer expressions with no effect",
+ deprecation: None,
+ module: "no_effect",
+ },
+ Lint {
+ name: "unnecessary_unwrap",
+ group: "complexity",
+ desc: "checks for calls of `unwrap[_err]()` that cannot fail",
+ deprecation: None,
+ module: "unwrap",
+ },
+ Lint {
+ name: "unneeded_field_pattern",
+ group: "restriction",
+ desc: "struct fields bound to a wildcard instead of using `..`",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "unneeded_wildcard_pattern",
+ group: "complexity",
+ desc: "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "unreachable",
+ group: "restriction",
+ desc: "`unreachable!` should not be present in production code",
+ deprecation: None,
+ module: "panic_unimplemented",
+ },
+ Lint {
+ name: "unreadable_literal",
+ group: "pedantic",
+ desc: "long integer literal without underscores",
+ deprecation: None,
+ module: "literal_representation",
+ },
+ Lint {
+ name: "unsafe_derive_deserialize",
+ group: "pedantic",
+ desc: "deriving `serde::Deserialize` on a type that has methods using `unsafe`",
+ deprecation: None,
+ module: "derive",
+ },
+ Lint {
+ name: "unsafe_removed_from_name",
+ group: "style",
+ desc: "`unsafe` removed from API names on import",
+ deprecation: None,
+ module: "unsafe_removed_from_name",
+ },
+ Lint {
+ name: "unseparated_literal_suffix",
+ group: "pedantic",
+ desc: "literals whose suffix is not separated by an underscore",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "unsound_collection_transmute",
+ group: "correctness",
+ desc: "transmute between collections of layout-incompatible types",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "unused_io_amount",
+ group: "correctness",
+ desc: "unused written/read amount",
+ deprecation: None,
+ module: "unused_io_amount",
+ },
+ Lint {
+ name: "unused_self",
+ group: "pedantic",
+ desc: "methods that contain a `self` argument but don\'t use it",
+ deprecation: None,
+ module: "unused_self",
+ },
+ Lint {
+ name: "unused_unit",
+ group: "style",
+ desc: "needless unit expression",
+ deprecation: None,
+ module: "returns",
+ },
+ Lint {
+ name: "use_debug",
+ group: "restriction",
+ desc: "use of `Debug`-based formatting",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "use_self",
+ group: "nursery",
+ desc: "Unnecessary structure name repetition whereas `Self` is applicable",
+ deprecation: None,
+ module: "use_self",
+ },
+ Lint {
+ name: "used_underscore_binding",
+ group: "pedantic",
+ desc: "using a binding which is prefixed with an underscore",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "useless_asref",
+ group: "complexity",
+ desc: "using `as_ref` where the types before and after the call are the same",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "useless_attribute",
+ group: "correctness",
+ desc: "use of lint attributes on `extern crate` items",
+ deprecation: None,
+ module: "attrs",
+ },
+ Lint {
+ name: "useless_format",
+ group: "complexity",
+ desc: "useless use of `format!`",
+ deprecation: None,
+ module: "format",
+ },
+ Lint {
+ name: "useless_let_if_seq",
+ group: "style",
+ desc: "unidiomatic `let mut` declaration followed by initialization in `if`",
+ deprecation: None,
+ module: "let_if_seq",
+ },
+ Lint {
+ name: "useless_transmute",
+ group: "nursery",
+ desc: "transmutes that have the same to and from types or could be a cast/coercion",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "useless_vec",
+ group: "perf",
+ desc: "useless `vec!`",
+ deprecation: None,
+ module: "vec",
+ },
+ Lint {
+ name: "vec_box",
+ group: "complexity",
+ desc: "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap",
+ deprecation: None,
+ module: "types",
+ },
+ Lint {
+ name: "verbose_bit_mask",
+ group: "style",
+ desc: "expressions where a bit mask is less readable than the corresponding method call",
+ deprecation: None,
+ module: "bit_mask",
+ },
+ Lint {
+ name: "verbose_file_reads",
+ group: "restriction",
+ desc: "use of `File::read_to_end` or `File::read_to_string`",
+ deprecation: None,
+ module: "verbose_file_reads",
+ },
+ Lint {
+ name: "vtable_address_comparisons",
+ group: "correctness",
+ desc: "comparison with an address of a trait vtable",
+ deprecation: None,
+ module: "unnamed_address",
+ },
+ Lint {
+ name: "while_immutable_condition",
+ group: "correctness",
+ desc: "variables used within while expression are not mutated in the body",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "while_let_loop",
+ group: "complexity",
+ desc: "`loop { if let { ... } else break }`, which can be written as a `while let` loop",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "while_let_on_iterator",
+ group: "style",
+ desc: "using a while-let loop instead of a for loop on an iterator",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
+ name: "wildcard_dependencies",
+ group: "cargo",
+ desc: "wildcard dependencies being used",
+ deprecation: None,
+ module: "wildcard_dependencies",
+ },
+ Lint {
+ name: "wildcard_enum_match_arm",
+ group: "restriction",
+ desc: "a wildcard enum match arm using `_`",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "wildcard_imports",
+ group: "pedantic",
+ desc: "lint `use _::*` statements",
+ deprecation: None,
+ module: "wildcard_imports",
+ },
+ Lint {
+ name: "wildcard_in_or_patterns",
+ group: "complexity",
+ desc: "a wildcard pattern used with others patterns in same match arm",
+ deprecation: None,
+ module: "matches",
+ },
+ Lint {
+ name: "write_literal",
+ group: "style",
+ desc: "writing a literal with a format string",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "write_with_newline",
+ group: "style",
+ desc: "using `write!()` with a format string that ends in a single newline",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "writeln_empty_string",
+ group: "style",
+ desc: "using `writeln!(buf, \"\")` with an empty string",
+ deprecation: None,
+ module: "write",
+ },
+ Lint {
+ name: "wrong_pub_self_convention",
+ group: "restriction",
+ desc: "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "wrong_self_convention",
+ group: "style",
+ desc: "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
+ name: "wrong_transmute",
+ group: "correctness",
+ desc: "transmutes that are confusing at best, undefined behaviour at worst and always useless",
+ deprecation: None,
+ module: "transmute",
+ },
+ Lint {
+ name: "zero_divided_by_zero",
+ group: "complexity",
+ desc: "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`",
+ deprecation: None,
+ module: "zero_div_zero",
+ },
+ Lint {
+ name: "zero_prefixed_literal",
+ group: "complexity",
+ desc: "integer literals starting with `0`",
+ deprecation: None,
+ module: "misc_early",
+ },
+ Lint {
+ name: "zero_ptr",
+ group: "style",
+ desc: "using `0 as *{const, mut} T`",
+ deprecation: None,
+ module: "misc",
+ },
+ Lint {
+ name: "zero_width_space",
+ group: "correctness",
+ desc: "using a zero-width space in a string literal, which is confusing",
+ deprecation: None,
+ module: "unicode",
+ },
+ Lint {
+ name: "zst_offset",
+ group: "correctness",
+ desc: "Check for offset calculations on raw pointers to zero-sized types",
+ deprecation: None,
+ module: "methods",
+ },
+];
+// end lint list, do not remove this comment, it’s used in `update_lints`
+}
--- /dev/null
- error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
+
+error: aborting due to previous error
+
--- /dev/null
- = help: type parameters must be constrained to match other types
- = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
+error: This generic shadows the built-in type `u32`
+ --> $DIR/builtin-type-shadow.rs:4:8
+ |
+LL | fn foo<u32>(a: u32) -> u32 {
+ | ^^^
+ |
+ = note: `-D clippy::builtin-type-shadow` implied by `-D warnings`
+
+error[E0308]: mismatched types
+ --> $DIR/builtin-type-shadow.rs:5:5
+ |
+LL | fn foo<u32>(a: u32) -> u32 {
+ | --- --- expected `u32` because of return type
+ | |
+ | this type parameter
+LL | 42
+ | ^^ expected type parameter `u32`, found integer
+ |
+ = note: expected type parameter `u32`
+ found type `{integer}`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+#![allow(clippy::if_same_then_else)]
+
+macro_rules! m {
+ ($a:expr) => {
+ if $a.is_some() {
+ $a.unwrap(); // unnecessary
+ }
+ };
+}
+
++macro_rules! checks_in_param {
++ ($a:expr, $b:expr) => {
++ if $a {
++ $b;
++ }
++ };
++}
++
++macro_rules! checks_unwrap {
++ ($a:expr, $b:expr) => {
++ if $a.is_some() {
++ $b;
++ }
++ };
++}
++
++macro_rules! checks_some {
++ ($a:expr, $b:expr) => {
++ if $a {
++ $b.unwrap();
++ }
++ };
++}
++
+fn main() {
+ let x = Some(());
+ if x.is_some() {
+ x.unwrap(); // unnecessary
+ } else {
+ x.unwrap(); // will panic
+ }
+ if x.is_none() {
+ x.unwrap(); // will panic
+ } else {
+ x.unwrap(); // unnecessary
+ }
+ m!(x);
++ checks_in_param!(x.is_some(), x.unwrap()); // ok
++ checks_unwrap!(x, x.unwrap()); // ok
++ checks_some!(x.is_some(), x); // ok
+ let mut x: Result<(), ()> = Ok(());
+ if x.is_ok() {
+ x.unwrap(); // unnecessary
+ x.unwrap_err(); // will panic
+ } else {
+ x.unwrap(); // will panic
+ x.unwrap_err(); // unnecessary
+ }
+ if x.is_err() {
+ x.unwrap(); // will panic
+ x.unwrap_err(); // unnecessary
+ } else {
+ x.unwrap(); // unnecessary
+ x.unwrap_err(); // will panic
+ }
+ if x.is_ok() {
+ x = Err(());
+ x.unwrap(); // not unnecessary because of mutation of x
+ // it will always panic but the lint is not smart enough to see this (it only
+ // checks if conditions).
+ } else {
+ x = Ok(());
+ x.unwrap_err(); // not unnecessary because of mutation of x
+ // it will always panic but the lint is not smart enough to see this (it
+ // only checks if conditions).
+ }
+
+ assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern
+}
--- /dev/null
- --> $DIR/simple_conditionals.rs:15:9
+error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
- --> $DIR/simple_conditionals.rs:17:9
++ --> $DIR/simple_conditionals.rs:39:9
+ |
+LL | if x.is_some() {
+ | ----------- the check is happening here
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/simple_conditionals.rs:1:35
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: This call to `unwrap()` will always panic.
- --> $DIR/simple_conditionals.rs:20:9
++ --> $DIR/simple_conditionals.rs:41:9
+ |
+LL | if x.is_some() {
+ | ----------- because of this check
+...
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/simple_conditionals.rs:1:9
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: This call to `unwrap()` will always panic.
- --> $DIR/simple_conditionals.rs:22:9
++ --> $DIR/simple_conditionals.rs:44:9
+ |
+LL | if x.is_none() {
+ | ----------- because of this check
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
- --> $DIR/simple_conditionals.rs:27:9
++ --> $DIR/simple_conditionals.rs:46:9
+ |
+LL | if x.is_none() {
+ | ----------- the check is happening here
+...
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+
+error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+ --> $DIR/simple_conditionals.rs:7:13
+ |
+LL | if $a.is_some() {
+ | ------------ the check is happening here
+LL | $a.unwrap(); // unnecessary
+ | ^^^^^^^^^^^
+...
+LL | m!(x);
+ | ------ in this macro invocation
+ |
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
- --> $DIR/simple_conditionals.rs:28:9
++ --> $DIR/simple_conditionals.rs:54:9
+ |
+LL | if x.is_ok() {
+ | --------- the check is happening here
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+
+error: This call to `unwrap_err()` will always panic.
- --> $DIR/simple_conditionals.rs:30:9
++ --> $DIR/simple_conditionals.rs:55:9
+ |
+LL | if x.is_ok() {
+ | --------- because of this check
+LL | x.unwrap(); // unnecessary
+LL | x.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+
+error: This call to `unwrap()` will always panic.
- --> $DIR/simple_conditionals.rs:31:9
++ --> $DIR/simple_conditionals.rs:57:9
+ |
+LL | if x.is_ok() {
+ | --------- because of this check
+...
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
- --> $DIR/simple_conditionals.rs:34:9
++ --> $DIR/simple_conditionals.rs:58:9
+ |
+LL | if x.is_ok() {
+ | --------- the check is happening here
+...
+LL | x.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+
+error: This call to `unwrap()` will always panic.
- --> $DIR/simple_conditionals.rs:35:9
++ --> $DIR/simple_conditionals.rs:61:9
+ |
+LL | if x.is_err() {
+ | ---------- because of this check
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
- --> $DIR/simple_conditionals.rs:37:9
++ --> $DIR/simple_conditionals.rs:62:9
+ |
+LL | if x.is_err() {
+ | ---------- the check is happening here
+LL | x.unwrap(); // will panic
+LL | x.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+
+error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
- --> $DIR/simple_conditionals.rs:38:9
++ --> $DIR/simple_conditionals.rs:64:9
+ |
+LL | if x.is_err() {
+ | ---------- the check is happening here
+...
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+
+error: This call to `unwrap_err()` will always panic.
++ --> $DIR/simple_conditionals.rs:65:9
+ |
+LL | if x.is_err() {
+ | ---------- because of this check
+...
+LL | x.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 13 previous errors
+
--- /dev/null
+// edition:2018
+#![warn(clippy::future_not_send)]
+
+use std::cell::Cell;
+use std::rc::Rc;
+use std::sync::Arc;
+
+async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ async { true }.await
+}
+
+pub async fn public_future(rc: Rc<[u8]>) {
+ async { true }.await;
+}
+
+pub async fn public_send(arc: Arc<[u8]>) -> bool {
+ async { false }.await
+}
+
+async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ true
+}
+
+pub async fn public_future2(rc: Rc<[u8]>) {}
+
+pub async fn public_send2(arc: Arc<[u8]>) -> bool {
+ false
+}
+
+struct Dummy {
+ rc: Rc<[u8]>,
+}
+
+impl Dummy {
+ async fn private_future(&self) -> usize {
+ async { true }.await;
+ self.rc.len()
+ }
+
+ pub async fn public_future(&self) {
+ self.private_future().await;
+ }
+
++ #[allow(clippy::manual_async_fn)]
+ pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
+ async { false }
+ }
+}
+
+async fn generic_future<T>(t: T) -> T
+where
+ T: Send,
+{
+ let rt = &t;
+ async { true }.await;
+ t
+}
+
+async fn generic_future_send<T>(t: T)
+where
+ T: Send,
+{
+ async { true }.await;
+}
+
+async fn unclear_future<T>(t: T) {}
+
+fn main() {
+ let rc = Rc::new([1, 2, 3]);
+ private_future(rc.clone(), &Cell::new(42));
+ public_future(rc.clone());
+ let arc = Arc::new([4, 5, 6]);
+ public_send(arc);
+ generic_future(42);
+ generic_future_send(42);
+
+ let dummy = Dummy { rc };
+ dummy.public_future();
+ dummy.public_send();
+}
--- /dev/null
- --> $DIR/future_not_send.rs:49:37
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:8:62
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^ future returned by `private_future` is not `Send`
+ |
+ = note: `-D clippy::future-not-send` implied by `-D warnings`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:9:5
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
+LL | async { true }.await
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
+LL | }
+ | - `rc` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:9:5
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ---- has type `&std::cell::Cell<usize>` which is not `Send`
+LL | async { true }.await
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `cell` maybe used later
+LL | }
+ | - `cell` is later dropped here
+ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:12:42
+ |
+LL | pub async fn public_future(rc: Rc<[u8]>) {
+ | ^ future returned by `public_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:13:5
+ |
+LL | pub async fn public_future(rc: Rc<[u8]>) {
+ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
+LL | }
+ | - `rc` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:20:63
+ |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^
+ |
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:24:43
+ |
+LL | pub async fn public_future2(rc: Rc<[u8]>) {}
+ | ^
+ |
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:35:39
+ |
+LL | async fn private_future(&self) -> usize {
+ | ^^^^^ future returned by `private_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:36:9
+ |
+LL | async fn private_future(&self) -> usize {
+ | ----- has type `&Dummy` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
+LL | self.rc.len()
+LL | }
+ | - `&self` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:40:39
+ |
+LL | pub async fn public_future(&self) {
+ | ^ future returned by `public_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:41:9
+ |
+LL | pub async fn public_future(&self) {
+ | ----- has type `&Dummy` which is not `Send`
+LL | self.private_future().await;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
+LL | }
+ | - `&self` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:54:5
++ --> $DIR/future_not_send.rs:50:37
+ |
+LL | async fn generic_future<T>(t: T) -> T
+ | ^ future returned by `generic_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:65:34
++ --> $DIR/future_not_send.rs:55:5
+ |
+LL | let rt = &t;
+ | -- has type `&T` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rt` maybe used later
+LL | t
+LL | }
+ | - `rt` is later dropped here
+ = note: `T` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:66:34
+ |
+LL | async fn unclear_future<T>(t: T) {}
+ | ^
+ |
+ = note: `T` doesn't implement `std::marker::Send`
+
+error: aborting due to 8 previous errors
+
--- /dev/null
--- /dev/null
++// run-rustfix
++// edition:2018
++#![warn(clippy::manual_async_fn)]
++#![allow(unused)]
++
++use std::future::Future;
++
++async fn fut() -> i32 { 42 }
++
++async fn empty_fut() {}
++
++async fn core_fut() -> i32 { 42 }
++
++// should be ignored
++fn has_other_stmts() -> impl core::future::Future<Output = i32> {
++ let _ = 42;
++ async move { 42 }
++}
++
++// should be ignored
++fn not_fut() -> i32 {
++ 42
++}
++
++// should be ignored
++async fn already_async() -> impl Future<Output = i32> {
++ async { 42 }
++}
++
++struct S {}
++impl S {
++ async fn inh_fut() -> i32 {
++ // NOTE: this code is here just to check that the identation is correct in the suggested fix
++ let a = 42;
++ let b = 21;
++ if a < b {
++ let c = 21;
++ let d = 42;
++ if c < d {
++ let _ = 42;
++ }
++ }
++ 42
++ }
++
++ async fn meth_fut(&self) -> i32 { 42 }
++
++ async fn empty_fut(&self) {}
++
++ // should be ignored
++ fn not_fut(&self) -> i32 {
++ 42
++ }
++
++ // should be ignored
++ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
++ let _ = 42;
++ async move { 42 }
++ }
++
++ // should be ignored
++ async fn already_async(&self) -> impl Future<Output = i32> {
++ async { 42 }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++// edition:2018
++#![warn(clippy::manual_async_fn)]
++#![allow(unused)]
++
++use std::future::Future;
++
++fn fut() -> impl Future<Output = i32> {
++ async { 42 }
++}
++
++fn empty_fut() -> impl Future<Output = ()> {
++ async {}
++}
++
++fn core_fut() -> impl core::future::Future<Output = i32> {
++ async move { 42 }
++}
++
++// should be ignored
++fn has_other_stmts() -> impl core::future::Future<Output = i32> {
++ let _ = 42;
++ async move { 42 }
++}
++
++// should be ignored
++fn not_fut() -> i32 {
++ 42
++}
++
++// should be ignored
++async fn already_async() -> impl Future<Output = i32> {
++ async { 42 }
++}
++
++struct S {}
++impl S {
++ fn inh_fut() -> impl Future<Output = i32> {
++ async {
++ // NOTE: this code is here just to check that the identation is correct in the suggested fix
++ let a = 42;
++ let b = 21;
++ if a < b {
++ let c = 21;
++ let d = 42;
++ if c < d {
++ let _ = 42;
++ }
++ }
++ 42
++ }
++ }
++
++ fn meth_fut(&self) -> impl Future<Output = i32> {
++ async { 42 }
++ }
++
++ fn empty_fut(&self) -> impl Future<Output = ()> {
++ async {}
++ }
++
++ // should be ignored
++ fn not_fut(&self) -> i32 {
++ 42
++ }
++
++ // should be ignored
++ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
++ let _ = 42;
++ async move { 42 }
++ }
++
++ // should be ignored
++ async fn already_async(&self) -> impl Future<Output = i32> {
++ async { 42 }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this function can be simplified using the `async fn` syntax
++ --> $DIR/manual_async_fn.rs:8:1
++ |
++LL | fn fut() -> impl Future<Output = i32> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::manual-async-fn` implied by `-D warnings`
++help: make the function `async` and return the output of the future directly
++ |
++LL | async fn fut() -> i32 {
++ | ^^^^^^^^^^^^^^^^^^^^^
++help: move the body of the async block to the enclosing function
++ |
++LL | fn fut() -> impl Future<Output = i32> { 42 }
++ | ^^^^^^
++
++error: this function can be simplified using the `async fn` syntax
++ --> $DIR/manual_async_fn.rs:12:1
++ |
++LL | fn empty_fut() -> impl Future<Output = ()> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: make the function `async` and remove the return type
++ |
++LL | async fn empty_fut() {
++ | ^^^^^^^^^^^^^^^^^^^^
++help: move the body of the async block to the enclosing function
++ |
++LL | fn empty_fut() -> impl Future<Output = ()> {}
++ | ^^
++
++error: this function can be simplified using the `async fn` syntax
++ --> $DIR/manual_async_fn.rs:16:1
++ |
++LL | fn core_fut() -> impl core::future::Future<Output = i32> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: make the function `async` and return the output of the future directly
++ |
++LL | async fn core_fut() -> i32 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++help: move the body of the async block to the enclosing function
++ |
++LL | fn core_fut() -> impl core::future::Future<Output = i32> { 42 }
++ | ^^^^^^
++
++error: this function can be simplified using the `async fn` syntax
++ --> $DIR/manual_async_fn.rs:38:5
++ |
++LL | fn inh_fut() -> impl Future<Output = i32> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: make the function `async` and return the output of the future directly
++ |
++LL | async fn inh_fut() -> i32 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++help: move the body of the async block to the enclosing function
++ |
++LL | fn inh_fut() -> impl Future<Output = i32> {
++LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix
++LL | let a = 42;
++LL | let b = 21;
++LL | if a < b {
++LL | let c = 21;
++ ...
++
++error: this function can be simplified using the `async fn` syntax
++ --> $DIR/manual_async_fn.rs:54:5
++ |
++LL | fn meth_fut(&self) -> impl Future<Output = i32> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: make the function `async` and return the output of the future directly
++ |
++LL | async fn meth_fut(&self) -> i32 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++help: move the body of the async block to the enclosing function
++ |
++LL | fn meth_fut(&self) -> impl Future<Output = i32> { 42 }
++ | ^^^^^^
++
++error: this function can be simplified using the `async fn` syntax
++ --> $DIR/manual_async_fn.rs:58:5
++ |
++LL | fn empty_fut(&self) -> impl Future<Output = ()> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: make the function `async` and remove the return type
++ |
++LL | async fn empty_fut(&self) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++help: move the body of the async block to the enclosing function
++ |
++LL | fn empty_fut(&self) -> impl Future<Output = ()> {}
++ | ^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
+#![warn(clippy::needless_range_loop, clippy::manual_memcpy)]
+
+const LOOP_OFFSET: usize = 5000;
+
+pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
+ // plain manual memcpy
+ for i in 0..src.len() {
+ dst[i] = src[i];
+ }
+
+ // dst offset memcpy
+ for i in 0..src.len() {
+ dst[i + 10] = src[i];
+ }
+
+ // src offset memcpy
+ for i in 0..src.len() {
+ dst[i] = src[i + 10];
+ }
+
+ // src offset memcpy
+ for i in 11..src.len() {
+ dst[i] = src[i - 10];
+ }
+
+ // overwrite entire dst
+ for i in 0..dst.len() {
+ dst[i] = src[i];
+ }
+
+ // manual copy with branch - can't easily convert to memcpy!
+ for i in 0..src.len() {
+ dst[i] = src[i];
+ if dst[i] > 5 {
+ break;
+ }
+ }
+
+ // multiple copies - suggest two memcpy statements
+ for i in 10..256 {
+ dst[i] = src[i - 5];
+ dst2[i + 500] = src[i]
+ }
+
+ // this is a reversal - the copy lint shouldn't be triggered
+ for i in 10..LOOP_OFFSET {
+ dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i];
+ }
+
+ let some_var = 5;
+ // Offset in variable
+ for i in 10..LOOP_OFFSET {
+ dst[i + LOOP_OFFSET] = src[i - some_var];
+ }
+
+ // Non continuous copy - don't trigger lint
+ for i in 0..10 {
+ dst[i + i] = src[i];
+ }
+
+ let src_vec = vec![1, 2, 3, 4, 5];
+ let mut dst_vec = vec![0, 0, 0, 0, 0];
+
+ // make sure vectors are supported
+ for i in 0..src_vec.len() {
+ dst_vec[i] = src_vec[i];
+ }
+
+ // lint should not trigger when either
+ // source or destination type is not
+ // slice-like, like DummyStruct
+ struct DummyStruct(i32);
+
+ impl ::std::ops::Index<usize> for DummyStruct {
+ type Output = i32;
+
+ fn index(&self, _: usize) -> &i32 {
+ &self.0
+ }
+ }
+
+ let src = DummyStruct(5);
+ let mut dst_vec = vec![0; 10];
+
+ for i in 0..10 {
+ dst_vec[i] = src[i];
+ }
+
+ // Simplify suggestion (issue #3004)
+ let src = [0, 1, 2, 3, 4];
+ let mut dst = [0, 0, 0, 0, 0, 0];
+ let from = 1;
+
+ for i in from..from + src.len() {
+ dst[i] = src[i - from];
+ }
+
+ for i in from..from + 3 {
+ dst[i] = src[i - from];
+ }
++
++ #[allow(clippy::identity_op)]
++ for i in 0..5 {
++ dst[i - 0] = src[i];
++ }
++
++ #[allow(clippy::reverse_range_loop)]
++ for i in 0..0 {
++ dst[i] = src[i];
++ }
++
++ // `RangeTo` `for` loop - don't trigger lint
++ for i in 0.. {
++ dst[i] = src[i];
++ }
+}
+
+#[warn(clippy::needless_range_loop, clippy::manual_memcpy)]
+pub fn manual_clone(src: &[String], dst: &mut [String]) {
+ for i in 0..src.len() {
+ dst[i] = src[i].clone();
+ }
+}
+
+fn main() {}
--- /dev/null
- | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])`
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:7:14
+ |
+LL | for i in 0..src.len() {
+ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`
+ |
+ = note: `-D clippy::manual-memcpy` implied by `-D warnings`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:12:14
+ |
+LL | for i in 0..src.len() {
+ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:17:14
+ |
+LL | for i in 0..src.len() {
+ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:22:14
+ |
+LL | for i in 11..src.len() {
+ | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:27:14
+ |
+LL | for i in 0..dst.len() {
+ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:40:14
+ |
+LL | for i in 10..256 {
+ | ^^^^^^^
+ |
+help: try replacing the loop by
+ |
+LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)])
+LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) {
+ |
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:52:14
+ |
+LL | for i in 10..LOOP_OFFSET {
+ | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:65:14
+ |
+LL | for i in 0..src_vec.len() {
+ | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:94:14
+ |
+LL | for i in from..from + src.len() {
- | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])`
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/manual_memcpy.rs:98:14
+ |
+LL | for i in from..from + 3 {
- --> $DIR/manual_memcpy.rs:105:14
++ | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])`
+
+error: it looks like you're manually copying between slices
- error: aborting due to 11 previous errors
++ --> $DIR/manual_memcpy.rs:103:14
++ |
++LL | for i in 0..5 {
++ | ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:108:14
++ |
++LL | for i in 0..0 {
++ | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:120:14
+ |
+LL | for i in 0..src.len() {
+ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`
+
++error: aborting due to 13 previous errors
+
--- /dev/null
--- /dev/null
++#![warn(clippy::manual_non_exhaustive)]
++#![allow(unused)]
++
++mod enums {
++ enum E {
++ A,
++ B,
++ #[doc(hidden)]
++ _C,
++ }
++
++ // user forgot to remove the marker
++ #[non_exhaustive]
++ enum Ep {
++ A,
++ B,
++ #[doc(hidden)]
++ _C,
++ }
++
++ // marker variant does not have doc hidden attribute, should be ignored
++ enum NoDocHidden {
++ A,
++ B,
++ _C,
++ }
++
++ // name of variant with doc hidden does not start with underscore, should be ignored
++ enum NoUnderscore {
++ A,
++ B,
++ #[doc(hidden)]
++ C,
++ }
++
++ // variant with doc hidden is not unit, should be ignored
++ enum NotUnit {
++ A,
++ B,
++ #[doc(hidden)]
++ _C(bool),
++ }
++
++ // variant with doc hidden is the only one, should be ignored
++ enum OnlyMarker {
++ #[doc(hidden)]
++ _A,
++ }
++
++ // variant with multiple markers, should be ignored
++ enum MultipleMarkers {
++ A,
++ #[doc(hidden)]
++ _B,
++ #[doc(hidden)]
++ _C,
++ }
++
++ // already non_exhaustive and no markers, should be ignored
++ #[non_exhaustive]
++ enum NonExhaustive {
++ A,
++ B,
++ }
++}
++
++mod structs {
++ struct S {
++ pub a: i32,
++ pub b: i32,
++ _c: (),
++ }
++
++ // user forgot to remove the private field
++ #[non_exhaustive]
++ struct Sp {
++ pub a: i32,
++ pub b: i32,
++ _c: (),
++ }
++
++ // some other fields are private, should be ignored
++ struct PrivateFields {
++ a: i32,
++ pub b: i32,
++ _c: (),
++ }
++
++ // private field name does not start with underscore, should be ignored
++ struct NoUnderscore {
++ pub a: i32,
++ pub b: i32,
++ c: (),
++ }
++
++ // private field is not unit type, should be ignored
++ struct NotUnit {
++ pub a: i32,
++ pub b: i32,
++ _c: i32,
++ }
++
++ // private field is the only field, should be ignored
++ struct OnlyMarker {
++ _a: (),
++ }
++
++ // already non exhaustive and no private fields, should be ignored
++ #[non_exhaustive]
++ struct NonExhaustive {
++ pub a: i32,
++ pub b: i32,
++ }
++}
++
++mod tuple_structs {
++ struct T(pub i32, pub i32, ());
++
++ // user forgot to remove the private field
++ #[non_exhaustive]
++ struct Tp(pub i32, pub i32, ());
++
++ // some other fields are private, should be ignored
++ struct PrivateFields(pub i32, i32, ());
++
++ // private field is not unit type, should be ignored
++ struct NotUnit(pub i32, pub i32, i32);
++
++ // private field is the only field, should be ignored
++ struct OnlyMarker(());
++
++ // already non exhaustive and no private fields, should be ignored
++ #[non_exhaustive]
++ struct NonExhaustive(pub i32, pub i32);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this seems like a manual implementation of the non-exhaustive pattern
++ --> $DIR/manual_non_exhaustive.rs:5:5
++ |
++LL | enum E {
++ | ^-----
++ | |
++ | _____help: add the attribute: `#[non_exhaustive] enum E`
++ | |
++LL | | A,
++LL | | B,
++LL | | #[doc(hidden)]
++LL | | _C,
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
++help: remove this variant
++ --> $DIR/manual_non_exhaustive.rs:9:9
++ |
++LL | _C,
++ | ^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++ --> $DIR/manual_non_exhaustive.rs:14:5
++ |
++LL | / enum Ep {
++LL | | A,
++LL | | B,
++LL | | #[doc(hidden)]
++LL | | _C,
++LL | | }
++ | |_____^
++ |
++help: remove this variant
++ --> $DIR/manual_non_exhaustive.rs:18:9
++ |
++LL | _C,
++ | ^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++ --> $DIR/manual_non_exhaustive.rs:68:5
++ |
++LL | struct S {
++ | ^-------
++ | |
++ | _____help: add the attribute: `#[non_exhaustive] struct S`
++ | |
++LL | | pub a: i32,
++LL | | pub b: i32,
++LL | | _c: (),
++LL | | }
++ | |_____^
++ |
++help: remove this field
++ --> $DIR/manual_non_exhaustive.rs:71:9
++ |
++LL | _c: (),
++ | ^^^^^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++ --> $DIR/manual_non_exhaustive.rs:76:5
++ |
++LL | / struct Sp {
++LL | | pub a: i32,
++LL | | pub b: i32,
++LL | | _c: (),
++LL | | }
++ | |_____^
++ |
++help: remove this field
++ --> $DIR/manual_non_exhaustive.rs:79:9
++ |
++LL | _c: (),
++ | ^^^^^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++ --> $DIR/manual_non_exhaustive.rs:117:5
++ |
++LL | struct T(pub i32, pub i32, ());
++ | --------^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: add the attribute: `#[non_exhaustive] struct T`
++ |
++help: remove this field
++ --> $DIR/manual_non_exhaustive.rs:117:32
++ |
++LL | struct T(pub i32, pub i32, ());
++ | ^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++ --> $DIR/manual_non_exhaustive.rs:121:5
++ |
++LL | struct Tp(pub i32, pub i32, ());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: remove this field
++ --> $DIR/manual_non_exhaustive.rs:121:33
++ |
++LL | struct Tp(pub i32, pub i32, ());
++ | ^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
+#![warn(clippy::match_on_vec_items)]
+
+fn match_with_wildcard() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 1;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => {},
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => {},
+ }
+}
+
+fn match_without_wildcard() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 2;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ num => {},
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ [ref sub @ ..] => {},
+ }
+}
+
+fn match_wildcard_and_action() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => println!("Hello, World!"),
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => println!("Hello, World!"),
+ }
+}
+
+fn match_vec_ref() {
+ let arr = &vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => {},
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => {},
+ }
+}
+
+fn match_with_get() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Ok
+ match arr.get(idx) {
+ Some(0) => println!("0"),
+ Some(1) => println!("1"),
+ _ => {},
+ }
+
+ // Ok
+ match arr.get(range) {
+ Some(&[0, 1]) => println!("0 1"),
+ Some(&[1, 2]) => println!("1 2"),
+ _ => {},
+ }
+}
+
+fn match_with_array() {
+ let arr = [0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Ok
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => {},
+ }
+
+ // Ok
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => {},
+ }
+}
+
++fn match_with_endless_range() {
++ let arr = vec![0, 1, 2, 3];
++ let range = ..;
++
++ // Ok
++ match arr[range] {
++ [0, 1] => println!("0 1"),
++ [1, 2] => println!("1 2"),
++ [0, 1, 2, 3] => println!("0, 1, 2, 3"),
++ _ => {},
++ }
++
++ // Ok
++ match arr[..] {
++ [0, 1] => println!("0 1"),
++ [1, 2] => println!("1 2"),
++ [0, 1, 2, 3] => println!("0, 1, 2, 3"),
++ _ => {},
++ }
++}
++
+fn main() {
+ match_with_wildcard();
+ match_without_wildcard();
+ match_wildcard_and_action();
+ match_vec_ref();
+ match_with_get();
+ match_with_array();
++ match_with_endless_range();
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::while_let_on_iterator)]
+#![allow(clippy::never_loop, unreachable_code, unused_mut)]
++#![feature(or_patterns)]
+
+fn base() {
+ let mut iter = 1..20;
+ for x in iter {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ for x in iter {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ for _ in iter {}
+
+ let mut iter = 1..20;
+ while let None = iter.next() {} // this is fine (if nonsensical)
+
+ let mut iter = 1..20;
+ if let Some(x) = iter.next() {
+ // also fine
+ println!("{}", x)
+ }
+
+ // the following shouldn't warn because it can't be written with a for loop
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next())
+ }
+
+ // neither can this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next());
+ }
+
+ // or this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ break;
+ }
+ println!("Remaining iter {:?}", iter);
+
+ // or this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ iter = 1..20;
+ }
+}
+
+// Issue #1188
+fn refutable() {
+ let a = [42, 1337];
+ let mut b = a.iter();
+
+ // consume all the 42s
+ while let Some(&42) = b.next() {}
+
+ let a = [(1, 2, 3)];
+ let mut b = a.iter();
+
+ while let Some(&(1, 2, 3)) = b.next() {}
+
+ let a = [Some(42)];
+ let mut b = a.iter();
+
+ while let Some(&None) = b.next() {}
+
+ /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
+ for &42 in b {}
+ for &(1, 2, 3) in b {}
+ for &Option::None in b.next() {}
+ // */
+}
+
++fn refutable2() {
++ // Issue 3780
++ {
++ let v = vec![1, 2, 3];
++ let mut it = v.windows(2);
++ while let Some([x, y]) = it.next() {
++ println!("x: {}", x);
++ println!("y: {}", y);
++ }
++
++ let mut it = v.windows(2);
++ while let Some([x, ..]) = it.next() {
++ println!("x: {}", x);
++ }
++
++ let mut it = v.windows(2);
++ while let Some([.., y]) = it.next() {
++ println!("y: {}", y);
++ }
++
++ let mut it = v.windows(2);
++ for [..] in it {}
++
++ let v = vec![[1], [2], [3]];
++ let mut it = v.iter();
++ while let Some([1]) = it.next() {}
++
++ let mut it = v.iter();
++ for [_x] in it {}
++ }
++
++ // binding
++ {
++ let v = vec![1, 2, 3];
++ let mut it = v.iter();
++ while let Some(x @ 1) = it.next() {
++ println!("{}", x);
++ }
++
++ let v = vec![[1], [2], [3]];
++ let mut it = v.iter();
++ for x @ [_] in it {
++ println!("{:?}", x);
++ }
++ }
++
++ // false negative
++ {
++ let v = vec![1, 2, 3];
++ let mut it = v.iter().map(Some);
++ while let Some(Some(_) | None) = it.next() {
++ println!("1");
++ }
++ }
++}
++
+fn nested_loops() {
+ let a = [42, 1337];
+ let mut y = a.iter();
+ loop {
+ // x is reused, so don't lint here
+ while let Some(_) = y.next() {}
+ }
+
+ let mut y = a.iter();
+ for _ in 0..2 {
+ while let Some(_) = y.next() {
+ // y is reused, don't lint
+ }
+ }
+
+ loop {
+ let mut y = a.iter();
+ for _ in y {
+ // use a for loop here
+ }
+ }
+}
+
+fn issue1121() {
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(&value) = values.iter().next() {
+ values.remove(&value);
+ }
+}
+
+fn issue2965() {
+ // This should not cause an ICE and suggest:
+ //
+ // for _ in values.iter() {}
+ //
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {}
+}
+
+fn issue3670() {
+ let array = [Some(0), None, Some(1)];
+ let mut iter = array.iter();
+
+ while let Some(elem) = iter.next() {
+ let _ = elem.or_else(|| *iter.next()?);
+ }
+}
+
+fn issue1654() {
+ // should not lint if the iterator is generated on every iteration
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {
+ values.remove(&1);
+ }
+
+ while let Some(..) = values.iter().map(|x| x + 1).next() {}
+
+ let chars = "Hello, World!".char_indices();
+ while let Some((i, ch)) = chars.clone().next() {
+ println!("{}: {}", i, ch);
+ }
+}
+
+fn main() {
+ base();
+ refutable();
++ refutable2();
+ nested_loops();
+ issue1121();
+ issue2965();
+ issue3670();
+ issue1654();
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::while_let_on_iterator)]
+#![allow(clippy::never_loop, unreachable_code, unused_mut)]
++#![feature(or_patterns)]
+
+fn base() {
+ let mut iter = 1..20;
+ while let Option::Some(x) = iter.next() {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ while let Some(x) = iter.next() {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ while let Some(_) = iter.next() {}
+
+ let mut iter = 1..20;
+ while let None = iter.next() {} // this is fine (if nonsensical)
+
+ let mut iter = 1..20;
+ if let Some(x) = iter.next() {
+ // also fine
+ println!("{}", x)
+ }
+
+ // the following shouldn't warn because it can't be written with a for loop
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next())
+ }
+
+ // neither can this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next());
+ }
+
+ // or this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ break;
+ }
+ println!("Remaining iter {:?}", iter);
+
+ // or this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ iter = 1..20;
+ }
+}
+
+// Issue #1188
+fn refutable() {
+ let a = [42, 1337];
+ let mut b = a.iter();
+
+ // consume all the 42s
+ while let Some(&42) = b.next() {}
+
+ let a = [(1, 2, 3)];
+ let mut b = a.iter();
+
+ while let Some(&(1, 2, 3)) = b.next() {}
+
+ let a = [Some(42)];
+ let mut b = a.iter();
+
+ while let Some(&None) = b.next() {}
+
+ /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
+ for &42 in b {}
+ for &(1, 2, 3) in b {}
+ for &Option::None in b.next() {}
+ // */
+}
+
++fn refutable2() {
++ // Issue 3780
++ {
++ let v = vec![1, 2, 3];
++ let mut it = v.windows(2);
++ while let Some([x, y]) = it.next() {
++ println!("x: {}", x);
++ println!("y: {}", y);
++ }
++
++ let mut it = v.windows(2);
++ while let Some([x, ..]) = it.next() {
++ println!("x: {}", x);
++ }
++
++ let mut it = v.windows(2);
++ while let Some([.., y]) = it.next() {
++ println!("y: {}", y);
++ }
++
++ let mut it = v.windows(2);
++ while let Some([..]) = it.next() {}
++
++ let v = vec![[1], [2], [3]];
++ let mut it = v.iter();
++ while let Some([1]) = it.next() {}
++
++ let mut it = v.iter();
++ while let Some([_x]) = it.next() {}
++ }
++
++ // binding
++ {
++ let v = vec![1, 2, 3];
++ let mut it = v.iter();
++ while let Some(x @ 1) = it.next() {
++ println!("{}", x);
++ }
++
++ let v = vec![[1], [2], [3]];
++ let mut it = v.iter();
++ while let Some(x @ [_]) = it.next() {
++ println!("{:?}", x);
++ }
++ }
++
++ // false negative
++ {
++ let v = vec![1, 2, 3];
++ let mut it = v.iter().map(Some);
++ while let Some(Some(_) | None) = it.next() {
++ println!("1");
++ }
++ }
++}
++
+fn nested_loops() {
+ let a = [42, 1337];
+ let mut y = a.iter();
+ loop {
+ // x is reused, so don't lint here
+ while let Some(_) = y.next() {}
+ }
+
+ let mut y = a.iter();
+ for _ in 0..2 {
+ while let Some(_) = y.next() {
+ // y is reused, don't lint
+ }
+ }
+
+ loop {
+ let mut y = a.iter();
+ while let Some(_) = y.next() {
+ // use a for loop here
+ }
+ }
+}
+
+fn issue1121() {
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(&value) = values.iter().next() {
+ values.remove(&value);
+ }
+}
+
+fn issue2965() {
+ // This should not cause an ICE and suggest:
+ //
+ // for _ in values.iter() {}
+ //
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {}
+}
+
+fn issue3670() {
+ let array = [Some(0), None, Some(1)];
+ let mut iter = array.iter();
+
+ while let Some(elem) = iter.next() {
+ let _ = elem.or_else(|| *iter.next()?);
+ }
+}
+
+fn issue1654() {
+ // should not lint if the iterator is generated on every iteration
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {
+ values.remove(&1);
+ }
+
+ while let Some(..) = values.iter().map(|x| x + 1).next() {}
+
+ let chars = "Hello, World!".char_indices();
+ while let Some((i, ch)) = chars.clone().next() {
+ println!("{}: {}", i, ch);
+ }
+}
+
+fn main() {
+ base();
+ refutable();
++ refutable2();
+ nested_loops();
+ issue1121();
+ issue2965();
+ issue3670();
+ issue1654();
+}
--- /dev/null
- --> $DIR/while_let_on_iterator.rs:8:5
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:13:5
++ --> $DIR/while_let_on_iterator.rs:9:5
+ |
+LL | while let Option::Some(x) = iter.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
+ |
+ = note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:18:5
++ --> $DIR/while_let_on_iterator.rs:14:5
+ |
+LL | while let Some(x) = iter.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:97:9
++ --> $DIR/while_let_on_iterator.rs:19:5
+ |
+LL | while let Some(_) = iter.next() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
+
+error: this loop could be written as a `for` loop
- error: aborting due to 4 previous errors
++ --> $DIR/while_let_on_iterator.rs:102:9
++ |
++LL | while let Some([..]) = it.next() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
++
++error: this loop could be written as a `for` loop
++ --> $DIR/while_let_on_iterator.rs:109:9
++ |
++LL | while let Some([_x]) = it.next() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
++
++error: this loop could be written as a `for` loop
++ --> $DIR/while_let_on_iterator.rs:122:9
++ |
++LL | while let Some(x @ [_]) = it.next() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
++
++error: this loop could be written as a `for` loop
++ --> $DIR/while_let_on_iterator.rs:154:9
+ |
+LL | while let Some(_) = y.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
+
++error: aborting due to 7 previous errors
+
--- /dev/null
+// run-rustfix
+// aux-build:wildcard_imports_helper.rs
+
+#![warn(clippy::wildcard_imports)]
+//#![allow(clippy::redundant_pub_crate)]
+#![allow(unused)]
+#![warn(unused_imports)]
+
+extern crate wildcard_imports_helper;
+
+use crate::fn_mod::foo;
+use crate::mod_mod::inner_mod;
+use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
+#[macro_use]
+use crate::struct_mod::{A, inner_struct_mod};
+
+#[allow(unused_imports)]
+use wildcard_imports_helper::inner::inner_for_self_import;
+use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
+use wildcard_imports_helper::{ExternA, extern_foo};
+
+use std::io::prelude::*;
+
+struct ReadFoo;
+
+impl Read for ReadFoo {
+ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
+ Ok(0)
+ }
+}
+
+mod fn_mod {
+ pub fn foo() {}
+}
+
+mod mod_mod {
+ pub mod inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod multi_fn_mod {
+ pub fn multi_foo() {}
+ pub fn multi_bar() {}
+ pub fn multi_baz() {}
+ pub mod multi_inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod struct_mod {
+ pub struct A;
+ pub struct B;
+ pub mod inner_struct_mod {
+ pub struct C;
+ }
+
+ #[macro_export]
+ macro_rules! double_struct_import_test {
+ () => {
+ let _ = A;
+ };
+ }
+}
+
+fn main() {
+ foo();
+ multi_foo();
+ multi_bar();
+ multi_inner_mod::foo();
+ inner_mod::foo();
+ extern_foo();
+ inner_extern_bar();
+
+ let _ = A;
+ let _ = inner_struct_mod::C;
+ let _ = ExternA;
+
+ double_struct_import_test!();
+ double_struct_import_test!();
+}
+
+mod in_fn_test {
+ pub use self::inner_exported::*;
+ #[allow(unused_imports)]
+ pub(crate) use self::inner_exported2::*;
+
+ fn test_intern() {
+ use crate::fn_mod::foo;
+
+ foo();
+ }
+
+ fn test_extern() {
+ use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
+ use wildcard_imports_helper::{ExternA, extern_foo};
+
+ inner_for_self_import::inner_extern_foo();
+ inner_extern_foo();
+
+ extern_foo();
+
+ let _ = ExternA;
+ }
+
+ fn test_inner_nested() {
+ use self::{inner::inner_foo, inner2::inner_bar};
+
+ inner_foo();
+ inner_bar();
+ }
+
+ fn test_extern_reexported() {
+ use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
+
+ extern_exported();
+ let _ = ExternExportedStruct;
+ let _ = ExternExportedEnum::A;
+ }
+
+ mod inner_exported {
+ pub fn exported() {}
+ pub struct ExportedStruct;
+ pub enum ExportedEnum {
+ A,
+ }
+ }
+
+ mod inner_exported2 {
+ pub(crate) fn exported2() {}
+ }
+
+ mod inner {
+ pub fn inner_foo() {}
+ }
+
+ mod inner2 {
+ pub fn inner_bar() {}
+ }
+}
+
+fn test_reexported() {
+ use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
+
+ exported();
+ let _ = ExportedStruct;
+ let _ = ExportedEnum::A;
+}
+
+#[rustfmt::skip]
+fn test_weird_formatting() {
+ use crate:: in_fn_test::exported;
+ use crate:: fn_mod::foo;
+
+ exported();
+ foo();
+}
++
++mod super_imports {
++ fn foofoo() {}
++
++ mod should_be_replaced {
++ use super::foofoo;
++
++ fn with_super() {
++ let _ = foofoo();
++ }
++ }
++
++ mod test_should_pass {
++ use super::*;
++
++ fn with_super() {
++ let _ = foofoo();
++ }
++ }
++
++ mod test_should_pass_inside_function {
++ fn with_super_inside_function() {
++ use super::*;
++ let _ = foofoo();
++ }
++ }
++
++ mod test_should_pass_further_inside {
++ fn insidefoo() {}
++ mod inner {
++ use super::*;
++ fn with_super() {
++ let _ = insidefoo();
++ }
++ }
++ }
++
++ mod should_be_replaced_futher_inside {
++ fn insidefoo() {}
++ mod inner {
++ use super::insidefoo;
++ fn with_super() {
++ let _ = insidefoo();
++ }
++ }
++ }
++
++ mod use_explicit_should_be_replaced {
++ use super_imports::foofoo;
++
++ fn with_explicit() {
++ let _ = foofoo();
++ }
++ }
++
++ mod use_double_super_should_be_replaced {
++ mod inner {
++ use super::super::foofoo;
++
++ fn with_double_super() {
++ let _ = foofoo();
++ }
++ }
++ }
++
++ mod use_super_explicit_should_be_replaced {
++ use super::super::super_imports::foofoo;
++
++ fn with_super_explicit() {
++ let _ = foofoo();
++ }
++ }
++}
--- /dev/null
+// run-rustfix
+// aux-build:wildcard_imports_helper.rs
+
+#![warn(clippy::wildcard_imports)]
+//#![allow(clippy::redundant_pub_crate)]
+#![allow(unused)]
+#![warn(unused_imports)]
+
+extern crate wildcard_imports_helper;
+
+use crate::fn_mod::*;
+use crate::mod_mod::*;
+use crate::multi_fn_mod::*;
+#[macro_use]
+use crate::struct_mod::*;
+
+#[allow(unused_imports)]
+use wildcard_imports_helper::inner::inner_for_self_import;
+use wildcard_imports_helper::inner::inner_for_self_import::*;
+use wildcard_imports_helper::*;
+
+use std::io::prelude::*;
+
+struct ReadFoo;
+
+impl Read for ReadFoo {
+ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
+ Ok(0)
+ }
+}
+
+mod fn_mod {
+ pub fn foo() {}
+}
+
+mod mod_mod {
+ pub mod inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod multi_fn_mod {
+ pub fn multi_foo() {}
+ pub fn multi_bar() {}
+ pub fn multi_baz() {}
+ pub mod multi_inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod struct_mod {
+ pub struct A;
+ pub struct B;
+ pub mod inner_struct_mod {
+ pub struct C;
+ }
+
+ #[macro_export]
+ macro_rules! double_struct_import_test {
+ () => {
+ let _ = A;
+ };
+ }
+}
+
+fn main() {
+ foo();
+ multi_foo();
+ multi_bar();
+ multi_inner_mod::foo();
+ inner_mod::foo();
+ extern_foo();
+ inner_extern_bar();
+
+ let _ = A;
+ let _ = inner_struct_mod::C;
+ let _ = ExternA;
+
+ double_struct_import_test!();
+ double_struct_import_test!();
+}
+
+mod in_fn_test {
+ pub use self::inner_exported::*;
+ #[allow(unused_imports)]
+ pub(crate) use self::inner_exported2::*;
+
+ fn test_intern() {
+ use crate::fn_mod::*;
+
+ foo();
+ }
+
+ fn test_extern() {
+ use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
+ use wildcard_imports_helper::*;
+
+ inner_for_self_import::inner_extern_foo();
+ inner_extern_foo();
+
+ extern_foo();
+
+ let _ = ExternA;
+ }
+
+ fn test_inner_nested() {
+ use self::{inner::*, inner2::*};
+
+ inner_foo();
+ inner_bar();
+ }
+
+ fn test_extern_reexported() {
+ use wildcard_imports_helper::*;
+
+ extern_exported();
+ let _ = ExternExportedStruct;
+ let _ = ExternExportedEnum::A;
+ }
+
+ mod inner_exported {
+ pub fn exported() {}
+ pub struct ExportedStruct;
+ pub enum ExportedEnum {
+ A,
+ }
+ }
+
+ mod inner_exported2 {
+ pub(crate) fn exported2() {}
+ }
+
+ mod inner {
+ pub fn inner_foo() {}
+ }
+
+ mod inner2 {
+ pub fn inner_bar() {}
+ }
+}
+
+fn test_reexported() {
+ use crate::in_fn_test::*;
+
+ exported();
+ let _ = ExportedStruct;
+ let _ = ExportedEnum::A;
+}
+
+#[rustfmt::skip]
+fn test_weird_formatting() {
+ use crate:: in_fn_test:: * ;
+ use crate:: fn_mod::
+ *;
+
+ exported();
+ foo();
+}
++
++mod super_imports {
++ fn foofoo() {}
++
++ mod should_be_replaced {
++ use super::*;
++
++ fn with_super() {
++ let _ = foofoo();
++ }
++ }
++
++ mod test_should_pass {
++ use super::*;
++
++ fn with_super() {
++ let _ = foofoo();
++ }
++ }
++
++ mod test_should_pass_inside_function {
++ fn with_super_inside_function() {
++ use super::*;
++ let _ = foofoo();
++ }
++ }
++
++ mod test_should_pass_further_inside {
++ fn insidefoo() {}
++ mod inner {
++ use super::*;
++ fn with_super() {
++ let _ = insidefoo();
++ }
++ }
++ }
++
++ mod should_be_replaced_futher_inside {
++ fn insidefoo() {}
++ mod inner {
++ use super::*;
++ fn with_super() {
++ let _ = insidefoo();
++ }
++ }
++ }
++
++ mod use_explicit_should_be_replaced {
++ use super_imports::*;
++
++ fn with_explicit() {
++ let _ = foofoo();
++ }
++ }
++
++ mod use_double_super_should_be_replaced {
++ mod inner {
++ use super::super::*;
++
++ fn with_double_super() {
++ let _ = foofoo();
++ }
++ }
++ }
++
++ mod use_super_explicit_should_be_replaced {
++ use super::super::super_imports::*;
++
++ fn with_super_explicit() {
++ let _ = foofoo();
++ }
++ }
++}
--- /dev/null
- error: aborting due to 15 previous errors
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:11:5
+ |
+LL | use crate::fn_mod::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
+ |
+ = note: `-D clippy::wildcard-imports` implied by `-D warnings`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:12:5
+ |
+LL | use crate::mod_mod::*;
+ | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:13:5
+ |
+LL | use crate::multi_fn_mod::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:15:5
+ |
+LL | use crate::struct_mod::*;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:19:5
+ |
+LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:20:5
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:89:13
+ |
+LL | use crate::fn_mod::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:95:75
+ |
+LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
+ | ^ help: try: `inner_extern_foo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:96:13
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:107:20
+ |
+LL | use self::{inner::*, inner2::*};
+ | ^^^^^^^^ help: try: `inner::inner_foo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:107:30
+ |
+LL | use self::{inner::*, inner2::*};
+ | ^^^^^^^^^ help: try: `inner2::inner_bar`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:114:13
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:143:9
+ |
+LL | use crate::in_fn_test::*;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:152:9
+ |
+LL | use crate:: in_fn_test:: * ;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:153:9
+ |
+LL | use crate:: fn_mod::
+ | _________^
+LL | | *;
+ | |_________^ help: try: `crate:: fn_mod::foo`
+
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:164:13
++ |
++LL | use super::*;
++ | ^^^^^^^^ help: try: `super::foofoo`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:199:17
++ |
++LL | use super::*;
++ | ^^^^^^^^ help: try: `super::insidefoo`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:207:13
++ |
++LL | use super_imports::*;
++ | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:216:17
++ |
++LL | use super::super::*;
++ | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:225:13
++ |
++LL | use super::super::super_imports::*;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
++
++error: aborting due to 20 previous errors
+