--- /dev/null
+# Changelog
+
+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
+
+[7bfc26e...master](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...master)
+
+## Rust 1.56
+
+Current beta, release 2021-10-21
+
+[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
+
+### New Lints
+
+* [`unwrap_or_else_default`]
+ [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
+
+### Enhancements
+
+* [`needless_continue`]: Now also lints in `loop { continue; }` case
+ [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
+* [`disallowed_type`]: Now also primitive types can be disallowed
+ [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
+* [`manual_swap`]: Now also lints on xor swaps
+ [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
+* [`map_flatten`]: Now also lints on the `Result` type
+ [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
+* [`no_effect`]: Now also lints on inclusive ranges
+ [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
+
+### False Positive Fixes
+
+* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
+ [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
+* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
+ [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
+* [`similar_names`]: No longer complains about `iter` and `item` being too
+ similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
+
+### Suggestion Fixes/Improvements
+
+* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
+ [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
+* [`new_without_default`]: No longer shows the full qualified type path when
+ suggesting adding a `Default` implementation
+ [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
+* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
+ [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
+* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
+ references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
+* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
+ [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
+* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
+ applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
+
+### Documentation Improvements
+
+* Clippy now uses a lint to generate its lint documentation. [Lints all the way
+ down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
+ [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
+* Reworked Clippy's website:
+ [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
+ [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
+ * Added applicability information about lints
+ * Added a link to jump into the implementation
+ * Improved loading times
+ * Adapted some styling
+* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
+ [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
+* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
+ example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
+
+### New Lints
+
+* Renamed Lint: `if_let_some_result` is now called [`match_result_ok`]. Now also handles `while let` case.
+
+## Rust 1.55
+
+Current stable, released 2021-09-09
+
+[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
+
+### Important Changes
+
+* Stabilized `cargo clippy --fix` :tada:
+ [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
+
+### New Lints
+
+* [`rc_mutex`]
+ [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
+* [`nonstandard_macro_braces`]
+ [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
+* [`strlen_on_c_strings`]
+ [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
+* [`self_named_constructors`]
+ [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
+* [`disallowed_script_idents`]
+ [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
+* [`disallowed_type`]
+ [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
+* [`missing_enforced_import_renames`]
+ [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
+* [`extend_with_drain`]
+ [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
+
+### Moves and Deprecations
+
+* Moved [`from_iter_instead_of_collect`] to `pedantic`
+ [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
+* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
+ [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
+ * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
+ * Moved [`empty_loop`] to `suspicious`
+ * Moved [`eval_order_dependence`] to `suspicious`
+ * Moved [`float_equality_without_abs`] to `suspicious`
+ * Moved [`for_loops_over_fallibles`] to `suspicious`
+ * Moved [`misrefactored_assign_op`] to `suspicious`
+ * Moved [`mut_range_bound`] to `suspicious`
+ * Moved [`mutable_key_type`] to `suspicious`
+ * Moved [`suspicious_arithmetic_impl`] to `suspicious`
+ * Moved [`suspicious_assignment_formatting`] to `suspicious`
+ * Moved [`suspicious_else_formatting`] to `suspicious`
+ * Moved [`suspicious_map`] to `suspicious`
+ * Moved [`suspicious_op_assign_impl`] to `suspicious`
+ * Moved [`suspicious_unary_op_formatting`] to `suspicious`
+
+### Enhancements
+
+* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
+ [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
+* [`doc_markdown`]:
+ * Now detects unbalanced ticks
+ [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
+ * Add `FreeBSD` to the default configuration as an allowed identifier
+ [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
+* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
+ or hidden variants
+ [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
+* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
+ [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
+* [`blacklisted_name`]: Now allows blacklisted names in test code
+ [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
+* [`redundant_closure`]: Suggests `&mut` for `FnMut`
+ [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
+* [`disallowed_method`], [`disallowed_type`]: The configuration values `disallowed-method` and `disallowed-type`
+ no longer require fully qualified paths
+ [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
+* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
+ [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
+
+### False Positive Fixes
+
+* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
+ [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
+* [`use_self`]: No longer lints on type parameters
+ [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
+* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
+ [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
+* [`branches_sharing_code`]: Now always checks for block expressions
+ [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
+* [`field_reassign_with_default`]: No longer triggers in macros
+ [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
+* [`redundant_clone`]: No longer lints on required clones for borrowed data
+ [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
+* [`default_numeric_fallback`]: No longer triggers in external macros
+ [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
+* [`needless_bool`]: No longer lints in macros
+ [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
+* [`useless_format`]: No longer triggers when additional text is being appended
+ [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
+* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
+ [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
+
+### Suggestion Fixes/Improvements
+
+* [`needless_collect`]: Now show correct lint messages for shadowed values
+ [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
+* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
+ [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
+* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
+ [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
+* [`redundant_pattern_matching`]
+ [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
+* [`modulo_one`]
+ [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
+* [`use_self`]
+ [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
+
+## Rust 1.54
+
+Released 2021-07-29
+
+[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
+
+### New Lints
+
+- [`ref_binding_to_reference`]
+ [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
+- [`needless_bitwise_bool`]
+ [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
+- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
+- [`manual_str_repeat`]
+ [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
+- [`suspicious_splitn`]
+ [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
+
+### Moves and Deprecations
+
+- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
+ the new `avoid-breaking-exported-api` config option (see
+ [Enhancements](#1-54-enhancements))
+ [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
+- Move [`inconsistent_struct_constructor`] to `pedantic`
+ [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
+- Move [`needless_borrow`] to `style` (now warn-by-default)
+ [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
+- Move [`suspicious_operation_groupings`] to `nursery`
+ [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
+- Move [`semicolon_if_nothing_returned`] to `pedantic`
+ [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
+
+### Enhancements <a name="1-54-enhancements"></a>
+
+- [`while_let_on_iterator`]: Now also lints in nested loops
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
+ [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
+- [`needless_collect`]: Now also lints on assignments with type annotations
+ [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
+- [`if_then_some_else_none`]: Now works with the MSRV config
+ [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
+- Add `avoid-breaking-exported-api` config option for the lints
+ [`enum_variant_names`], [`large_types_passed_by_value`],
+ [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
+ [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
+ this configuration option to `false` before a major release (1.0/2.0/...) to
+ clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
+- [`needless_collect`]: Now lints on even more data structures
+ [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
+- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
+ attributes as sufficient documentation
+ [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
+- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
+ Now work as expected when used with `allow`
+ [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
+
+### False Positive Fixes
+
+- [`implicit_return`]: Now takes all diverging functions in account to avoid
+ false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
+- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
+ and the struct is used in the loop
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`multiple_inherent_impl`]: No longer lints with generic arguments
+ [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
+- [`comparison_chain`]: No longer lints in a `const` context
+ [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
+- [`while_immutable_condition`]: Fix false positive where mutation in the loop
+ variable wasn't picked up
+ [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
+- [`default_trait_access`]: No longer lints in macros
+ [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
+- [`needless_question_mark`]: No longer lints when the inner value is implicitly
+ dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
+- [`unused_unit`]: No longer lints when multiple macro contexts are involved
+ [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
+- [`eval_order_dependence`]: Fix false positive in async context
+ [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
+- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
+ type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
+- [`wrong_self_convention`]: No longer lints in trait implementations of
+ non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
+- [`suboptimal_flops`]: No longer lints on `powi(2)`
+ [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
+- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
+ [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
+- [`option_if_let_else`]: No longer lints on `else if let` pattern
+ [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
+- [`use_self`], [`useless_conversion`]: Fix false positives when generic
+ arguments are involved
+ [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
+- [`manual_unwrap_or`]: Fix false positive with deref coercion
+ [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
+- [`similar_names`]: No longer lints on `wparam`/`lparam`
+ [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
+- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
+ closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
+
+### Suggestion Fixes/Improvements
+
+- [`implicit_return`]
+ [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
+ - Fix suggestion for async functions
+ - Improve suggestion with macros
+ - Suggest to change `break` to `return` when appropriate
+- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`match_single_binding`]: Improve suggestion when match scrutinee has side
+ effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
+- [`needless_borrow`]: Now suggests to also change usage sites as needed
+ [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
+- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
+ buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
+- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
+ when a `<_ as Trait>::_` is involved
+ [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
+- [`not_unsafe_ptr_arg_deref`]: Improved error message
+ [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
+
+### ICE Fixes
+
+- Fix ICE when running Clippy on `libstd`
+ [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
+- [`implicit_return`]
+ [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
+
+## Rust 1.53
+
+Released 2021-06-17
+
+[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
+
+### New Lints
+
+* [`option_filter_map`]
+ [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
+* [`branches_sharing_code`]
+ [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
+* [`needless_for_each`]
+ [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
+* [`if_then_some_else_none`]
+ [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
+* [`non_octal_unix_permissions`]
+ [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
+* [`unnecessary_self_imports`]
+ [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
+* [`bool_assert_comparison`]
+ [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
+* [`cloned_instead_of_copied`]
+ [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
+* [`flat_map_option`]
+ [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
+
+### Moves and Deprecations
+
+* Deprecate [`filter_map`] lint
+ [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
+* Move [`transmute_ptr_to_ptr`] to `pedantic`
+ [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
+
+### Enhancements
+
+* [`mem_replace_with_default`]: Also lint on common std constructors
+ [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
+* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
+ [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
+* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
+ [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
+ * Attempt to find a common path prefix in suggestion
+ * Don't lint on `Option` and `Result`
+ * Consider `Self` prefix
+* [`explicit_deref_methods`]: Also lint on chained `deref` calls
+ [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
+* [`or_fun_call`]: Also lint on `unsafe` blocks
+ [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
+* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
+ `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
+* [`search_is_some`]: Also check for `is_none`
+ [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
+* [`string_lit_as_bytes`]: Also lint on `into_bytes`
+ [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
+* [`len_without_is_empty`]: Also lint if function signatures of `len` and
+ `is_empty` don't match
+ [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
+* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
+ [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
+* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
+ [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
+* [`needless_return`]: Also lint in `async` functions
+ [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
+* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
+ [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
+* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
+ now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
+
+### False Positive Fixes
+
+* [`upper_case_acronyms`]: No longer lints on public items
+ [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
+* [`suspicious_map`]: No longer lints when side effects may occur inside the
+ `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
+* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
+ [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
+* [`wrong_self_convention`]: Now respects `Copy` types
+ [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
+* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
+ from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
+* [`map_entry`]: Better detect if the entry API can be used
+ [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
+* [`or_fun_call`]: No longer lints on some `len` function calls
+ [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
+* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
+ generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
+* [`upper_case_acronyms`]: No longer lints on public items
+ [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
+* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
+ of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
+* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
+ suggesting to use `derive` instead
+ [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
+* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
+ [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
+* [`clone_on_copy`]: Only lint when using the `Clone` trait
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`wrong_self_convention`]: No longer lints inside a trait implementation
+ [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
+* [`redundant_clone`]: No longer lints when the cloned value is modified while
+ the clone is in use
+ [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
+* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
+ [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
+* [`cargo_common_metadata`]: Remove author requirement
+ [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
+* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
+ [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
+* [`panic`]: No longer wrongfully lints on `debug_assert` with message
+ [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
+* [`wrong_self_convention`]: No longer lints in trait implementations where no
+ `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
+* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
+ involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
+* [`suspicious_else_formatting`]: Allow Allman style braces
+ [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
+* [`inconsistent_struct_constructor`]: No longer lints in macros
+ [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
+* [`single_component_path_imports`]: No longer lints on macro re-exports
+ [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
+
+### Suggestion Fixes/Improvements
+
+* [`redundant_pattern_matching`]: Add a note when applying this lint would
+ change the drop order
+ [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
+* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
+ [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
+* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
+ [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
+* [`inconsistent_struct_constructor`]: Make lint description and message clearer
+ [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
+* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
+ as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
+* [`manual_flatten`]: Suggest to insert `copied` if necessary
+ [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
+* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
+ when the value is from a macro call
+ [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
+* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
+ [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
+* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`manual_map`]: Fix suggestion at the end of an if chain
+ [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
+* Fix needless parenthesis output in multiple lint suggestions
+ [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
+* [`needless_collect`]: Better explanation in the lint message
+ [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
+* [`useless_vec`]: Now considers mutability
+ [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
+* [`useless_format`]: Wrap the content in braces if necessary
+ [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
+* [`single_match`]: Don't suggest an equality check for types which don't
+ implement `PartialEq`
+ [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
+* [`from_over_into`]: Mention type in help message
+ [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
+* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
+ [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
+
+### ICE Fixes
+
+* [`macro_use_imports`]
+ [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
+* [`missing_panics_doc`]
+ [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
+* [`tabs_in_doc_comments`]
+ [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
+* [`missing_const_for_fn`]
+ [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
+
+### Others
+
+* [Clippy's lint
+ list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
+ themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
+* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
+ deprecation warning
+ [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
+
+## Rust 1.52
+
+Released 2021-05-06
+
+[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
+
+### New Lints
+
+* [`from_str_radix_10`]
+ [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
+* [`implicit_clone`]
+ [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
+* [`semicolon_if_nothing_returned`]
+ [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
+* [`manual_flatten`]
+ [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
+* [`inconsistent_struct_constructor`]
+ [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
+* [`iter_count`]
+ [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
+* [`default_numeric_fallback`]
+ [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
+* [`bytes_nth`]
+ [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
+* [`filter_map_identity`]
+ [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
+* [`manual_map`]
+ [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
+
+### Moves and Deprecations
+
+* Moved [`upper_case_acronyms`] to `pedantic`
+ [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
+* Moved [`manual_map`] to `nursery`
+ [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
+* Moved [`unnecessary_wraps`] to `pedantic`
+ [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
+* Moved [`trivial_regex`] to `nursery`
+ [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
+* Moved [`naive_bytecount`] to `pedantic`
+ [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
+* Moved [`upper_case_acronyms`] to `style`
+ [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
+* Moved [`manual_map`] to `style`
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+
+### Enhancements
+
+* [`disallowed_method`]: Now supports functions in addition to methods
+ [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
+* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
+ trigger the lint if there is more than one uppercase character next to each other
+ [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
+* [`collapsible_match`]: Now supports block comparison with different value names
+ [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
+* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
+ [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
+* Improved value usage detection in closures
+ [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
+
+### False Positive Fixes
+
+* [`use_self`]: No longer lints in macros
+ [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
+* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
+ [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
+* [`missing_inline_in_public_items`]: No longer lints for procedural macros
+ [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
+* [`inherent_to_string`]: No longer lints on functions with function generics
+ [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
+* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
+ [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
+* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
+ [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
+* [`collapsible_if`]: No longer lints on if statements with attributes
+ [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
+* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
+ [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
+* [`redundant_closure`]: Now ignores macros
+ [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
+* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+* [`vec_init_then_push`]: Fixed false positives for loops and if statements
+ [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
+* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
+ the `len` method as well as the type definition.
+ [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
+* [`let_underscore_drop`]: Only lints on types which implement `Drop`
+ [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
+* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
+ [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
+* [`cargo_common_metadata`]: No longer lints if
+ [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
+ is defined in the manifest
+ [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
+
+### Suggestion Fixes/Improvements
+
+* [`collapsible_match`]: Fixed lint message capitalization
+ [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
+* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
+ [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
+* [`manual_map`]: No longer expands macros in the suggestions
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+* Aligned Clippy's lint messages with the rustc dev guide
+ [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
+
+### Documentation Improvements
+
+* [`useless_format`]: Improved the documentation example
+ [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
+* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
+ [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
+
+### Others
+* Running `cargo clippy` after `cargo check` now works as expected
+ (`cargo clippy` and `cargo check` no longer shares the same build cache)
+ [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
+* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
+ [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
+* Extracted Clippy's `utils` module into the new `clippy_utils` crate
+ [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
+* Clippy lintcheck tool improvements
+ [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
+ [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
+ [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
+ [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
+ [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
+ [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
+
+## Rust 1.51
+
+Released 2021-03-25
+
+[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
+
+### New Lints
+
+* [`upper_case_acronyms`]
+ [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
+* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
+* [`case_sensitive_file_extension_comparisons`]
+ [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
+* [`needless_question_mark`]
+ [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
+* [`missing_panics_doc`]
+ [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
+* [`redundant_slicing`]
+ [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
+* [`vec_init_then_push`]
+ [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
+* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
+* [`collapsible_else_if`] (split out from `collapsible_if`)
+ [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
+* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
+* [`manual_filter_map`]
+ [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* [`exhaustive_enums`]
+ [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+* [`exhaustive_structs`]
+ [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+
+### Moves and Deprecations
+
+* Replace [`find_map`] with [`manual_find_map`]
+ [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
+ [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
+
+### Enhancements
+
+* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
+ [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
+* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
+ [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
+* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
+ scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
+
+### False Positive Fixes
+
+* [`similar_names`] Ignore underscore prefixed names
+ [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
+* [`print_literal`] and [`write_literal`] No longer lint numeric literals
+ [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
+* [`large_enum_variant`] No longer lints in external macros
+ [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
+* [`empty_enum`] Only lint if `never_type` feature is enabled
+ [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
+* [`field_reassign_with_default`] No longer lints in macros
+ [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
+* [`size_of_in_element_count`] No longer lints when dividing by element size
+ [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
+* [`needless_return`] No longer lints in macros
+ [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
+* [`match_overlapping_arm`] No longer lint when first arm is completely included
+ in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
+* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
+ identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
+
+### Suggestion Fixes/Improvements
+
+* [`field_reassign_with_default`] Don't expand macro in lint suggestion
+ [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
+* [`match_like_matches_macro`] Strip references in suggestion
+ [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
+* [`single_match`] Suggest `if` over `if let` when possible
+ [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
+* [`ref_in_deref`] Use parentheses correctly in suggestion
+ [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
+* [`stable_sort_primitive`] Clarify error message
+ [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
+
+### Documentation Improvements
+
+* Improve search performance on the Clippy website and make it possible to
+ directly search for lints on the GitHub issue tracker
+ [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
+* Clean up `README.md` by removing outdated paragraph
+ [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
+* [`await_holding_refcell_ref`] and [`await_holding_lock`]
+ [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
+* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
+
+### Others
+
+* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
+ project, take a look at the [Roadmap project page]. All issues listed there
+ are actively mentored
+ [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
+* The Clippy version number now corresponds to the Rust version number
+ [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
+* Fix oversight which caused Clippy to lint deps in some environments, where
+ `CLIPPY_TESTS=true` was set somewhere
+ [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
+* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
+ [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
+
+[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
+[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
+
+## Rust 1.50
+
+Released 2021-02-11
+
+[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
+
+### New Lints
+
+* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
+* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
+* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
+* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
+* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
+* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
+* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
+* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
+* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
+
+### Moves and Deprecations
+
+* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
+ as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
+ [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
+* Move [`map_err_ignore`] to `restriction`
+ [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
+* Move [`await_holding_refcell_ref`] to `pedantic`
+ [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
+* Move [`await_holding_lock`] to `pedantic`
+ [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
+
+### Enhancements
+
+* Add the `unreadable-literal-lint-fractions` configuration to disable
+ the `unreadable_literal` lint for fractions
+ [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
+* [`clone_on_copy`]: Now shows the type in the lint message
+ [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
+* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
+ [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
+* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
+ [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
+* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
+ [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
+* [`clone_double_ref`]: Now prints the reference type in the lint message
+ [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
+* [`modulo_one`]: Now also lints on -1.
+ [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
+* [`empty_loop`]: Now lints no_std crates, too
+ [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
+* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
+ [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
+* [`wrong_self_convention`]: Now also lints in trait definitions
+ [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
+* [`needless_borrow`]: Print the type in the lint message
+ [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
+
+[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
+
+### False Positive Fixes
+
+* [`manual_range_contains`]: No longer lints in `const fn`
+ [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
+* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
+ [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
+* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
+ [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
+* [`match_like_matches_macro`]: No longer lints on arms with attributes
+ [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
+* [`map_clone`]: No longer lints with deref and clone
+ [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
+* [`map_clone`]: No longer lints in the case of &mut
+ [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
+* [`needless_update`]: Now ignores `non_exhaustive` structs
+ [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
+* [`needless_collect`]: No longer lints when a collect is needed multiple times
+ [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
+* [`unnecessary_cast`] No longer lints cfg-dependent types
+ [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
+* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
+ Both now ignore enums with frozen variants
+ [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
+* [`field_reassign_with_default`] No longer lint for private fields
+ [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
+
+
+### Suggestion Fixes/Improvements
+
+* [`vec_box`]: Provide correct type scope suggestion
+ [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
+* [`manual_range_contains`]: Give correct suggestion when using floats
+ [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
+* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
+ [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
+* [`manual_async_fn`]: Improve suggestion formatting
+ [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
+* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
+ [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
+
+### ICE Fixes
+
+* Fix a crash in [`from_iter_instead_of_collect`]
+ [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
+* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
+ [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
+
+### Documentation Improvements
+
+* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
+ * Searching for lints with dashes and spaces is possible now. For example
+ `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
+ * Improved fuzzy search in lint descriptions
+* Various README improvements
+ [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
+* Add known problems to [`comparison_chain`] documentation
+ [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
+* Fix example used in [`cargo_common_metadata`]
+ [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
+* Improve [`map_clone`] documentation
+ [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
+
+### Others
+
+* You can now tell Clippy about the MSRV your project supports. Please refer to
+ the specific README section to learn more about MSRV support [here][msrv_readme]
+ [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
+* Add `--no-deps` option to avoid running on path dependencies in workspaces
+ [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
+
+## Rust 1.49
+
+Released 2020-12-31
+
+[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
+
+### New Lints
+
+* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
+* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
+* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
+* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
+* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
+* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
+* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
+* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
+* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
+* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
+* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
+* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
+* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
+* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
+* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
+* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
+* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
+
+### Moves and Deprecations
+
+* Rename `single_char_push_str` to [`single_char_add_str`]
+ [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
+* Rename `zero_width_space` to [`invisible_characters`]
+ [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
+* Deprecate `drop_bounds` (uplifted)
+ [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
+* Move [`string_lit_as_bytes`] to `nursery`
+ [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
+* Move [`rc_buffer`] to `restriction`
+ [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
+
+### Enhancements
+
+* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
+ reliable suggestion)
+ [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
+* [`single_char_add_str`]: Also lint on `String::insert_str`
+ [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
+* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
+ [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
+* [`eq_op`]: Also lint on the `assert_*!` macro family
+ [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
+* [`items_after_statements`]: Also lint in local macro expansions
+ [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
+* [`unnecessary_cast`]: Also lint casts on integer and float literals
+ [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
+* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
+ [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
+* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
+ [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
+* [`integer_arithmetic`]: Better handle `/` an `%` operators
+ [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
+
+### False Positive Fixes
+
+* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
+ lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
+* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
+ is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
+* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
+ [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
+* [`needless_range_loop`]: No longer lints, when the iterable is used in the
+ range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
+* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
+ [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
+* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
+ float (e.g. `713.32_64`)
+ [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
+* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
+ [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
+* [`boxed_local`]: No longer lints on `extern fn` arguments
+ [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
+* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
+ clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
+
+### Suggestion Fixes/Improvements
+
+* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
+ [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
+* [`needless_arbitrary_self_type`]: Correctly handle expanded code
+ [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
+* [`useless_format`]: Preserve raw strings in suggestion
+ [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
+* [`empty_loop`]: Suggest alternatives
+ [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
+* [`borrowed_box`]: Correctly add parentheses in suggestion
+ [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
+* [`unused_unit`]: Improve suggestion formatting
+ [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
+
+### Documentation Improvements
+
+* Some doc improvements:
+ * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
+ * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
+* [`doc_markdown`]: Document problematic link text style
+ [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
+
+## Rust 1.48
+
+Released 2020-11-19
+
+[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
+
+### New lints
+
+* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
+* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
+* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
+* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
+* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
+* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
+* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
+
+### Moves and Deprecations
+
+* Downgrade [`verbose_bit_mask`] to pedantic
+ [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
+
+### Enhancements
+
+* Extend [`precedence`] to handle chains of methods combined with unary negation
+ [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
+* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
+ [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
+* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
+ [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
+* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
+ [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
+* Avoid [`redundant_pattern_matching`] triggering in macros
+ [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
+* [`option_if_let_else`]: distinguish pure from impure `else` expressions
+ [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
+* [`needless_doctest_main`]: parse doctests instead of using textual search
+ [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
+* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
+ [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
+* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
+ [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
+* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
+ [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
+
+### False Positive Fixes
+
+* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
+ [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
+* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
+ [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
+* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
+ [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
+* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
+ [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
+* [`doc_markdown`]: allow using "GraphQL" without backticks
+ [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
+* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self`
+ [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
+* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
+ [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
+* [`should_implement_trait`]: ignore methods with lifetime parameters
+ [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
+* [`needless_return`]: avoid linting if a temporary borrows a local variable
+ [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
+* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
+ [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
+* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
+ [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
+* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
+ [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
+
+### Suggestion Fixes/Improvements
+
+* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
+ [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
+* [`useless_conversion`]: show the type in the error message
+ [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
+* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
+ [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
+* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
+ [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
+* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
+ [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
+* [`collapsible_if`]: don't use expanded code in the suggestion
+ [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
+* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
+ [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
+* [`unit_arg`]: improve the readability of the suggestion
+ [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
+* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
+ [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
+* Show line count and max lines in [`too_many_lines`] lint message
+ [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
+* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
+ [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
+* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
+ [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
+* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
+ [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
+* Make lint messages adhere to rustc dev guide conventions
+ [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
+
+### ICE Fixes
+
+* Fix ICE in [`repeat_once`]
+ [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
+
+### Documentation Improvements
+
+* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
+ [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
+* [`unnecessary_mut_passed`]: fix typo
+ [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
+* Add example of false positive to [`ptr_arg`] docs.
+ [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
+* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
+ [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
+
+## Rust 1.47
+
+Released 2020-10-08
+
+[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
+
+### New lints
+
+* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
+* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
+* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
+* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
+* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
+* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
+* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
+* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
+* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
+* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
+* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
+* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
+
+### Moves and Deprecations
+
+* Deprecate [`regex_macro`] lint
+ [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
+* Move [`range_minus_one`] to `pedantic`
+ [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
+
+### Enhancements
+
+* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
+ [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
+* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
+ [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
+* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
+ [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
+* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
+ [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
+* Make it possible to allow [`unsafe_derive_deserialize`]
+ [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
+* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
+ [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
+* Make [`clone_on_copy`] suggestion machine applicable
+ [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
+* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
+ [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
+
+### False Positive Fixes
+
+* Avoid triggering [`or_fun_call`] with const fns that take no arguments
+ [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
+* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
+ [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
+* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
+ [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
+* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
+ [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
+* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
+ [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
+* Avoid linting if key borrows in [`unnecessary_sort_by`]
+ [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
+* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
+ [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
+* Take input lifetimes into account in `manual_async_fn`
+ [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
+* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
+ [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
+* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
+ [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
+
+### Suggestion Fixes/Improvements
+
+* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
+ [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
+* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
+ [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
+* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
+ [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
+* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
+ [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
+* Add suggestion for [`iter_skip_next`]
+ [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
+* Improve [`collapsible_if`] fix suggestion
+ [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
+
+### ICE Fixes
+
+* Fix ICE caused by [`needless_collect`]
+ [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
+* Fix ICE caused by [`unnested_or_patterns`]
+ [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
+
+### Documentation Improvements
+
+* Fix grammar of [`await_holding_lock`] documentation
+ [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
+
+### Others
+
+* Make lints adhere to the rustc dev guide
+ [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
+
+## Rust 1.46
+
+Released 2020-08-27
+
+[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
+
+### New lints
+
+* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
+* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
+* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
+* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
+
+### Moves and Deprecations
+
+* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
+
+### Enhancements
+
+* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
+
+### False Positive Fixes
+
+* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
+ [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
+* [`let_and_return`]: Don't lint if a temporary borrow is involved
+ [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
+* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
+ [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
+* [`if_same_then_else`]: Don't assume multiplication is always commutative
+ [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
+* [`blacklisted_name`]: Remove `bar` from the default configuration
+ [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
+* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
+ [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
+
+### Suggestion Fixes/Improvements
+
+* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
+ [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
+* Add auto applicable suggestion to [`macro_use_imports`]
+ [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
+
+### ICE Fixes
+
+* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
+
+### Documentation Improvements
+
+* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
+
+### Others
+
+* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
+ into `rustc` and passes all the given arguments to `rustc`. This is especially
+ useful for tools that need the `rustc` version Clippy was compiled with,
+ instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
+ print the output of `rustc --version`.
+ [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
+* New issue templates now make it easier to complain if Clippy is too annoying
+ or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
+
+## Rust 1.45
+
+Released 2020-07-16
+
+[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
+
+### New lints
+
+* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
+* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
+* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
+* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
+* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
+* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
+* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
+* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
+* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
+
+### Moves and Deprecations
+
+* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
+* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
+* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
+* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
+* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
+* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+
+### Enhancements
+
+* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
+* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
+* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
+* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
+
+### False Positive Fixes
+
+* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
+* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
+* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
+* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
+* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
+* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
+* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
+* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
+* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
+* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
+* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
+* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
+and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
+* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
+
+### Suggestion Improvements
+
+* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
+* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
+* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
+* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
+
+### ICE Fixes
+
+* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
+* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
+
+### Documentation
+
+* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
+* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
+
+## Rust 1.44
+
+Released 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
+
+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
+[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.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
+[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
+[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
+[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
+[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
+[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
+[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
+[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
+[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
+[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
+[`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_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
+[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
+[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
+[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
+[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
+[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
+[`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
+[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
+[`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_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
+[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
+[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
+[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
+[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
+[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
+[`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_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
+[`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
+[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
+[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
+[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
+[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
+[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
+[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
+[`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_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
++[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
+[`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
+[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
+[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
+[`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
+[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
+[`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
+[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
+[`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
+[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
+[`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_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
+[`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
+[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
+[`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
+[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
+[`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_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
+[`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
+[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
+[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
+[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
+[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
+[`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_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_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
+[`if_then_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_panic
+[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
+[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
+[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
+[`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
+[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
+[`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_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
+[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
+[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
+[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
+[`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_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
+[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
+[`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
+[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
+[`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_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
+[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
+[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
+[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
+[`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
+[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
+[`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_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
+[`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_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
+[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
+[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
+[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
+[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
+[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
+[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
+[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
+[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
+[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
+[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
+[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
+[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
+[`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_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
+[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
+[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
+[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
+[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
+[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
+[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
+[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
+[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
+[`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_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
+[`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
+[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
+[`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_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
+[`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_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
+[`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
+[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
+[`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_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
+[`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_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
+[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
+[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
+[`needless_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_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
+[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
+[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
+[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
+[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
+[`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
+[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
+[`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
+[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
++[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
+[`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
+[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
+[`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_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_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
+[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
+[`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_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
+[`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_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
+[`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
+[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
+[`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_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
+[`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_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
+[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
+[`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
+[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
+[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
+[`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_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
+[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
+[`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_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
+[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
+[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
+[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
+[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
+[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
+[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
+[`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_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_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
+[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
+[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
+[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
+[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
+[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
+[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
+[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
+[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
+[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
+[`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_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
+[`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_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
+[`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
+[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
+[`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
+[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
+[`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_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
+[`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
+[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
+[`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_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
+[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
+[`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
+[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
+[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
+[`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
+[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
+[`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
+[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
+[`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
+[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
+[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
+[`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
+[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
+[`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_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
+[`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_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
+[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
+[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
+[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
+[`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
+[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
+[`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_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
+[`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_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
+[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
+[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
+[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
+[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
+[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
+[`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_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
+[`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
+[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
+[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
+[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
+[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
+[`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_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
+[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
+<!-- end autogenerated links to lint list -->
--- /dev/null
- use itertools::Itertools;
- use regex::Regex;
- use std::collections::HashMap;
- use std::ffi::OsStr;
- use std::fs;
- use std::lazy::SyncLazy;
- use std::path::{Path, PathBuf};
- use walkdir::WalkDir;
+#![feature(once_cell)]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
- static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
- Regex::new(
- r#"(?x)
- declare_clippy_lint!\s*[\{(]
- (?:\s+///.*)*
- \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
- (?P<cat>[a-z_]+)\s*,\s*
- "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
- "#,
- )
- .unwrap()
- });
-
- static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
- Regex::new(
- r#"(?x)
- declare_deprecated_lint!\s*[{(]\s*
- (?:\s+///.*)*
- \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
- "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
- "#,
- )
- .unwrap()
- });
- static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
-
- pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
-
- /// Lint data parsed from the Clippy source code.
- #[derive(Clone, PartialEq, Debug)]
- pub struct Lint {
- pub name: String,
- pub group: String,
- pub desc: String,
- pub deprecation: Option<String>,
- pub module: String,
- }
-
- impl Lint {
- #[must_use]
- pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
- Self {
- name: name.to_lowercase(),
- group: group.to_string(),
- desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
- deprecation: deprecation.map(ToString::to_string),
- module: module.to_string(),
- }
- }
-
- /// Returns all non-deprecated lints and non-internal lints
- #[must_use]
- pub fn usable_lints(lints: &[Self]) -> Vec<Self> {
- lints
- .iter()
- .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
- .cloned()
- .collect()
- }
-
- /// Returns all internal lints (not `internal_warn` lints)
- #[must_use]
- pub fn internal_lints(lints: &[Self]) -> Vec<Self> {
- lints.iter().filter(|l| l.group == "internal").cloned().collect()
- }
-
- /// Returns all deprecated lints
- #[must_use]
- pub fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
- lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
- }
-
- /// Returns the lints in a `HashMap`, grouped by the different lint groups
- #[must_use]
- pub fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
- lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
- }
- }
-
- /// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`.
- #[must_use]
- pub fn gen_lint_group_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
- lints
- .map(|l| format!(" LintId::of({}::{}),", l.module, l.name.to_uppercase()))
- .sorted()
- .collect::<Vec<String>>()
- }
-
- /// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`.
- #[must_use]
- pub fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
- lints
- .map(|l| &l.module)
- .unique()
- .map(|module| format!("mod {};", module))
- .sorted()
- .collect::<Vec<String>>()
- }
-
- /// Generates the list of lint links at the bottom of the README
- #[must_use]
- pub fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
- lints
- .sorted_by_key(|l| &l.name)
- .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
- .collect()
- }
-
- /// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`.
- #[must_use]
- pub fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
- lints
- .flat_map(|l| {
- l.deprecation
- .clone()
- .map(|depr_text| {
- vec![
- " store.register_removed(".to_string(),
- format!(" \"clippy::{}\",", l.name),
- format!(" \"{}\",", depr_text),
- " );".to_string(),
- ]
- })
- .expect("only deprecated lints should be passed")
- })
- .collect::<Vec<String>>()
- }
-
- #[must_use]
- pub fn gen_register_lint_list<'a>(
- internal_lints: impl Iterator<Item = &'a Lint>,
- usable_lints: impl Iterator<Item = &'a Lint>,
- ) -> Vec<String> {
- let header = " store.register_lints(&[".to_string();
- let footer = " ]);".to_string();
- let internal_lints = internal_lints
- .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase()))
- .map(|l| {
- format!(
- " #[cfg(feature = \"internal-lints\")]\n {}::{},",
- l.module,
- l.name.to_uppercase()
- )
- });
- let other_lints = usable_lints
- .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase()))
- .map(|l| format!(" {}::{},", l.module, l.name.to_uppercase()))
- .sorted();
- let mut lint_list = vec![header];
- lint_list.extend(internal_lints);
- lint_list.extend(other_lints);
- lint_list.push(footer);
- lint_list
- }
-
- /// Gathers all files in `src/clippy_lints` and gathers all lints inside
- pub fn gather_all() -> impl Iterator<Item = Lint> {
- lint_files().flat_map(|f| gather_from_file(&f))
- }
-
- fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
- let content = fs::read_to_string(dir_entry.path()).unwrap();
- let path = dir_entry.path();
- let filename = path.file_stem().unwrap();
- let path_buf = path.with_file_name(filename);
- let mut rel_path = path_buf
- .strip_prefix(clippy_project_root().join("clippy_lints/src"))
- .expect("only files in `clippy_lints/src` should be looked at");
- // If the lints are stored in mod.rs, we get the module name from
- // the containing directory:
- if filename == "mod" {
- rel_path = rel_path.parent().unwrap();
- }
-
- let module = rel_path
- .components()
- .map(|c| c.as_os_str().to_str().unwrap())
- .collect::<Vec<_>>()
- .join("::");
-
- parse_contents(&content, &module)
- }
-
- fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
- let lints = DEC_CLIPPY_LINT_RE
- .captures_iter(content)
- .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
- let deprecated = DEC_DEPRECATED_LINT_RE
- .captures_iter(content)
- .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
- // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
- lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
- }
-
- /// Collects all .rs files in the `clippy_lints/src` directory
- fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
- // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
- // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
- let path = clippy_project_root().join("clippy_lints/src");
- WalkDir::new(path)
- .into_iter()
- .filter_map(Result::ok)
- .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
- }
-
- /// Whether a file has had its text changed or not
- #[derive(PartialEq, Debug)]
- pub struct FileChange {
- pub changed: bool,
- pub new_lines: String,
- }
-
- /// Replaces a region in a file delimited by two lines matching regexes.
- ///
- /// `path` is the relative path to the file on which you want to perform the replacement.
- ///
- /// See `replace_region_in_text` for documentation of the other options.
- ///
- /// # Panics
- ///
- /// Panics if the path could not read or then written
- pub fn replace_region_in_file<F>(
- path: &Path,
- start: &str,
- end: &str,
- replace_start: bool,
- write_back: bool,
- replacements: F,
- ) -> FileChange
- where
- F: FnOnce() -> Vec<String>,
- {
- let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
- let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
-
- if write_back {
- if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
- panic!("Cannot write to {}: {}", path.display(), e);
- }
- }
- file_change
- }
-
- /// Replaces a region in a text delimited by two lines matching regexes.
- ///
- /// * `text` is the input text on which you want to perform the replacement
- /// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
- /// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
- /// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
- /// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
- /// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
- /// delimiter line is never replaced.
- /// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
- ///
- /// If you want to perform the replacement on files instead of already parsed text,
- /// use `replace_region_in_file`.
- ///
- /// # Example
- ///
- /// ```
- /// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
- /// let result =
- /// clippy_dev::replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
- /// vec!["a different".to_string(), "text".to_string()]
- /// })
- /// .new_lines;
- /// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
- /// ```
- ///
- /// # Panics
- ///
- /// Panics if start or end is not valid regex
- pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
- where
- F: FnOnce() -> Vec<String>,
- {
- let replace_it = replacements();
- let mut in_old_region = false;
- let mut found = false;
- let mut new_lines = vec![];
- let start = Regex::new(start).unwrap();
- let end = Regex::new(end).unwrap();
-
- for line in text.lines() {
- if in_old_region {
- if end.is_match(line) {
- in_old_region = false;
- new_lines.extend(replace_it.clone());
- new_lines.push(line.to_string());
- }
- } else if start.is_match(line) {
- if !replace_start {
- new_lines.push(line.to_string());
- }
- in_old_region = true;
- found = true;
- } else {
- new_lines.push(line.to_string());
- }
- }
-
- if !found {
- // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
- // given text or file. Most likely this is an error on the programmer's side and the Regex
- // is incorrect.
- eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
- std::process::exit(1);
- }
-
- let mut new_lines = new_lines.join("\n");
- if text.ends_with('\n') {
- new_lines.push('\n');
- }
- let changed = new_lines != text;
- FileChange { changed, new_lines }
- }
-
++use std::path::PathBuf;
+
+pub mod bless;
+pub mod fmt;
+pub mod new_lint;
+pub mod serve;
+pub mod setup;
+pub mod update_lints;
+
-
- #[test]
- fn test_parse_contents() {
- let result: Vec<Lint> = parse_contents(
- r#"
- declare_clippy_lint! {
- pub PTR_ARG,
- style,
- "really long \
- text"
- }
-
- declare_clippy_lint!{
- pub DOC_MARKDOWN,
- pedantic,
- "single line"
- }
-
- /// some doc comment
- declare_deprecated_lint! {
- pub SHOULD_ASSERT_EQ,
- "`assert!()` will be more flexible with RFC 2011"
- }
- "#,
- "module_name",
- )
- .collect();
-
- let expected = vec![
- Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
- Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
- Lint::new(
- "should_assert_eq",
- "Deprecated",
- "`assert!()` will be more flexible with RFC 2011",
- Some("`assert!()` will be more flexible with RFC 2011"),
- "module_name",
- ),
- ];
- assert_eq!(expected, result);
- }
-
- #[test]
- fn test_replace_region() {
- let text = "\nabc\n123\n789\ndef\nghi";
- let expected = FileChange {
- changed: true,
- new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
- vec!["hello world".to_string()]
- });
- assert_eq!(expected, result);
- }
-
- #[test]
- fn test_replace_region_with_start() {
- let text = "\nabc\n123\n789\ndef\nghi";
- let expected = FileChange {
- changed: true,
- new_lines: "\nhello world\ndef\nghi".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
- vec!["hello world".to_string()]
- });
- assert_eq!(expected, result);
- }
-
- #[test]
- fn test_replace_region_no_changes() {
- let text = "123\n456\n789";
- let expected = FileChange {
- changed: false,
- new_lines: "123\n456\n789".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
- assert_eq!(expected, result);
- }
-
- #[test]
- fn test_usable_lints() {
- let lints = vec![
- Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
- Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
- ];
- let expected = vec![Lint::new(
- "should_assert_eq2",
- "Not Deprecated",
- "abc",
- None,
- "module_name",
- )];
- assert_eq!(expected, Lint::usable_lints(&lints));
- }
-
- #[test]
- fn test_by_lint_group() {
- let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
- Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
- ];
- let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
- expected.insert(
- "group1".to_string(),
- vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
- ],
- );
- expected.insert(
- "group2".to_string(),
- vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
- );
- assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
- }
-
- #[test]
- fn test_gen_changelog_lint_list() {
- let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
- ];
- let expected = vec![
- format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
- format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()),
- ];
- assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
- }
-
- #[test]
- fn test_gen_deprecated() {
- let lints = vec![
- Lint::new(
- "should_assert_eq",
- "group1",
- "abc",
- Some("has been superseded by should_assert_eq2"),
- "module_name",
- ),
- Lint::new(
- "another_deprecated",
- "group2",
- "abc",
- Some("will be removed"),
- "module_name",
- ),
- ];
- let expected: Vec<String> = vec![
- " store.register_removed(",
- " \"clippy::should_assert_eq\",",
- " \"has been superseded by should_assert_eq2\",",
- " );",
- " store.register_removed(",
- " \"clippy::another_deprecated\",",
- " \"will be removed\",",
- " );",
- ]
- .into_iter()
- .map(String::from)
- .collect();
- assert_eq!(expected, gen_deprecated(lints.iter()));
- }
-
- #[test]
- #[should_panic]
- fn test_gen_deprecated_fail() {
- let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
- let _deprecated_lints = gen_deprecated(lints.iter());
- }
-
- #[test]
- fn test_gen_modules_list() {
- let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
- ];
- let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
- assert_eq!(expected, gen_modules_list(lints.iter()));
- }
-
- #[test]
- fn test_gen_lint_group_list() {
- let lints = vec![
- Lint::new("abc", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("internal", "internal_style", "abc", None, "module_name"),
- ];
- let expected = vec![
- " LintId::of(module_name::ABC),".to_string(),
- " LintId::of(module_name::INTERNAL),".to_string(),
- " LintId::of(module_name::SHOULD_ASSERT_EQ),".to_string(),
- ];
- assert_eq!(expected, gen_lint_group_list(lints.iter()));
- }
+/// Returns the path to the Clippy project directory
+///
+/// # Panics
+///
+/// Panics if the current directory could not be retrieved, there was an error reading any of the
+/// Cargo.toml files or ancestor directory is the clippy root directory
+#[must_use]
+pub fn clippy_project_root() -> PathBuf {
+ let current_dir = std::env::current_dir().unwrap();
+ for path in current_dir.ancestors() {
+ let result = std::fs::read_to_string(path.join("Cargo.toml"));
+ if let Err(err) = &result {
+ if err.kind() == std::io::ErrorKind::NotFound {
+ continue;
+ }
+ }
+
+ let content = result.unwrap();
+ if content.contains("[package]\nname = \"clippy\"") {
+ return path.to_path_buf();
+ }
+ }
+ panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
+}
--- /dev/null
- use crate::{
- gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list,
- replace_region_in_file, Lint, DOCS_LINK,
- };
++use itertools::Itertools;
++use regex::Regex;
++use std::collections::HashMap;
++use std::ffi::OsStr;
++use std::fs;
++use std::lazy::SyncLazy;
+use std::path::Path;
++use walkdir::WalkDir;
++
++use crate::clippy_project_root;
++
++const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
++ // Use that command to update this file and do not edit by hand.\n\
++ // Manual edits will be overwritten.\n\n";
++
++static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
++ Regex::new(
++ r#"(?x)
++ declare_clippy_lint!\s*[\{(]
++ (?:\s+///.*)*
++ \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
++ (?P<cat>[a-z_]+)\s*,\s*
++ "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
++"#,
++ )
++ .unwrap()
++});
++
++static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
++ Regex::new(
++ r#"(?x)
++ declare_deprecated_lint!\s*[{(]\s*
++ (?:\s+///.*)*
++ \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
++ "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
++"#,
++ )
++ .unwrap()
++});
++static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
++
++static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum UpdateMode {
+ Check,
+ Change,
+}
+
++/// Runs the `update_lints` command.
++///
++/// This updates various generated values from the lint source code.
++///
++/// `update_mode` indicates if the files should be updated or if updates should be checked for.
++///
++/// # Panics
++///
++/// Panics if a file path could not read from or then written to
+#[allow(clippy::too_many_lines)]
+pub fn run(update_mode: UpdateMode) {
+ let lint_list: Vec<Lint> = gather_all().collect();
+
+ let internal_lints = Lint::internal_lints(&lint_list);
+ let deprecated_lints = Lint::deprecated_lints(&lint_list);
+ let usable_lints = Lint::usable_lints(&lint_list);
+ let mut sorted_usable_lints = usable_lints.clone();
+ sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
+
+ let usable_lint_count = round_to_fifty(usable_lints.len());
+
+ let mut file_change = false;
+
+ file_change |= replace_region_in_file(
+ Path::new("README.md"),
+ &format!(
+ r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
+ DOCS_LINK
+ ),
+ "",
+ true,
+ update_mode == UpdateMode::Change,
+ || {
+ vec![format!(
+ "[There are over {} lints included in this crate!]({})",
+ usable_lint_count, DOCS_LINK
+ )]
+ },
+ )
+ .changed;
+
+ file_change |= replace_region_in_file(
+ Path::new("CHANGELOG.md"),
+ "<!-- begin autogenerated links to lint list -->",
+ "<!-- end autogenerated links to lint list -->",
+ false,
+ update_mode == UpdateMode::Change,
+ || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
+ )
+ .changed;
+
- file_change |= replace_region_in_file(
- Path::new("clippy_lints/src/lib.rs"),
- "begin deprecated lints",
- "end deprecated lints",
- false,
- update_mode == UpdateMode::Change,
- || gen_deprecated(deprecated_lints.iter()),
- )
- .changed;
-
- file_change |= replace_region_in_file(
- Path::new("clippy_lints/src/lib.rs"),
- "begin register lints",
- "end register lints",
- false,
- update_mode == UpdateMode::Change,
- || gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
- )
- .changed;
-
++ // This has to be in lib.rs, otherwise rustfmt doesn't work
+ file_change |= replace_region_in_file(
+ Path::new("clippy_lints/src/lib.rs"),
+ "begin lints modules",
+ "end lints modules",
+ false,
+ update_mode == UpdateMode::Change,
+ || gen_modules_list(usable_lints.iter()),
+ )
+ .changed;
+
- // Generate lists of lints in the clippy::all lint group
- file_change |= replace_region_in_file(
- Path::new("clippy_lints/src/lib.rs"),
- r#"store.register_group\(true, "clippy::all""#,
- r#"\]\);"#,
- false,
- update_mode == UpdateMode::Change,
- || {
- // clippy::all should only include the following lint groups:
- let all_group_lints = usable_lints.iter().filter(|l| {
- matches!(
- &*l.group,
- "correctness" | "suspicious" | "style" | "complexity" | "perf"
- )
- });
-
- gen_lint_group_list(all_group_lints)
- },
- )
- .changed;
++ if file_change && update_mode == UpdateMode::Check {
++ exit_with_failure();
++ }
+
- // Generate the list of lints for all other lint groups
- for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
- file_change |= replace_region_in_file(
- Path::new("clippy_lints/src/lib.rs"),
- &format!("store.register_group\\(true, \"clippy::{}\"", lint_group),
- r#"\]\);"#,
- false,
- update_mode == UpdateMode::Change,
- || gen_lint_group_list(lints.iter()),
++ process_file(
++ "clippy_lints/src/lib.register_lints.rs",
++ update_mode,
++ &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
++ );
++ process_file(
++ "clippy_lints/src/lib.deprecated.rs",
++ update_mode,
++ &gen_deprecated(deprecated_lints.iter()),
++ );
++
++ let all_group_lints = usable_lints.iter().filter(|l| {
++ matches!(
++ &*l.group,
++ "correctness" | "suspicious" | "style" | "complexity" | "perf"
+ )
- .changed;
- }
++ });
++ let content = gen_lint_group_list("all", all_group_lints);
++ process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
+
- if update_mode == UpdateMode::Check && file_change {
- println!(
- "Not all lints defined properly. \
- Please run `cargo dev update_lints` to make sure all lints are defined properly."
++ for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
++ let content = gen_lint_group_list(&lint_group, lints.iter());
++ process_file(
++ &format!("clippy_lints/src/lib.register_{}.rs", lint_group),
++ update_mode,
++ &content,
+ );
- std::process::exit(1);
+ }
+}
+
+pub fn print_lints() {
+ let lint_list: Vec<Lint> = gather_all().collect();
+ let usable_lints = Lint::usable_lints(&lint_list);
+ let usable_lint_count = usable_lints.len();
+ let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
+
+ for (lint_group, mut lints) in grouped_by_lint_group {
+ if lint_group == "Deprecated" {
+ continue;
+ }
+ println!("\n## {}", lint_group);
+
+ lints.sort_by_key(|l| l.name.clone());
+
+ for lint in lints {
+ println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
+ }
+ }
+
+ println!("there are {} lints", usable_lint_count);
+}
+
+fn round_to_fifty(count: usize) -> usize {
+ count / 50 * 50
+}
++
++fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
++ if update_mode == UpdateMode::Check {
++ let old_content =
++ fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e));
++ if content != old_content {
++ exit_with_failure();
++ }
++ } else {
++ fs::write(&path, content.as_bytes())
++ .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e));
++ }
++}
++
++fn exit_with_failure() {
++ println!(
++ "Not all lints defined properly. \
++ Please run `cargo dev update_lints` to make sure all lints are defined properly."
++ );
++ std::process::exit(1);
++}
++
++/// Lint data parsed from the Clippy source code.
++#[derive(Clone, PartialEq, Debug)]
++struct Lint {
++ name: String,
++ group: String,
++ desc: String,
++ deprecation: Option<String>,
++ module: String,
++}
++
++impl Lint {
++ #[must_use]
++ fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
++ Self {
++ name: name.to_lowercase(),
++ group: group.to_string(),
++ desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
++ deprecation: deprecation.map(ToString::to_string),
++ module: module.to_string(),
++ }
++ }
++
++ /// Returns all non-deprecated lints and non-internal lints
++ #[must_use]
++ fn usable_lints(lints: &[Self]) -> Vec<Self> {
++ lints
++ .iter()
++ .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
++ .cloned()
++ .collect()
++ }
++
++ /// Returns all internal lints (not `internal_warn` lints)
++ #[must_use]
++ fn internal_lints(lints: &[Self]) -> Vec<Self> {
++ lints.iter().filter(|l| l.group == "internal").cloned().collect()
++ }
++
++ /// Returns all deprecated lints
++ #[must_use]
++ fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
++ lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
++ }
++
++ /// Returns the lints in a `HashMap`, grouped by the different lint groups
++ #[must_use]
++ fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
++ lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
++ }
++}
++
++/// Generates the code for registering a group
++fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
++ let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
++ details.sort_unstable();
++
++ let mut output = GENERATED_FILE_COMMENT.to_string();
++
++ output.push_str(&format!(
++ "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n",
++ group_name
++ ));
++ for (module, name) in details {
++ output.push_str(&format!(" LintId::of({}::{}),\n", module, name));
++ }
++ output.push_str("])\n");
++
++ output
++}
++
++/// Generates the module declarations for `lints`
++#[must_use]
++fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++ lints
++ .map(|l| &l.module)
++ .unique()
++ .map(|module| format!("mod {};", module))
++ .sorted()
++ .collect::<Vec<String>>()
++}
++
++/// Generates the list of lint links at the bottom of the CHANGELOG
++#[must_use]
++fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++ lints
++ .sorted_by_key(|l| &l.name)
++ .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
++ .collect()
++}
++
++/// Generates the `register_removed` code
++#[must_use]
++fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
++ let mut output = GENERATED_FILE_COMMENT.to_string();
++ output.push_str("{\n");
++ for Lint { name, deprecation, .. } in lints {
++ output.push_str(&format!(
++ concat!(
++ " store.register_removed(\n",
++ " \"clippy::{}\",\n",
++ " \"{}\",\n",
++ " );\n"
++ ),
++ name,
++ deprecation.as_ref().expect("`lints` are deprecated")
++ ));
++ }
++ output.push_str("}\n");
++
++ output
++}
++
++/// Generates the code for registering lints
++#[must_use]
++fn gen_register_lint_list<'a>(
++ internal_lints: impl Iterator<Item = &'a Lint>,
++ usable_lints: impl Iterator<Item = &'a Lint>,
++) -> String {
++ let mut details: Vec<_> = internal_lints
++ .map(|l| (false, &l.module, l.name.to_uppercase()))
++ .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase())))
++ .collect();
++ details.sort_unstable();
++
++ let mut output = GENERATED_FILE_COMMENT.to_string();
++ output.push_str("store.register_lints(&[\n");
++
++ for (is_public, module_name, lint_name) in details {
++ if !is_public {
++ output.push_str(" #[cfg(feature = \"internal-lints\")]\n");
++ }
++ output.push_str(&format!(" {}::{},\n", module_name, lint_name));
++ }
++ output.push_str("])\n");
++
++ output
++}
++
++/// Gathers all files in `src/clippy_lints` and gathers all lints inside
++fn gather_all() -> impl Iterator<Item = Lint> {
++ lint_files().flat_map(|f| gather_from_file(&f))
++}
++
++fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
++ let content = fs::read_to_string(dir_entry.path()).unwrap();
++ let path = dir_entry.path();
++ let filename = path.file_stem().unwrap();
++ let path_buf = path.with_file_name(filename);
++ let mut rel_path = path_buf
++ .strip_prefix(clippy_project_root().join("clippy_lints/src"))
++ .expect("only files in `clippy_lints/src` should be looked at");
++ // If the lints are stored in mod.rs, we get the module name from
++ // the containing directory:
++ if filename == "mod" {
++ rel_path = rel_path.parent().unwrap();
++ }
++
++ let module = rel_path
++ .components()
++ .map(|c| c.as_os_str().to_str().unwrap())
++ .collect::<Vec<_>>()
++ .join("::");
++
++ parse_contents(&content, &module)
++}
++
++fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
++ let lints = DEC_CLIPPY_LINT_RE
++ .captures_iter(content)
++ .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
++ let deprecated = DEC_DEPRECATED_LINT_RE
++ .captures_iter(content)
++ .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
++ // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
++ lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
++}
++
++/// Collects all .rs files in the `clippy_lints/src` directory
++fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
++ // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
++ // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
++ let path = clippy_project_root().join("clippy_lints/src");
++ WalkDir::new(path)
++ .into_iter()
++ .filter_map(Result::ok)
++ .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
++}
++
++/// Whether a file has had its text changed or not
++#[derive(PartialEq, Debug)]
++struct FileChange {
++ changed: bool,
++ new_lines: String,
++}
++
++/// Replaces a region in a file delimited by two lines matching regexes.
++///
++/// `path` is the relative path to the file on which you want to perform the replacement.
++///
++/// See `replace_region_in_text` for documentation of the other options.
++///
++/// # Panics
++///
++/// Panics if the path could not read or then written
++fn replace_region_in_file<F>(
++ path: &Path,
++ start: &str,
++ end: &str,
++ replace_start: bool,
++ write_back: bool,
++ replacements: F,
++) -> FileChange
++where
++ F: FnOnce() -> Vec<String>,
++{
++ let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
++ let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
++
++ if write_back {
++ if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
++ panic!("Cannot write to {}: {}", path.display(), e);
++ }
++ }
++ file_change
++}
++
++/// Replaces a region in a text delimited by two lines matching regexes.
++///
++/// * `text` is the input text on which you want to perform the replacement
++/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
++/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
++/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
++/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
++/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
++/// delimiter line is never replaced.
++/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
++///
++/// If you want to perform the replacement on files instead of already parsed text,
++/// use `replace_region_in_file`.
++///
++/// # Example
++///
++/// ```ignore
++/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
++/// let result =
++/// replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
++/// vec!["a different".to_string(), "text".to_string()]
++/// })
++/// .new_lines;
++/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
++/// ```
++///
++/// # Panics
++///
++/// Panics if start or end is not valid regex
++fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
++where
++ F: FnOnce() -> Vec<String>,
++{
++ let replace_it = replacements();
++ let mut in_old_region = false;
++ let mut found = false;
++ let mut new_lines = vec![];
++ let start = Regex::new(start).unwrap();
++ let end = Regex::new(end).unwrap();
++
++ for line in text.lines() {
++ if in_old_region {
++ if end.is_match(line) {
++ in_old_region = false;
++ new_lines.extend(replace_it.clone());
++ new_lines.push(line.to_string());
++ }
++ } else if start.is_match(line) {
++ if !replace_start {
++ new_lines.push(line.to_string());
++ }
++ in_old_region = true;
++ found = true;
++ } else {
++ new_lines.push(line.to_string());
++ }
++ }
++
++ if !found {
++ // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
++ // given text or file. Most likely this is an error on the programmer's side and the Regex
++ // is incorrect.
++ eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
++ std::process::exit(1);
++ }
++
++ let mut new_lines = new_lines.join("\n");
++ if text.ends_with('\n') {
++ new_lines.push('\n');
++ }
++ let changed = new_lines != text;
++ FileChange { changed, new_lines }
++}
++
++#[test]
++fn test_parse_contents() {
++ let result: Vec<Lint> = parse_contents(
++ r#"
++declare_clippy_lint! {
++ pub PTR_ARG,
++ style,
++ "really long \
++ text"
++}
++
++declare_clippy_lint!{
++ pub DOC_MARKDOWN,
++ pedantic,
++ "single line"
++}
++
++/// some doc comment
++declare_deprecated_lint! {
++ pub SHOULD_ASSERT_EQ,
++ "`assert!()` will be more flexible with RFC 2011"
++}
++ "#,
++ "module_name",
++ )
++ .collect();
++
++ let expected = vec![
++ Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
++ Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
++ Lint::new(
++ "should_assert_eq",
++ "Deprecated",
++ "`assert!()` will be more flexible with RFC 2011",
++ Some("`assert!()` will be more flexible with RFC 2011"),
++ "module_name",
++ ),
++ ];
++ assert_eq!(expected, result);
++}
++
++#[cfg(test)]
++mod tests {
++ use super::*;
++
++ #[test]
++ fn test_replace_region() {
++ let text = "\nabc\n123\n789\ndef\nghi";
++ let expected = FileChange {
++ changed: true,
++ new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
++ };
++ let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
++ vec!["hello world".to_string()]
++ });
++ assert_eq!(expected, result);
++ }
++
++ #[test]
++ fn test_replace_region_with_start() {
++ let text = "\nabc\n123\n789\ndef\nghi";
++ let expected = FileChange {
++ changed: true,
++ new_lines: "\nhello world\ndef\nghi".to_string(),
++ };
++ let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
++ vec!["hello world".to_string()]
++ });
++ assert_eq!(expected, result);
++ }
++
++ #[test]
++ fn test_replace_region_no_changes() {
++ let text = "123\n456\n789";
++ let expected = FileChange {
++ changed: false,
++ new_lines: "123\n456\n789".to_string(),
++ };
++ let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
++ assert_eq!(expected, result);
++ }
++
++ #[test]
++ fn test_usable_lints() {
++ let lints = vec![
++ Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
++ Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
++ Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
++ Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
++ ];
++ let expected = vec![Lint::new(
++ "should_assert_eq2",
++ "Not Deprecated",
++ "abc",
++ None,
++ "module_name",
++ )];
++ assert_eq!(expected, Lint::usable_lints(&lints));
++ }
++
++ #[test]
++ fn test_by_lint_group() {
++ let lints = vec![
++ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++ Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
++ Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
++ ];
++ let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
++ expected.insert(
++ "group1".to_string(),
++ vec![
++ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++ Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
++ ],
++ );
++ expected.insert(
++ "group2".to_string(),
++ vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
++ );
++ assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
++ }
++
++ #[test]
++ fn test_gen_changelog_lint_list() {
++ let lints = vec![
++ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++ Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
++ ];
++ let expected = vec![
++ format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
++ format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()),
++ ];
++ assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
++ }
++
++ #[test]
++ fn test_gen_deprecated() {
++ let lints = vec![
++ Lint::new(
++ "should_assert_eq",
++ "group1",
++ "abc",
++ Some("has been superseded by should_assert_eq2"),
++ "module_name",
++ ),
++ Lint::new(
++ "another_deprecated",
++ "group2",
++ "abc",
++ Some("will be removed"),
++ "module_name",
++ ),
++ ];
++
++ let expected = GENERATED_FILE_COMMENT.to_string()
++ + &[
++ "{",
++ " store.register_removed(",
++ " \"clippy::should_assert_eq\",",
++ " \"has been superseded by should_assert_eq2\",",
++ " );",
++ " store.register_removed(",
++ " \"clippy::another_deprecated\",",
++ " \"will be removed\",",
++ " );",
++ "}",
++ ]
++ .join("\n")
++ + "\n";
++
++ assert_eq!(expected, gen_deprecated(lints.iter()));
++ }
++
++ #[test]
++ #[should_panic]
++ fn test_gen_deprecated_fail() {
++ let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
++ let _deprecated_lints = gen_deprecated(lints.iter());
++ }
++
++ #[test]
++ fn test_gen_modules_list() {
++ let lints = vec![
++ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++ Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
++ ];
++ let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
++ assert_eq!(expected, gen_modules_list(lints.iter()));
++ }
++
++ #[test]
++ fn test_gen_lint_group_list() {
++ let lints = vec![
++ Lint::new("abc", "group1", "abc", None, "module_name"),
++ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++ Lint::new("internal", "internal_style", "abc", None, "module_name"),
++ ];
++ let expected = GENERATED_FILE_COMMENT.to_string()
++ + &[
++ "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
++ " LintId::of(module_name::ABC),",
++ " LintId::of(module_name::INTERNAL),",
++ " LintId::of(module_name::SHOULD_ASSERT_EQ),",
++ "])",
++ ]
++ .join("\n")
++ + "\n";
++
++ let result = gen_lint_group_list("group1", lints.iter());
++
++ assert_eq!(expected, result);
++ }
++}
--- /dev/null
- if let AttrStyle::Outer = attr.style;
+//! checks for attributes
+
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::match_panic_def_id;
+use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
+use if_chain::if_chain;
+use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
+use rustc_errors::Applicability;
+use rustc_hir::{
+ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
+};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
+use rustc_span::symbol::{Symbol, SymbolStr};
+use semver::Version;
+
+static UNIX_SYSTEMS: &[&str] = &[
+ "android",
+ "dragonfly",
+ "emscripten",
+ "freebsd",
+ "fuchsia",
+ "haiku",
+ "illumos",
+ "ios",
+ "l4re",
+ "linux",
+ "macos",
+ "netbsd",
+ "openbsd",
+ "redox",
+ "solaris",
+ "vxworks",
+];
+
+// NOTE: windows is excluded from the list because it's also a valid target family.
+static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for items annotated with `#[inline(always)]`,
+ /// unless the annotated function is empty or simply panics.
+ ///
+ /// ### Why is this bad?
+ /// While there are valid uses of this annotation (and once
+ /// you know when to use it, by all means `allow` this lint), it's a common
+ /// newbie-mistake to pepper one's code with it.
+ ///
+ /// As a rule of thumb, before slapping `#[inline(always)]` on a function,
+ /// measure if that additional function call really affects your runtime profile
+ /// sufficiently to make up for the increase in compile time.
+ ///
+ /// ### Known problems
+ /// False positives, big time. This lint is meant to be
+ /// deactivated by everyone doing serious performance work. This means having
+ /// done the measurement.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// #[inline(always)]
+ /// fn not_quite_hot_code(..) { ... }
+ /// ```
+ pub INLINE_ALWAYS,
+ pedantic,
+ "use of `#[inline(always)]`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `extern crate` and `use` items annotated with
+ /// lint attributes.
+ ///
+ /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
+ /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
+ /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
+ /// `extern crate` items with a `#[macro_use]` attribute.
+ ///
+ /// ### Why is this bad?
+ /// Lint attributes have no effect on crate imports. Most
+ /// likely a `!` was forgotten.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// // Bad
+ /// #[deny(dead_code)]
+ /// extern crate foo;
+ /// #[forbid(dead_code)]
+ /// use foo::bar;
+ ///
+ /// // Ok
+ /// #[allow(unused_imports)]
+ /// use foo::baz;
+ /// #[allow(unused_imports)]
+ /// #[macro_use]
+ /// extern crate baz;
+ /// ```
+ pub USELESS_ATTRIBUTE,
+ correctness,
+ "use of lint attributes on `extern crate` items"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `#[deprecated]` annotations with a `since`
+ /// field that is not a valid semantic version.
+ ///
+ /// ### Why is this bad?
+ /// For checking the version of the deprecation, it must be
+ /// a valid semver. Failing that, the contained information is useless.
+ ///
+ /// ### Example
+ /// ```rust
+ /// #[deprecated(since = "forever")]
+ /// fn something_else() { /* ... */ }
+ /// ```
+ pub DEPRECATED_SEMVER,
+ correctness,
+ "use of `#[deprecated(since = \"x\")]` where x is not semver"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for empty lines after outer attributes
+ ///
+ /// ### Why is this bad?
+ /// Most likely the attribute was meant to be an inner attribute using a '!'.
+ /// If it was meant to be an outer attribute, then the following item
+ /// should not be separated by empty lines.
+ ///
+ /// ### Known problems
+ /// Can cause false positives.
+ ///
+ /// From the clippy side it's difficult to detect empty lines between an attributes and the
+ /// following item because empty lines and comments are not part of the AST. The parsing
+ /// currently works for basic cases but is not perfect.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Good (as inner attribute)
+ /// #![allow(dead_code)]
+ ///
+ /// fn this_is_fine() { }
+ ///
+ /// // Bad
+ /// #[allow(dead_code)]
+ ///
+ /// fn not_quite_good_code() { }
+ ///
+ /// // Good (as outer attribute)
+ /// #[allow(dead_code)]
+ /// fn this_is_fine_too() { }
+ /// ```
+ pub EMPTY_LINE_AFTER_OUTER_ATTR,
+ nursery,
+ "empty line after outer attribute"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
+ ///
+ /// ### Why is this bad?
+ /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
+ /// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust
+ /// #![deny(clippy::restriction)]
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// #![deny(clippy::as_conversions)]
+ /// ```
+ pub BLANKET_CLIPPY_RESTRICTION_LINTS,
+ suspicious,
+ "enabling the complete restriction group"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
+ /// with `#[rustfmt::skip]`.
+ ///
+ /// ### Why is this bad?
+ /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
+ /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
+ ///
+ /// ### Known problems
+ /// This lint doesn't detect crate level inner attributes, because they get
+ /// processed before the PreExpansionPass lints get executed. See
+ /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust
+ /// #[cfg_attr(rustfmt, rustfmt_skip)]
+ /// fn main() { }
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// #[rustfmt::skip]
+ /// fn main() { }
+ /// ```
+ pub DEPRECATED_CFG_ATTR,
+ complexity,
+ "usage of `cfg_attr(rustfmt)` instead of tool attributes"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for cfg attributes having operating systems used in target family position.
+ ///
+ /// ### Why is this bad?
+ /// The configuration option will not be recognised and the related item will not be included
+ /// by the conditional compilation engine.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust
+ /// #[cfg(linux)]
+ /// fn conditional() { }
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// #[cfg(target_os = "linux")]
+ /// fn conditional() { }
+ /// ```
+ ///
+ /// Or:
+ /// ```rust
+ /// #[cfg(unix)]
+ /// fn conditional() { }
+ /// ```
+ /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
+ pub MISMATCHED_TARGET_OS,
+ correctness,
+ "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
+}
+
+declare_lint_pass!(Attributes => [
+ INLINE_ALWAYS,
+ DEPRECATED_SEMVER,
+ USELESS_ATTRIBUTE,
+ BLANKET_CLIPPY_RESTRICTION_LINTS,
+]);
+
+impl<'tcx> LateLintPass<'tcx> for Attributes {
+ fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
+ if let Some(items) = &attr.meta_item_list() {
+ if let Some(ident) = attr.ident() {
+ if is_lint_level(ident.name) {
+ check_clippy_lint_names(cx, ident.name, items);
+ }
+ if items.is_empty() || !attr.has_name(sym::deprecated) {
+ return;
+ }
+ for item in items {
+ if_chain! {
+ if let NestedMetaItem::MetaItem(mi) = &item;
+ if let MetaItemKind::NameValue(lit) = &mi.kind;
+ if mi.has_name(sym::since);
+ then {
+ check_semver(cx, item.span(), lit);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ if is_relevant_item(cx, item) {
+ check_attrs(cx, item.span, item.ident.name, attrs);
+ }
+ match item.kind {
+ ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
+ let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
+
+ for attr in attrs {
+ if in_external_macro(cx.sess(), attr.span) {
+ return;
+ }
+ if let Some(lint_list) = &attr.meta_item_list() {
+ if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
+ // permit `unused_imports`, `deprecated`, `unreachable_pub`,
+ // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items
+ // and `unused_imports` for `extern crate` items with `macro_use`
+ for lint in lint_list {
+ match item.kind {
+ ItemKind::Use(..) => {
+ if is_word(lint, sym!(unused_imports))
+ || is_word(lint, sym::deprecated)
+ || is_word(lint, sym!(unreachable_pub))
+ || is_word(lint, sym!(unused))
+ || extract_clippy_lint(lint).map_or(false, |s| s == "wildcard_imports")
+ || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use")
+ {
+ return;
+ }
+ },
+ ItemKind::ExternCrate(..) => {
+ if is_word(lint, sym!(unused_imports)) && skip_unused_imports {
+ return;
+ }
+ if is_word(lint, sym!(unused_extern_crates)) {
+ return;
+ }
+ },
+ _ => {},
+ }
+ }
+ let line_span = first_line_of_span(cx, attr.span);
+
+ if let Some(mut sugg) = snippet_opt(cx, line_span) {
+ if sugg.contains("#[") {
+ span_lint_and_then(
+ cx,
+ USELESS_ATTRIBUTE,
+ line_span,
+ "useless lint attribute",
+ |diag| {
+ sugg = sugg.replacen("#[", "#![", 1);
+ diag.span_suggestion(
+ line_span,
+ "if you just forgot a `!`, use",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
+ }
+ }
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
+ if is_relevant_impl(cx, item) {
+ check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if is_relevant_trait(cx, item) {
+ check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
+ }
+ }
+}
+
+/// Returns the lint name if it is clippy lint.
+fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<SymbolStr> {
+ if_chain! {
+ if let Some(meta_item) = lint.meta_item();
+ if meta_item.path.segments.len() > 1;
+ if let tool_name = meta_item.path.segments[0].ident;
+ if tool_name.name == sym::clippy;
+ then {
+ let lint_name = meta_item.path.segments.last().unwrap().ident.name;
+ return Some(lint_name.as_str());
+ }
+ }
+ None
+}
+
+fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
+ for lint in items {
+ if let Some(lint_name) = extract_clippy_lint(lint) {
+ if lint_name == "restriction" && name != sym::allow {
+ span_lint_and_help(
+ cx,
+ BLANKET_CLIPPY_RESTRICTION_LINTS,
+ lint.span(),
+ "restriction lints are not meant to be all enabled",
+ None,
+ "try enabling only the lints you really need",
+ );
+ }
+ }
+ }
+}
+
+fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
+ if let ItemKind::Fn(_, _, eid) = item.kind {
+ is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
+ } else {
+ true
+ }
+}
+
+fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
+ match item.kind {
+ ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value),
+ _ => false,
+ }
+}
+
+fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
+ match item.kind {
+ TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
+ TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
+ is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
+ },
+ _ => false,
+ }
+}
+
+fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
+ block.stmts.first().map_or(
+ block
+ .expr
+ .as_ref()
+ .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
+ |stmt| match &stmt.kind {
+ StmtKind::Local(_) => true,
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
+ StmtKind::Item(_) => false,
+ },
+ )
+}
+
+fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
+ match &expr.kind {
+ ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
+ ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
+ ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
+ ExprKind::Call(path_expr, _) => {
+ if let ExprKind::Path(qpath) = &path_expr.kind {
+ typeck_results
+ .qpath_res(qpath, path_expr.hir_id)
+ .opt_def_id()
+ .map_or(true, |fun_id| !match_panic_def_id(cx, fun_id))
+ } else {
+ true
+ }
+ },
+ _ => true,
+ }
+}
+
+fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
+ if span.from_expansion() {
+ return;
+ }
+
+ for attr in attrs {
+ if let Some(values) = attr.meta_item_list() {
+ if values.len() != 1 || !attr.has_name(sym::inline) {
+ continue;
+ }
+ if is_word(&values[0], sym::always) {
+ span_lint(
+ cx,
+ INLINE_ALWAYS,
+ attr.span,
+ &format!(
+ "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
+ name
+ ),
+ );
+ }
+ }
+ }
+}
+
+fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) {
+ if let LitKind::Str(is, _) = lit.kind {
+ if Version::parse(&is.as_str()).is_ok() {
+ return;
+ }
+ }
+ span_lint(
+ cx,
+ DEPRECATED_SEMVER,
+ span,
+ "the since field must contain a semver-compliant version",
+ );
+}
+
+fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
+ if let NestedMetaItem::MetaItem(mi) = &nmi {
+ mi.is_word() && mi.has_name(expected)
+ } else {
+ false
+ }
+}
+
+declare_lint_pass!(EarlyAttributes => [
+ DEPRECATED_CFG_ATTR,
+ MISMATCHED_TARGET_OS,
+ EMPTY_LINE_AFTER_OUTER_ATTR,
+]);
+
+impl EarlyLintPass for EarlyAttributes {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
+ check_empty_line_after_outer_attr(cx, item);
+ }
+
+ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
+ check_deprecated_cfg_attr(cx, attr);
+ check_mismatched_target_os(cx, attr);
+ }
+}
+
+fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
+ for attr in &item.attrs {
+ let attr_item = if let AttrKind::Normal(ref attr, _) = attr.kind {
+ attr
+ } else {
+ return;
+ };
+
+ if attr.style == AttrStyle::Outer {
+ if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
+ return;
+ }
+
+ let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
+ let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent());
+
+ if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
+ let lines = snippet.split('\n').collect::<Vec<_>>();
+ let lines = without_block_comments(lines);
+
+ if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
+ span_lint(
+ cx,
+ EMPTY_LINE_AFTER_OUTER_ATTR,
+ begin_of_attr_to_item,
+ "found an empty line after an outer attribute. \
+ Perhaps you forgot to add a `!` to make it an inner attribute?",
+ );
+ }
+ }
+ }
+ }
+}
+
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
+ if_chain! {
+ // check cfg_attr
+ if attr.has_name(sym::cfg_attr);
+ if let Some(items) = attr.meta_item_list();
+ if items.len() == 2;
+ // check for `rustfmt`
+ if let Some(feature_item) = items[0].meta_item();
+ if feature_item.has_name(sym::rustfmt);
+ // check for `rustfmt_skip` and `rustfmt::skip`
+ if let Some(skip_item) = &items[1].meta_item();
+ if skip_item.has_name(sym!(rustfmt_skip)) ||
+ skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
+ // Only lint outer attributes, because custom inner attributes are unstable
+ // Tracking issue: https://github.com/rust-lang/rust/issues/54726
++ if attr.style == AttrStyle::Outer;
+ then {
+ span_lint_and_sugg(
+ cx,
+ DEPRECATED_CFG_ATTR,
+ attr.span,
+ "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
+ "use",
+ "#[rustfmt::skip]".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
+
+fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
+ fn find_os(name: &str) -> Option<&'static str> {
+ UNIX_SYSTEMS
+ .iter()
+ .chain(NON_UNIX_SYSTEMS.iter())
+ .find(|&&os| os == name)
+ .copied()
+ }
+
+ fn is_unix(name: &str) -> bool {
+ UNIX_SYSTEMS.iter().any(|&os| os == name)
+ }
+
+ fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
+ let mut mismatched = Vec::new();
+
+ for item in items {
+ if let NestedMetaItem::MetaItem(meta) = item {
+ match &meta.kind {
+ MetaItemKind::List(list) => {
+ mismatched.extend(find_mismatched_target_os(list));
+ },
+ MetaItemKind::Word => {
+ if_chain! {
+ if let Some(ident) = meta.ident();
+ if let Some(os) = find_os(&*ident.name.as_str());
+ then {
+ mismatched.push((os, ident.span));
+ }
+ }
+ },
+ MetaItemKind::NameValue(..) => {},
+ }
+ }
+ }
+
+ mismatched
+ }
+
+ if_chain! {
+ if attr.has_name(sym::cfg);
+ if let Some(list) = attr.meta_item_list();
+ let mismatched = find_mismatched_target_os(&list);
+ if !mismatched.is_empty();
+ then {
+ let mess = "operating system used in target family position";
+
+ span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
+ // Avoid showing the unix suggestion multiple times in case
+ // we have more than one mismatch for unix-like systems
+ let mut unix_suggested = false;
+
+ for (os, span) in mismatched {
+ let sugg = format!("target_os = \"{}\"", os);
+ diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
+
+ if !unix_suggested && is_unix(os) {
+ diag.help("did you mean `unix`?");
+ unix_suggested = true;
+ }
+ }
+ });
+ }
+ }
+}
+
+fn is_lint_level(symbol: Symbol) -> bool {
+ matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid)
+}
--- /dev/null
- /// There are two potential solutions. One is to use an asynx-aware Mutex
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{match_def_path, paths};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::GeneratorInteriorTypeCause;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to await while holding a
+ /// non-async-aware MutexGuard.
+ ///
+ /// ### Why is this bad?
+ /// The Mutex types found in std::sync and parking_lot
+ /// are not designed to operate in an async context across await points.
+ ///
++ /// There are two potential solutions. One is to use an async-aware Mutex
+ /// type. Many asynchronous foundation crates provide such a Mutex type. The
+ /// other solution is to ensure the mutex is unlocked before calling await,
+ /// either by introducing a scope or an explicit call to Drop::drop.
+ ///
+ /// ### Known problems
+ /// Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// use std::sync::Mutex;
+ ///
+ /// async fn foo(x: &Mutex<u32>) {
+ /// let guard = x.lock().unwrap();
+ /// *guard += 1;
+ /// bar.await;
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// use std::sync::Mutex;
+ ///
+ /// async fn foo(x: &Mutex<u32>) {
+ /// {
+ /// let guard = x.lock().unwrap();
+ /// *guard += 1;
+ /// }
+ /// bar.await;
+ /// }
+ /// ```
+ pub AWAIT_HOLDING_LOCK,
+ pedantic,
+ "Inside an async function, holding a MutexGuard while calling await"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to await while holding a
+ /// `RefCell` `Ref` or `RefMut`.
+ ///
+ /// ### Why is this bad?
+ /// `RefCell` refs only check for exclusive mutable access
+ /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
+ /// risks panics from a mutable ref shared while other refs are outstanding.
+ ///
+ /// ### Known problems
+ /// Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// use std::cell::RefCell;
+ ///
+ /// async fn foo(x: &RefCell<u32>) {
+ /// let mut y = x.borrow_mut();
+ /// *y += 1;
+ /// bar.await;
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// use std::cell::RefCell;
+ ///
+ /// async fn foo(x: &RefCell<u32>) {
+ /// {
+ /// let mut y = x.borrow_mut();
+ /// *y += 1;
+ /// }
+ /// bar.await;
+ /// }
+ /// ```
+ pub AWAIT_HOLDING_REFCELL_REF,
+ pedantic,
+ "Inside an async function, holding a RefCell ref while calling await"
+}
+
+declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
+
+impl LateLintPass<'_> for AwaitHolding {
+ fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
+ use AsyncGeneratorKind::{Block, Closure, Fn};
+ if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
+ let body_id = BodyId {
+ hir_id: body.value.hir_id,
+ };
+ let typeck_results = cx.tcx.typeck_body(body_id);
+ check_interior_types(
+ cx,
+ typeck_results.generator_interior_types.as_ref().skip_binder(),
+ body.value.span,
+ );
+ }
+ }
+}
+
+fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
+ for ty_cause in ty_causes {
+ if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
+ if is_mutex_guard(cx, adt.did) {
+ span_lint_and_note(
+ cx,
+ AWAIT_HOLDING_LOCK,
+ ty_cause.span,
+ "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await",
+ ty_cause.scope_span.or(Some(span)),
+ "these are all the await points this lock is held through",
+ );
+ }
+ if is_refcell_ref(cx, adt.did) {
+ span_lint_and_note(
+ cx,
+ AWAIT_HOLDING_REFCELL_REF,
+ ty_cause.span,
+ "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await",
+ ty_cause.scope_span.or(Some(span)),
+ "these are all the await points this ref is held through",
+ );
+ }
+ }
+ }
+}
+
+fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
+ match_def_path(cx, def_id, &paths::MUTEX_GUARD)
+ || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
+ || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
+ || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
+ || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
+ || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
+}
+
+fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
+ match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
+}
--- /dev/null
- let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_isize_or_usize;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, FloatTy, Ty};
+
+use super::{utils, CAST_PRECISION_LOSS};
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+ if !cast_from.is_integral() || cast_to.is_integral() {
+ return;
+ }
+
+ let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
++ let to_nbits = if cast_to.kind() == &ty::Float(FloatTy::F32) {
+ 32
+ } else {
+ 64
+ };
+
+ if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) {
+ return;
+ }
+
+ let cast_to_f64 = to_nbits == 64;
+ let mantissa_nbits = if cast_to_f64 { 52 } else { 23 };
+ let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64;
+ let arch_dependent_str = "on targets with 64-bit wide pointers ";
+ let from_nbits_str = if arch_dependent {
+ "64".to_owned()
+ } else if is_isize_or_usize(cast_from) {
+ "32 or 64".to_owned()
+ } else {
+ utils::int_ty_to_nbits(cast_from, cx.tcx).to_string()
+ };
+
+ span_lint(
+ cx,
+ CAST_PRECISION_LOSS,
+ expr.span,
+ &format!(
+ "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \
+ but `{1}`'s mantissa is only {4} bits wide)",
+ cast_from,
+ if cast_to_f64 { "f64" } else { "f32" },
+ if arch_dependent { arch_dependent_str } else { "" },
+ from_nbits_str,
+ mantissa_nbits
+ ),
+ );
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
+use rustc_hir::{
+ def::{DefKind, Res},
+ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects manual `std::default::Default` implementations that are identical to a derived implementation.
+ ///
+ /// ### Why is this bad?
+ /// It is less concise.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct Foo {
+ /// bar: bool
+ /// }
+ ///
+ /// impl std::default::Default for Foo {
+ /// fn default() -> Self {
+ /// Self {
+ /// bar: false
+ /// }
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Could be written as:
+ ///
+ /// ```rust
+ /// #[derive(Default)]
+ /// struct Foo {
+ /// bar: bool
+ /// }
+ /// ```
+ ///
+ /// ### Known problems
+ /// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
+ /// in generic types and the user defined `impl` maybe is more generalized or
+ /// specialized than what derive will produce. This lint can't detect the manual `impl`
+ /// has exactly equal bounds, and therefore this lint is disabled for types with
+ /// generic parameters.
+ ///
+ pub DERIVABLE_IMPLS,
+ complexity,
+ "manual implementation of the `Default` trait which is equal to a derive"
+}
+
+declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
+
+fn is_path_self(e: &Expr<'_>) -> bool {
+ if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
+ matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _))
+ } else {
+ false
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ if_chain! {
+ if let ItemKind::Impl(Impl {
+ of_trait: Some(ref trait_ref),
+ items: [child],
+ self_ty,
+ ..
+ }) = item.kind;
+ if let attrs = cx.tcx.hir().attrs(item.hir_id());
+ if !is_automatically_derived(attrs);
+ if !in_macro(item.span);
+ if let Some(def_id) = trait_ref.trait_def_id();
+ if cx.tcx.is_diagnostic_item(sym::Default, def_id);
+ if let impl_item_hir = child.id.hir_id();
+ if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
+ if let ImplItemKind::Fn(_, b) = &impl_item.kind;
+ if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
+ if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
+ if !attrs.iter().any(|attr| attr.doc_str().is_some());
+ if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
+ if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
++ if adt_def.is_struct();
+ then {
+ if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
+ if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
+ for arg in a.args {
+ if !matches!(arg, GenericArg::Lifetime(_)) {
+ return;
+ }
+ }
+ }
+ }
+ let should_emit = match remove_blocks(func_expr).kind {
+ ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
+ ExprKind::Call(callee, args)
+ if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
+ ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
+ _ => false,
+ };
+ if should_emit {
+ let path_string = cx.tcx.def_path_str(adt_def.did);
+ span_lint_and_help(
+ cx,
+ DERIVABLE_IMPLS,
+ item.span,
+ "this `impl` can be derived",
+ None,
+ &format!("try annotating `{}` with `#[derive(Default)]`", path_string),
+ );
+ }
+ }
+ }
+ }
+}
--- /dev/null
- if let Unsafety::Unsafe = header.unsafety;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
+use clippy_utils::paths;
+use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{get_trait_def_id, is_automatically_derived, is_lint_allowed, match_def_path};
+use if_chain::if_chain;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::{
+ BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty::{self, Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for deriving `Hash` but implementing `PartialEq`
+ /// explicitly or vice versa.
+ ///
+ /// ### Why is this bad?
+ /// The implementation of these traits must agree (for
+ /// example for use with `HashMap`) so it’s probably a bad idea to use a
+ /// default-generated `Hash` implementation with an explicitly defined
+ /// `PartialEq`. In particular, the following must hold for any type:
+ ///
+ /// ```text
+ /// k1 == k2 ⇒ hash(k1) == hash(k2)
+ /// ```
+ ///
+ /// ### Example
+ /// ```ignore
+ /// #[derive(Hash)]
+ /// struct Foo;
+ ///
+ /// impl PartialEq for Foo {
+ /// ...
+ /// }
+ /// ```
+ pub DERIVE_HASH_XOR_EQ,
+ correctness,
+ "deriving `Hash` but implementing `PartialEq` explicitly"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for deriving `Ord` but implementing `PartialOrd`
+ /// explicitly or vice versa.
+ ///
+ /// ### Why is this bad?
+ /// The implementation of these traits must agree (for
+ /// example for use with `sort`) so it’s probably a bad idea to use a
+ /// default-generated `Ord` implementation with an explicitly defined
+ /// `PartialOrd`. In particular, the following must hold for any type
+ /// implementing `Ord`:
+ ///
+ /// ```text
+ /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
+ /// ```
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// #[derive(Ord, PartialEq, Eq)]
+ /// struct Foo;
+ ///
+ /// impl PartialOrd for Foo {
+ /// ...
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// #[derive(PartialEq, Eq)]
+ /// struct Foo;
+ ///
+ /// impl PartialOrd for Foo {
+ /// fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
+ /// Some(self.cmp(other))
+ /// }
+ /// }
+ ///
+ /// impl Ord for Foo {
+ /// ...
+ /// }
+ /// ```
+ /// or, if you don't need a custom ordering:
+ /// ```rust,ignore
+ /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
+ /// struct Foo;
+ /// ```
+ pub DERIVE_ORD_XOR_PARTIAL_ORD,
+ correctness,
+ "deriving `Ord` but implementing `PartialOrd` explicitly"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for explicit `Clone` implementations for `Copy`
+ /// types.
+ ///
+ /// ### Why is this bad?
+ /// To avoid surprising behaviour, these traits should
+ /// agree and the behaviour of `Copy` cannot be overridden. In almost all
+ /// situations a `Copy` type should have a `Clone` implementation that does
+ /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
+ /// gets you.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// #[derive(Copy)]
+ /// struct Foo;
+ ///
+ /// impl Clone for Foo {
+ /// // ..
+ /// }
+ /// ```
+ pub EXPL_IMPL_CLONE_ON_COPY,
+ pedantic,
+ "implementing `Clone` explicitly on `Copy` types"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for deriving `serde::Deserialize` on a type that
+ /// has methods using `unsafe`.
+ ///
+ /// ### Why is this bad?
+ /// Deriving `serde::Deserialize` will create a constructor
+ /// that may violate invariants hold by another constructor.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// use serde::Deserialize;
+ ///
+ /// #[derive(Deserialize)]
+ /// pub struct Foo {
+ /// // ..
+ /// }
+ ///
+ /// impl Foo {
+ /// pub fn new() -> Self {
+ /// // setup here ..
+ /// }
+ ///
+ /// pub unsafe fn parts() -> (&str, &str) {
+ /// // assumes invariants hold
+ /// }
+ /// }
+ /// ```
+ pub UNSAFE_DERIVE_DESERIALIZE,
+ pedantic,
+ "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
+}
+
+declare_lint_pass!(Derive => [
+ EXPL_IMPL_CLONE_ON_COPY,
+ DERIVE_HASH_XOR_EQ,
+ DERIVE_ORD_XOR_PARTIAL_ORD,
+ UNSAFE_DERIVE_DESERIALIZE
+]);
+
+impl<'tcx> LateLintPass<'tcx> for Derive {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ if let ItemKind::Impl(Impl {
+ of_trait: Some(ref trait_ref),
+ ..
+ }) = item.kind
+ {
+ let ty = cx.tcx.type_of(item.def_id);
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let is_automatically_derived = is_automatically_derived(attrs);
+
+ check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
+ check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
+
+ if is_automatically_derived {
+ check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
+ } else {
+ check_copy_clone(cx, item, trait_ref, ty);
+ }
+ }
+ }
+}
+
+/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
+fn check_hash_peq<'tcx>(
+ cx: &LateContext<'tcx>,
+ span: Span,
+ trait_ref: &TraitRef<'_>,
+ ty: Ty<'tcx>,
+ hash_is_automatically_derived: bool,
+) {
+ if_chain! {
+ if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
+ if let Some(def_id) = trait_ref.trait_def_id();
+ if match_def_path(cx, def_id, &paths::HASH);
+ then {
+ // Look for the PartialEq implementations for `ty`
+ cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
+ let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
+
+ if peq_is_automatically_derived == hash_is_automatically_derived {
+ return;
+ }
+
+ let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
+
+ // Only care about `impl PartialEq<Foo> for Foo`
+ // For `impl PartialEq<B> for A, input_types is [A, B]
+ if trait_ref.substs.type_at(1) == ty {
+ let mess = if peq_is_automatically_derived {
+ "you are implementing `Hash` explicitly but have derived `PartialEq`"
+ } else {
+ "you are deriving `Hash` but have implemented `PartialEq` explicitly"
+ };
+
+ span_lint_and_then(
+ cx,
+ DERIVE_HASH_XOR_EQ,
+ span,
+ mess,
+ |diag| {
+ if let Some(local_def_id) = impl_id.as_local() {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
+ diag.span_note(
+ cx.tcx.hir().span(hir_id),
+ "`PartialEq` implemented here"
+ );
+ }
+ }
+ );
+ }
+ });
+ }
+ }
+}
+
+/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
+fn check_ord_partial_ord<'tcx>(
+ cx: &LateContext<'tcx>,
+ span: Span,
+ trait_ref: &TraitRef<'_>,
+ ty: Ty<'tcx>,
+ ord_is_automatically_derived: bool,
+) {
+ if_chain! {
+ if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD);
+ if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
+ if let Some(def_id) = &trait_ref.trait_def_id();
+ if *def_id == ord_trait_def_id;
+ then {
+ // Look for the PartialOrd implementations for `ty`
+ cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
+ let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
+
+ if partial_ord_is_automatically_derived == ord_is_automatically_derived {
+ return;
+ }
+
+ let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
+
+ // Only care about `impl PartialOrd<Foo> for Foo`
+ // For `impl PartialOrd<B> for A, input_types is [A, B]
+ if trait_ref.substs.type_at(1) == ty {
+ let mess = if partial_ord_is_automatically_derived {
+ "you are implementing `Ord` explicitly but have derived `PartialOrd`"
+ } else {
+ "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
+ };
+
+ span_lint_and_then(
+ cx,
+ DERIVE_ORD_XOR_PARTIAL_ORD,
+ span,
+ mess,
+ |diag| {
+ if let Some(local_def_id) = impl_id.as_local() {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
+ diag.span_note(
+ cx.tcx.hir().span(hir_id),
+ "`PartialOrd` implemented here"
+ );
+ }
+ }
+ );
+ }
+ });
+ }
+ }
+}
+
+/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
+fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
+ let clone_id = match cx.tcx.lang_items().clone_trait() {
+ Some(id) if trait_ref.trait_def_id() == Some(id) => id,
+ _ => return,
+ };
+ let copy_id = match cx.tcx.lang_items().copy_trait() {
+ Some(id) => id,
+ None => return,
+ };
+ let (ty_adt, ty_subs) = match *ty.kind() {
+ // Unions can't derive clone.
+ ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
+ _ => return,
+ };
+ // If the current self type doesn't implement Copy (due to generic constraints), search to see if
+ // there's a Copy impl for any instance of the adt.
+ if !is_copy(cx, ty) {
+ if ty_subs.non_erasable_generics().next().is_some() {
+ let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| {
+ impls
+ .iter()
+ .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did))
+ });
+ if !has_copy_impl {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+ // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
+ // this impl.
+ if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
+ return;
+ }
+
+ span_lint_and_note(
+ cx,
+ EXPL_IMPL_CLONE_ON_COPY,
+ item.span,
+ "you are implementing `Clone` explicitly on a `Copy` type",
+ Some(item.span),
+ "consider deriving `Clone` or removing `Copy`",
+ );
+}
+
+/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
+fn check_unsafe_derive_deserialize<'tcx>(
+ cx: &LateContext<'tcx>,
+ item: &Item<'_>,
+ trait_ref: &TraitRef<'_>,
+ ty: Ty<'tcx>,
+) {
+ fn item_from_def_id<'tcx>(cx: &LateContext<'tcx>, def_id: DefId) -> &'tcx Item<'tcx> {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ cx.tcx.hir().expect_item(hir_id)
+ }
+
+ fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
+ let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
+ walk_item(&mut visitor, item);
+ visitor.has_unsafe
+ }
+
+ if_chain! {
+ if let Some(trait_def_id) = trait_ref.trait_def_id();
+ if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
+ if let ty::Adt(def, _) = ty.kind();
+ if let Some(local_def_id) = def.did.as_local();
+ let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
+ if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
+ if cx.tcx.inherent_impls(def.did)
+ .iter()
+ .map(|imp_did| item_from_def_id(cx, *imp_did))
+ .any(|imp| has_unsafe(cx, imp));
+ then {
+ span_lint_and_help(
+ cx,
+ UNSAFE_DERIVE_DESERIALIZE,
+ item.span,
+ "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
+ None,
+ "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
+ );
+ }
+ }
+}
+
+struct UnsafeVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ has_unsafe: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) {
+ if self.has_unsafe {
+ return;
+ }
+
+ if_chain! {
+ if let Some(header) = kind.header();
- if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
++ if header.unsafety == Unsafety::Unsafe;
+ then {
+ self.has_unsafe = true;
+ }
+ }
+
+ walk_fn(self, kind, decl, body_id, span, id);
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.has_unsafe {
+ return;
+ }
+
+ if let ExprKind::Block(block, _) = expr.kind {
++ if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
+ self.has_unsafe = true;
+ }
+ }
+
+ walk_expr(self, expr);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::All(self.cx.tcx.hir())
+ }
+}
--- /dev/null
- _ => {},
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note};
+use clippy_utils::source::first_line_of_span;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
+use if_chain::if_chain;
+use itertools::Itertools;
+use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind};
+use rustc_ast::token::CommentKind;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::emitter::EmitterWriter;
+use rustc_errors::Handler;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_parse::maybe_new_parser_from_source_str;
+use rustc_parse::parser::ForceCollect;
+use rustc_session::parse::ParseSess;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
+use rustc_span::{sym, FileName, Pos};
+use std::io;
+use std::ops::Range;
+use std::thread;
+use url::Url;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the presence of `_`, `::` or camel-case words
+ /// outside ticks in documentation.
+ ///
+ /// ### Why is this bad?
+ /// *Rustdoc* supports markdown formatting, `_`, `::` and
+ /// camel-case probably indicates some code which should be included between
+ /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
+ /// consider that.
+ ///
+ /// ### Known problems
+ /// Lots of bad docs won’t be fixed, what the lint checks
+ /// for is limited, and there are still false positives. HTML elements and their
+ /// content are not linted.
+ ///
+ /// In addition, when writing documentation comments, including `[]` brackets
+ /// inside a link text would trip the parser. Therfore, documenting link with
+ /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
+ /// would fail.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// /// Do something with the foo_bar parameter. See also
+ /// /// that::other::module::foo.
+ /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
+ /// fn doit(foo_bar: usize) {}
+ /// ```
+ ///
+ /// ```rust
+ /// // Link text with `[]` brackets should be written as following:
+ /// /// Consume the array and return the inner
+ /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
+ /// /// [SmallVec]: SmallVec
+ /// fn main() {}
+ /// ```
+ pub DOC_MARKDOWN,
+ pedantic,
+ "presence of `_`, `::` or camel-case outside backticks in documentation"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the doc comments of publicly visible
+ /// unsafe functions and warns if there is no `# Safety` section.
+ ///
+ /// ### Why is this bad?
+ /// Unsafe functions should document their safety
+ /// preconditions, so that users can be sure they are using them safely.
+ ///
+ /// ### Examples
+ /// ```rust
+ ///# type Universe = ();
+ /// /// This function should really be documented
+ /// pub unsafe fn start_apocalypse(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ ///
+ /// At least write a line about safety:
+ ///
+ /// ```rust
+ ///# type Universe = ();
+ /// /// # Safety
+ /// ///
+ /// /// This function should not be called before the horsemen are ready.
+ /// pub unsafe fn start_apocalypse(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ pub MISSING_SAFETY_DOC,
+ style,
+ "`pub unsafe fn` without `# Safety` docs"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks the doc comments of publicly visible functions that
+ /// return a `Result` type and warns if there is no `# Errors` section.
+ ///
+ /// ### Why is this bad?
+ /// Documenting the type of errors that can be returned from a
+ /// function can help callers write code to handle the errors appropriately.
+ ///
+ /// ### Examples
+ /// Since the following function returns a `Result` it has an `# Errors` section in
+ /// its doc comment:
+ ///
+ /// ```rust
+ ///# use std::io;
+ /// /// # Errors
+ /// ///
+ /// /// Will return `Err` if `filename` does not exist or the user does not have
+ /// /// permission to read it.
+ /// pub fn read(filename: String) -> io::Result<String> {
+ /// unimplemented!();
+ /// }
+ /// ```
+ pub MISSING_ERRORS_DOC,
+ pedantic,
+ "`pub fn` returns `Result` without `# Errors` in doc comment"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks the doc comments of publicly visible functions that
+ /// may panic and warns if there is no `# Panics` section.
+ ///
+ /// ### Why is this bad?
+ /// Documenting the scenarios in which panicking occurs
+ /// can help callers who do not want to panic to avoid those situations.
+ ///
+ /// ### Examples
+ /// Since the following function may panic it has a `# Panics` section in
+ /// its doc comment:
+ ///
+ /// ```rust
+ /// /// # Panics
+ /// ///
+ /// /// Will panic if y is 0
+ /// pub fn divide_by(x: i32, y: i32) -> i32 {
+ /// if y == 0 {
+ /// panic!("Cannot divide by 0")
+ /// } else {
+ /// x / y
+ /// }
+ /// }
+ /// ```
+ pub MISSING_PANICS_DOC,
+ pedantic,
+ "`pub fn` may panic without `# Panics` in doc comment"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `fn main() { .. }` in doctests
+ ///
+ /// ### Why is this bad?
+ /// The test can be shorter (and likely more readable)
+ /// if the `fn main()` is left implicit.
+ ///
+ /// ### Examples
+ /// ``````rust
+ /// /// An example of a doctest with a `main()` function
+ /// ///
+ /// /// # Examples
+ /// ///
+ /// /// ```
+ /// /// fn main() {
+ /// /// // this needs not be in an `fn`
+ /// /// }
+ /// /// ```
+ /// fn needless_main() {
+ /// unimplemented!();
+ /// }
+ /// ``````
+ pub NEEDLESS_DOCTEST_MAIN,
+ style,
+ "presence of `fn main() {` in code examples"
+}
+
+#[allow(clippy::module_name_repetitions)]
+#[derive(Clone)]
+pub struct DocMarkdown {
+ valid_idents: FxHashSet<String>,
+ in_trait_impl: bool,
+}
+
+impl DocMarkdown {
+ pub fn new(valid_idents: FxHashSet<String>) -> Self {
+ Self {
+ valid_idents,
+ in_trait_impl: false,
+ }
+ }
+}
+
+impl_lint_pass!(DocMarkdown =>
+ [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
+);
+
+impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
+ fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+ let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
+ check_attrs(cx, &self.valid_idents, attrs);
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let headers = check_attrs(cx, &self.valid_idents, attrs);
+ match item.kind {
+ hir::ItemKind::Fn(ref sig, _, body_id) => {
+ if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
+ let body = cx.tcx.hir().body(body_id);
+ let mut fpu = FindPanicUnwrap {
+ cx,
+ typeck_results: cx.tcx.typeck(item.def_id),
+ panic_span: None,
+ };
+ fpu.visit_expr(&body.value);
+ lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
+ }
+ },
+ hir::ItemKind::Impl(ref impl_) => {
+ self.in_trait_impl = impl_.of_trait.is_some();
+ },
- let parser = pulldown_cmark::Parser::new(&doc).into_offset_iter();
++ hir::ItemKind::Trait(_, unsafety, ..) => {
++ if !headers.safety && unsafety == hir::Unsafety::Unsafe {
++ span_lint(
++ cx,
++ MISSING_SAFETY_DOC,
++ item.span,
++ "docs for unsafe trait missing `# Safety` section",
++ );
++ }
++ },
++ _ => (),
+ }
+ }
+
+ fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ if let hir::ItemKind::Impl { .. } = item.kind {
+ self.in_trait_impl = false;
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let headers = check_attrs(cx, &self.valid_idents, attrs);
+ if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
+ if !in_external_macro(cx.tcx.sess, item.span) {
+ lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, None, None);
+ }
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let headers = check_attrs(cx, &self.valid_idents, attrs);
+ if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
+ return;
+ }
+ if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
+ let body = cx.tcx.hir().body(body_id);
+ let mut fpu = FindPanicUnwrap {
+ cx,
+ typeck_results: cx.tcx.typeck(item.def_id),
+ panic_span: None,
+ };
+ fpu.visit_expr(&body.value);
+ lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
+ }
+ }
+}
+
+fn lint_for_missing_headers<'tcx>(
+ cx: &LateContext<'tcx>,
+ def_id: LocalDefId,
+ span: impl Into<MultiSpan> + Copy,
+ sig: &hir::FnSig<'_>,
+ headers: DocHeaders,
+ body_id: Option<hir::BodyId>,
+ panic_span: Option<Span>,
+) {
+ if !cx.access_levels.is_exported(def_id) {
+ return; // Private functions do not require doc comments
+ }
+ if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
+ span_lint(
+ cx,
+ MISSING_SAFETY_DOC,
+ span,
+ "unsafe function's docs miss `# Safety` section",
+ );
+ }
+ if !headers.panics && panic_span.is_some() {
+ span_lint_and_note(
+ cx,
+ MISSING_PANICS_DOC,
+ span,
+ "docs for function which may panic missing `# Panics` section",
+ panic_span,
+ "first possible panic found here",
+ );
+ }
+ if !headers.errors {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
+ if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
+ span_lint(
+ cx,
+ MISSING_ERRORS_DOC,
+ span,
+ "docs for function returning `Result` missing `# Errors` section",
+ );
+ } else {
+ if_chain! {
+ if let Some(body_id) = body_id;
+ if let Some(future) = cx.tcx.lang_items().future_trait();
+ let typeck = cx.tcx.typeck_body(body_id);
+ let body = cx.tcx.hir().body(body_id);
+ let ret_ty = typeck.expr_ty(&body.value);
+ if implements_trait(cx, ret_ty, future, &[]);
+ if let ty::Opaque(_, subs) = ret_ty.kind();
+ if let Some(gen) = subs.types().next();
+ if let ty::Generator(_, subs, _) = gen.kind();
+ if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
+ then {
+ span_lint(
+ cx,
+ MISSING_ERRORS_DOC,
+ span,
+ "docs for function returning `Result` missing `# Errors` section",
+ );
+ }
+ }
+ }
+ }
+}
+
+/// Cleanup documentation decoration.
+///
+/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
+/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
+/// need to keep track of
+/// the spans but this function is inspired from the later.
+#[allow(clippy::cast_possible_truncation)]
+#[must_use]
+pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
+ // one-line comments lose their prefix
+ if comment_kind == CommentKind::Line {
+ let mut doc = doc.to_owned();
+ doc.push('\n');
+ let len = doc.len();
+ // +3 skips the opening delimiter
+ return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
+ }
+
+ let mut sizes = vec![];
+ let mut contains_initial_stars = false;
+ for line in doc.lines() {
+ let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
+ debug_assert_eq!(offset as u32 as usize, offset);
+ contains_initial_stars |= line.trim_start().starts_with('*');
+ // +1 adds the newline, +3 skips the opening delimiter
+ sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
+ }
+ if !contains_initial_stars {
+ return (doc.to_string(), sizes);
+ }
+ // remove the initial '*'s if any
+ let mut no_stars = String::with_capacity(doc.len());
+ for line in doc.lines() {
+ let mut chars = line.chars();
+ for c in &mut chars {
+ if c.is_whitespace() {
+ no_stars.push(c);
+ } else {
+ no_stars.push(if c == '*' { ' ' } else { c });
+ break;
+ }
+ }
+ no_stars.push_str(chars.as_str());
+ no_stars.push('\n');
+ }
+
+ (no_stars, sizes)
+}
+
+#[derive(Copy, Clone)]
+struct DocHeaders {
+ safety: bool,
+ errors: bool,
+ panics: bool,
+}
+
+fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
++ use pulldown_cmark::{BrokenLink, CowStr, Options};
++ /// We don't want the parser to choke on intra doc links. Since we don't
++ /// actually care about rendering them, just pretend that all broken links are
++ /// point to a fake address.
++ #[allow(clippy::unnecessary_wraps)] // we're following a type signature
++ fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
++ Some(("fake".into(), "fake".into()))
++ }
++
+ let mut doc = String::new();
+ let mut spans = vec![];
+
+ for attr in attrs {
+ if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
+ let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
+ spans.extend_from_slice(¤t_spans);
+ doc.push_str(&comment);
+ } else if attr.has_name(sym::doc) {
+ // ignore mix of sugared and non-sugared doc
+ // don't trigger the safety or errors check
+ return DocHeaders {
+ safety: true,
+ errors: true,
+ panics: true,
+ };
+ }
+ }
+
+ let mut current = 0;
+ for &mut (ref mut offset, _) in &mut spans {
+ let offset_copy = *offset;
+ *offset = current;
+ current += offset_copy;
+ }
+
+ if doc.is_empty() {
+ return DocHeaders {
+ safety: false,
+ errors: false,
+ panics: false,
+ };
+ }
+
++ let mut cb = fake_broken_link_callback;
++
++ let parser =
++ pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
+ // Iterate over all `Events` and combine consecutive events into one
+ let events = parser.coalesce(|previous, current| {
+ use pulldown_cmark::Event::Text;
+
+ let previous_range = previous.1;
+ let current_range = current.1;
+
+ match (previous.0, current.0) {
+ (Text(previous), Text(current)) => {
+ let mut previous = previous.to_string();
+ previous.push_str(¤t);
+ Ok((Text(previous.into()), previous_range))
+ },
+ (previous, current) => Err(((previous, previous_range), (current, current_range))),
+ }
+ });
+ check_doc(cx, valid_idents, events, &spans)
+}
+
+const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
+
+fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
+ cx: &LateContext<'_>,
+ valid_idents: &FxHashSet<String>,
+ events: Events,
+ spans: &[(usize, Span)],
+) -> DocHeaders {
+ // true if a safety header was found
+ use pulldown_cmark::Event::{
+ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
+ };
+ use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
+ use pulldown_cmark::{CodeBlockKind, CowStr};
+
+ let mut headers = DocHeaders {
+ safety: false,
+ errors: false,
+ panics: false,
+ };
+ let mut in_code = false;
+ let mut in_link = None;
+ let mut in_heading = false;
+ let mut is_rust = false;
+ let mut edition = None;
+ let mut ticks_unbalanced = false;
+ let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
+ let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
+ for (event, range) in events {
+ match event {
+ Start(CodeBlock(ref kind)) => {
+ in_code = true;
+ if let CodeBlockKind::Fenced(lang) = kind {
+ for item in lang.split(',') {
+ if item == "ignore" {
+ is_rust = false;
+ break;
+ }
+ if let Some(stripped) = item.strip_prefix("edition") {
+ is_rust = true;
+ edition = stripped.parse::<Edition>().ok();
+ } else if item.is_empty() || RUST_CODE.contains(&item) {
+ is_rust = true;
+ }
+ }
+ }
+ },
+ End(CodeBlock(_)) => {
+ in_code = false;
+ is_rust = false;
+ },
+ Start(Link(_, url, _)) => in_link = Some(url),
+ End(Link(..)) => in_link = None,
+ Start(Heading(_) | Paragraph | Item) => {
+ if let Start(Heading(_)) = event {
+ in_heading = true;
+ }
+ ticks_unbalanced = false;
+ let (_, span) = get_current_span(spans, range.start);
+ paragraph_span = first_line_of_span(cx, span);
+ },
+ End(Heading(_) | Paragraph | Item) => {
+ if let End(Heading(_)) = event {
+ in_heading = false;
+ }
+ if ticks_unbalanced {
+ span_lint_and_help(
+ cx,
+ DOC_MARKDOWN,
+ paragraph_span,
+ "backticks are unbalanced",
+ None,
+ "a backtick may be missing a pair",
+ );
+ } else {
+ for (text, span) in text_to_check {
+ check_text(cx, valid_idents, &text, span);
+ }
+ }
+ text_to_check = Vec::new();
+ },
+ Start(_tag) | End(_tag) => (), // We don't care about other tags
+ Html(_html) => (), // HTML is weird, just ignore it
+ SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
+ FootnoteReference(text) | Text(text) => {
+ let (begin, span) = get_current_span(spans, range.start);
+ paragraph_span = paragraph_span.with_hi(span.hi());
+ ticks_unbalanced |= text.contains('`') && !in_code;
+ if Some(&text) == in_link.as_ref() || ticks_unbalanced {
+ // Probably a link of the form `<http://example.com>`
+ // Which are represented as a link to "http://example.com" with
+ // text "http://example.com" by pulldown-cmark
+ continue;
+ }
+ headers.safety |= in_heading && text.trim() == "Safety";
+ headers.errors |= in_heading && text.trim() == "Errors";
+ headers.panics |= in_heading && text.trim() == "Panics";
+ if in_code {
+ if is_rust {
+ let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
+ check_code(cx, &text, edition, span);
+ }
+ } else {
+ // Adjust for the beginning of the current `Event`
+ let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
+ text_to_check.push((text, span));
+ }
+ },
+ }
+ }
+ headers
+}
+
+fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
+ let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
+ Ok(o) => o,
+ Err(e) => e - 1,
+ };
+ spans[index]
+}
+
+fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
+ fn has_needless_main(code: String, edition: Edition) -> bool {
+ rustc_driver::catch_fatal_errors(|| {
+ rustc_span::create_session_globals_then(edition, || {
+ let filename = FileName::anon_source_code(&code);
+
+ let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+ let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false);
+ let handler = Handler::with_emitter(false, None, Box::new(emitter));
+ let sess = ParseSess::with_span_handler(handler, sm);
+
+ let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
+ Ok(p) => p,
+ Err(errs) => {
+ for mut err in errs {
+ err.cancel();
+ }
+ return false;
+ },
+ };
+
+ let mut relevant_main_found = false;
+ loop {
+ match parser.parse_item(ForceCollect::No) {
+ Ok(Some(item)) => match &item.kind {
+ // Tests with one of these items are ignored
+ ItemKind::Static(..)
+ | ItemKind::Const(..)
+ | ItemKind::ExternCrate(..)
+ | ItemKind::ForeignMod(..) => return false,
+ // We found a main function ...
+ ItemKind::Fn(box FnKind(_, sig, _, Some(block))) if item.ident.name == sym::main => {
+ let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
+ let returns_nothing = match &sig.decl.output {
+ FnRetTy::Default(..) => true,
+ FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
+ FnRetTy::Ty(_) => false,
+ };
+
+ if returns_nothing && !is_async && !block.stmts.is_empty() {
+ // This main function should be linted, but only if there are no other functions
+ relevant_main_found = true;
+ } else {
+ // This main function should not be linted, we're done
+ return false;
+ }
+ },
+ // Another function was found; this case is ignored too
+ ItemKind::Fn(..) => return false,
+ _ => {},
+ },
+ Ok(None) => break,
+ Err(mut e) => {
+ e.cancel();
+ return false;
+ },
+ }
+ }
+
+ relevant_main_found
+ })
+ })
+ .ok()
+ .unwrap_or_default()
+ }
+
+ // Because of the global session, we need to create a new session in a different thread with
+ // the edition we need.
+ let text = text.to_owned();
+ if thread::spawn(move || has_needless_main(text, edition))
+ .join()
+ .expect("thread::spawn failed")
+ {
+ span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
+ }
+}
+
+fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
+ for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
+ // Trim punctuation as in `some comment (see foo::bar).`
+ // ^^
+ // Or even as in `_foo bar_` which is emphasized.
+ let word = word.trim_matches(|c: char| !c.is_alphanumeric());
+
+ if valid_idents.contains(word) {
+ continue;
+ }
+
+ // Adjust for the current word
+ let offset = word.as_ptr() as usize - text.as_ptr() as usize;
+ let span = Span::new(
+ span.lo() + BytePos::from_usize(offset),
+ span.lo() + BytePos::from_usize(offset + word.len()),
+ span.ctxt(),
+ span.parent(),
+ );
+
+ check_word(cx, word, span);
+ }
+}
+
+fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
+ /// Checks if a string is camel-case, i.e., contains at least two uppercase
+ /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
+ /// Plurals are also excluded (`IDs` is ok).
+ fn is_camel_case(s: &str) -> bool {
+ if s.starts_with(|c: char| c.is_digit(10)) {
+ return false;
+ }
+
+ let s = s.strip_suffix('s').unwrap_or(s);
+
+ s.chars().all(char::is_alphanumeric)
+ && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
+ && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
+ }
+
+ fn has_underscore(s: &str) -> bool {
+ s != "_" && !s.contains("\\_") && s.contains('_')
+ }
+
+ fn has_hyphen(s: &str) -> bool {
+ s != "-" && s.contains('-')
+ }
+
+ if let Ok(url) = Url::parse(word) {
+ // try to get around the fact that `foo::bar` parses as a valid URL
+ if !url.cannot_be_a_base() {
+ span_lint(
+ cx,
+ DOC_MARKDOWN,
+ span,
+ "you should put bare URLs between `<`/`>` or make a proper Markdown link",
+ );
+
+ return;
+ }
+ }
+
+ // We assume that mixed-case words are not meant to be put inside bacticks. (Issue #2343)
+ if has_underscore(word) && has_hyphen(word) {
+ return;
+ }
+
+ if has_underscore(word) || word.contains("::") || is_camel_case(word) {
+ span_lint(
+ cx,
+ DOC_MARKDOWN,
+ span,
+ &format!("you should put `{}` between ticks in the documentation", word),
+ );
+ }
+}
+
+struct FindPanicUnwrap<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ panic_span: Option<Span>,
+ typeck_results: &'tcx ty::TypeckResults<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.panic_span.is_some() {
+ return;
+ }
+
+ // check for `begin_panic`
+ if_chain! {
+ if let ExprKind::Call(func_expr, _) = expr.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
+ if let Some(path_def_id) = path.res.opt_def_id();
+ if match_panic_def_id(self.cx, path_def_id);
+ if is_expn_of(expr.span, "unreachable").is_none();
+ if !is_expn_of_debug_assertions(expr.span);
+ then {
+ self.panic_span = Some(expr.span);
+ }
+ }
+
+ // check for `assert_eq` or `assert_ne`
+ if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() {
+ self.panic_span = Some(expr.span);
+ }
+
+ // check for `unwrap`
+ if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
+ let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
+ if is_type_diagnostic_item(self.cx, reciever_ty, sym::Option)
+ || is_type_diagnostic_item(self.cx, reciever_ty, sym::Result)
+ {
+ self.panic_span = Some(expr.span);
+ }
+ }
+
+ // and check sub-expressions
+ intravisit::walk_expr(self, expr);
+ }
+
+ // Panics in const blocks will cause compilation to fail.
+ fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+}
+
+fn is_expn_of_debug_assertions(span: Span) -> bool {
+ const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
+ MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some())
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::ty::implements_trait;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::Ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// ### What it does
++ /// Checks for pattern matchings that can be expressed using equality.
++ ///
++ /// ### Why is this bad?
++ ///
++ /// * It reads better and has less cognitive load because equality won't cause binding.
++ /// * It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely
++ /// criticized for increasing the cognitive load of reading the code.
++ /// * Equality is a simple bool expression and can be merged with `&&` and `||` and
++ /// reuse if blocks
++ ///
++ /// ### Example
++ /// ```rust,ignore
++ /// if let Some(2) = x {
++ /// do_thing();
++ /// }
++ /// ```
++ /// Should be written
++ /// ```rust,ignore
++ /// if x == Some(2) {
++ /// do_thing();
++ /// }
++ /// ```
++ pub EQUATABLE_IF_LET,
++ nursery,
++ "using pattern matching instead of equality"
++}
++
++declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]);
++
++/// detects if pattern matches just one thing
++fn unary_pattern(pat: &Pat<'_>) -> bool {
++ fn array_rec(pats: &[Pat<'_>]) -> bool {
++ pats.iter().all(unary_pattern)
++ }
++ match &pat.kind {
++ PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => {
++ false
++ },
++ PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
++ PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => !etc.is_some() && array_rec(a),
++ PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
++ PatKind::Path(_) | PatKind::Lit(_) => true,
++ }
++}
++
++fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
++ if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
++ implements_trait(cx, ty, def_id, &[other.into()])
++ } else {
++ false
++ }
++}
++
++impl<'tcx> LateLintPass<'tcx> for PatternEquality {
++ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
++ if_chain! {
++ if let ExprKind::Let(pat, exp, _) = expr.kind;
++ if unary_pattern(pat);
++ let exp_ty = cx.typeck_results().expr_ty(exp);
++ let pat_ty = cx.typeck_results().pat_ty(pat);
++ if is_structural_partial_eq(cx, exp_ty, pat_ty);
++ then {
++
++ let mut applicability = Applicability::MachineApplicable;
++ let pat_str = match pat.kind {
++ PatKind::Struct(..) => format!(
++ "({})",
++ snippet_with_applicability(cx, pat.span, "..", &mut applicability),
++ ),
++ _ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(),
++ };
++ span_lint_and_sugg(
++ cx,
++ EQUATABLE_IF_LET,
++ expr.span,
++ "this pattern matching can be expressed using equality",
++ "try",
++ format!(
++ "{} == {}",
++ snippet_with_applicability(cx, exp.span, "..", &mut applicability),
++ pat_str,
++ ),
++ applicability,
++ );
++ }
++ }
++ }
++}
--- /dev/null
- if let Some(Constant::Int(0)) = constant_simple(cx, cx.typeck_results(), e) {
+use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for erasing operations, e.g., `x * 0`.
+ ///
+ /// ### Why is this bad?
+ /// The whole expression can be replaced by zero.
+ /// This is most likely not the intended outcome and should probably be
+ /// corrected
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = 1;
+ /// 0 / x;
+ /// 0 * x;
+ /// x & 0;
+ /// ```
+ pub ERASING_OP,
+ correctness,
+ "using erasing operations, e.g., `x * 0` or `y & 0`"
+}
+
+declare_lint_pass!(ErasingOp => [ERASING_OP]);
+
+impl<'tcx> LateLintPass<'tcx> for ErasingOp {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if e.span.from_expansion() {
+ return;
+ }
+ if let ExprKind::Binary(ref cmp, left, right) = e.kind {
+ match cmp.node {
+ BinOpKind::Mul | BinOpKind::BitAnd => {
+ check(cx, left, e.span);
+ check(cx, right, e.span);
+ },
+ BinOpKind::Div => check(cx, left, e.span),
+ _ => (),
+ }
+ }
+ }
+}
+
+fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) {
++ if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) {
+ span_lint(
+ cx,
+ ERASING_OP,
+ span,
+ "this operation will always return zero. This is likely not the intended outcome",
+ );
+ }
+}
--- /dev/null
- if let AssocItemKind::Fn { has_self: true } = trait_item.kind {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::contains_ty;
+use rustc_hir::intravisit;
+use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::mir::FakeReadCause;
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::{self, TraitRef, Ty};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+use rustc_span::symbol::kw;
+use rustc_target::spec::abi::Abi;
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+
+#[derive(Copy, Clone)]
+pub struct BoxedLocal {
+ pub too_large_for_stack: u64,
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `Box<T>` where an unboxed `T` would
+ /// work fine.
+ ///
+ /// ### Why is this bad?
+ /// This is an unnecessary allocation, and bad for
+ /// performance. It is only necessary to allocate if you wish to move the box
+ /// into something.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn foo(bar: usize) {}
+ /// // Bad
+ /// let x = Box::new(1);
+ /// foo(*x);
+ /// println!("{}", *x);
+ ///
+ /// // Good
+ /// let x = 1;
+ /// foo(x);
+ /// println!("{}", x);
+ /// ```
+ pub BOXED_LOCAL,
+ perf,
+ "using `Box<T>` where unnecessary"
+}
+
+fn is_non_trait_box(ty: Ty<'_>) -> bool {
+ ty.is_box() && !ty.boxed_ty().is_trait()
+}
+
+struct EscapeDelegate<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ set: HirIdSet,
+ trait_self_ty: Option<Ty<'tcx>>,
+ too_large_for_stack: u64,
+}
+
+impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
+
+impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ fn_kind: intravisit::FnKind<'tcx>,
+ _: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ _: Span,
+ hir_id: HirId,
+ ) {
+ if let Some(header) = fn_kind.header() {
+ if header.abi != Abi::Rust {
+ return;
+ }
+ }
+
+ let parent_id = cx.tcx.hir().get_parent_item(hir_id);
+ let parent_node = cx.tcx.hir().find(parent_id);
+
+ let mut trait_self_ty = None;
+ if let Some(Node::Item(item)) = parent_node {
+ // If the method is an impl for a trait, don't warn.
+ if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind {
+ return;
+ }
+
+ // find `self` ty for this trait if relevant
+ if let ItemKind::Trait(_, _, _, _, items) = item.kind {
+ for trait_item in items {
+ if trait_item.id.hir_id() == hir_id {
+ // be sure we have `self` parameter in this function
++ if trait_item.kind == (AssocItemKind::Fn { has_self: true }) {
+ trait_self_ty = Some(
+ TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id())
+ .self_ty()
+ .skip_binder(),
+ );
+ }
+ }
+ }
+ }
+ }
+
+ let mut v = EscapeDelegate {
+ cx,
+ set: HirIdSet::default(),
+ trait_self_ty,
+ too_large_for_stack: self.too_large_for_stack,
+ };
+
+ let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
+ });
+
+ for node in v.set {
+ span_lint(
+ cx,
+ BOXED_LOCAL,
+ cx.tcx.hir().span(node),
+ "local variable doesn't need to be boxed here",
+ );
+ }
+ }
+}
+
+// TODO: Replace with Map::is_argument(..) when it's fixed
+fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
+ match map.find(id) {
+ Some(Node::Binding(_)) => (),
+ _ => return false,
+ }
+
+ matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_)))
+}
+
+impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
+ fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
+ if cmt.place.projections.is_empty() {
+ if let PlaceBase::Local(lid) = cmt.place.base {
+ self.set.remove(&lid);
+ let map = &self.cx.tcx.hir();
+ if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
+ if self.set.contains(&lid) {
+ // let y = x where x is known
+ // remove x, insert y
+ self.set.insert(cmt.hir_id);
+ self.set.remove(&lid);
+ }
+ }
+ }
+ }
+ }
+
+ fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
+ if cmt.place.projections.is_empty() {
+ if let PlaceBase::Local(lid) = cmt.place.base {
+ self.set.remove(&lid);
+ }
+ }
+ }
+
+ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
+ if cmt.place.projections.is_empty() {
+ let map = &self.cx.tcx.hir();
+ if is_argument(*map, cmt.hir_id) {
+ // Skip closure arguments
+ let parent_id = map.get_parent_node(cmt.hir_id);
+ if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
+ return;
+ }
+
+ // skip if there is a `self` parameter binding to a type
+ // that contains `Self` (i.e.: `self: Box<Self>`), see #4804
+ if let Some(trait_self_ty) = self.trait_self_ty {
+ if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(self.cx.tcx, cmt.place.ty(), trait_self_ty)
+ {
+ return;
+ }
+ }
+
+ if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
+ self.set.insert(cmt.hir_id);
+ }
+ }
+ }
+ }
+
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
+impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
+ fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
+ // Large types need to be boxed to avoid stack overflows.
+ if ty.is_box() {
+ self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
+ } else {
+ false
+ }
+ }
+}
--- /dev/null
- if let ClosureKind::FnMut = substs.as_closure().kind();
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher::VecArgs;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::usage::UsedAfterExprVisitor;
+use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for closures which just call another function where
+ /// the function can be called directly. `unsafe` functions or calls where types
+ /// get adjusted are ignored.
+ ///
+ /// ### Why is this bad?
+ /// Needlessly creating a closure adds code for no benefit
+ /// and gives the optimizer more work.
+ ///
+ /// ### Known problems
+ /// If creating the closure inside the closure has a side-
+ /// effect then moving the closure creation out will change when that side-
+ /// effect runs.
+ /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// // Bad
+ /// xs.map(|x| foo(x))
+ ///
+ /// // Good
+ /// xs.map(foo)
+ /// ```
+ /// where `foo(_)` is a plain function that takes the exact argument type of
+ /// `x`.
+ pub REDUNDANT_CLOSURE,
+ style,
+ "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for closures which only invoke a method on the closure
+ /// argument and can be replaced by referencing the method directly.
+ ///
+ /// ### Why is this bad?
+ /// It's unnecessary to create the closure.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// Some('a').map(|s| s.to_uppercase());
+ /// ```
+ /// may be rewritten as
+ /// ```rust,ignore
+ /// Some('a').map(char::to_uppercase);
+ /// ```
+ pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+ pedantic,
+ "redundant closures for method calls"
+}
+
+declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
+
+impl<'tcx> LateLintPass<'tcx> for EtaReduction {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+ let body = match expr.kind {
+ ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id),
+ _ => return,
+ };
+ if body.value.span.from_expansion() {
+ if body.params.is_empty() {
+ if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
+ // replace `|| vec![]` with `Vec::new`
+ span_lint_and_sugg(
+ cx,
+ REDUNDANT_CLOSURE,
+ expr.span,
+ "redundant closure",
+ "replace the closure with `Vec::new`",
+ "std::vec::Vec::new".into(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ // skip `foo(|| macro!())`
+ return;
+ }
+
+ let closure_ty = cx.typeck_results().expr_ty(expr);
+
+ if_chain!(
+ if let ExprKind::Call(callee, args) = body.value.kind;
+ if let ExprKind::Path(_) = callee.kind;
+ if check_inputs(cx, body.params, args);
+ let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
+ let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
+ .map_or(callee_ty, |id| cx.tcx.type_of(id));
+ if check_sig(cx, closure_ty, call_ty);
+ let substs = cx.typeck_results().node_substs(callee.hir_id);
+ // This fixes some false positives that I don't entirely understand
+ if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
+ // A type param function ref like `T::f` is not 'static, however
+ // it is if cast like `T::f as fn()`. This seems like a rustc bug.
+ if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
+ then {
+ span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
+ if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+ if_chain! {
+ if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
++ if substs.as_closure().kind() == ClosureKind::FnMut;
+ if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
+ || UsedAfterExprVisitor::is_found(cx, callee);
+
+ then {
+ // Mutable closure is used after current expr; we cannot consume it.
+ snippet = format!("&mut {}", snippet);
+ }
+ }
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the function itself",
+ snippet,
+ Applicability::MachineApplicable,
+ );
+ }
+ });
+ }
+ );
+
+ if_chain!(
+ if let ExprKind::MethodCall(path, _, args, _) = body.value.kind;
+ if check_inputs(cx, body.params, args);
+ let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
+ let substs = cx.typeck_results().node_substs(body.value.hir_id);
+ let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
+ if check_sig(cx, closure_ty, call_ty);
+ then {
+ span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
+ let name = get_ufcs_type_name(cx, method_def_id);
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the method itself",
+ format!("{}::{}", name, path.ident.name),
+ Applicability::MachineApplicable,
+ );
+ })
+ }
+ );
+ }
+}
+
+fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
+ if params.len() != call_args.len() {
+ return false;
+ }
+ std::iter::zip(params, call_args).all(|(param, arg)| {
+ match param.pat.kind {
+ PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
+ _ => return false,
+ }
+ match *cx.typeck_results().expr_adjustments(arg) {
+ [] => true,
+ [Adjustment {
+ kind: Adjust::Deref(None),
+ ..
+ }, Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
+ ..
+ }] => {
+ // re-borrow with the same mutability is allowed
+ let ty = cx.typeck_results().expr_ty(arg);
+ matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
+ },
+ _ => false,
+ }
+ })
+}
+
+fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
+ let call_sig = call_ty.fn_sig(cx.tcx);
+ if call_sig.unsafety() == Unsafety::Unsafe {
+ return false;
+ }
+ if !closure_ty.has_late_bound_regions() {
+ return true;
+ }
+ let substs = match closure_ty.kind() {
+ ty::Closure(_, substs) => substs,
+ _ => return false,
+ };
+ let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
+ cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
+}
+
+fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
+ match cx.tcx.associated_item(method_def_id).container {
+ ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id),
+ ty::ImplContainer(def_id) => {
+ let ty = cx.tcx.type_of(def_id);
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did),
+ _ => ty.to_string(),
+ }
+ },
+ }
+}
--- /dev/null
- if let ty::Never = self.cx.tcx.erase_late_bound_regions(sig).output().kind() {
+use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
+use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
+use if_chain::if_chain;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for a read and a write to the same variable where
+ /// whether the read occurs before or after the write depends on the evaluation
+ /// order of sub-expressions.
+ ///
+ /// ### Why is this bad?
+ /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
+ /// the operands of these expressions are evaluated before applying the effects of the expression.
+ ///
+ /// ### Known problems
+ /// Code which intentionally depends on the evaluation
+ /// order, or which is correct for any evaluation order.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut x = 0;
+ ///
+ /// // Bad
+ /// let a = {
+ /// x = 1;
+ /// 1
+ /// } + x;
+ /// // Unclear whether a is 1 or 2.
+ ///
+ /// // Good
+ /// let tmp = {
+ /// x = 1;
+ /// 1
+ /// };
+ /// let a = tmp + x;
+ /// ```
+ pub EVAL_ORDER_DEPENDENCE,
+ suspicious,
+ "whether a variable read occurs before a write depends on sub-expression evaluation order"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for diverging calls that are not match arms or
+ /// statements.
+ ///
+ /// ### Why is this bad?
+ /// It is often confusing to read. In addition, the
+ /// sub-expression evaluation order for Rust is not well documented.
+ ///
+ /// ### Known problems
+ /// Someone might want to use `some_bool || panic!()` as a
+ /// shorthand.
+ ///
+ /// ### Example
+ /// ```rust,no_run
+ /// # fn b() -> bool { true }
+ /// # fn c() -> bool { true }
+ /// let a = b() || panic!() || c();
+ /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
+ /// let x = (a, b, c, panic!());
+ /// // can simply be replaced by `panic!()`
+ /// ```
+ pub DIVERGING_SUB_EXPRESSION,
+ complexity,
+ "whether an expression contains a diverging sub expression"
+}
+
+declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]);
+
+impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // Find a write to a local variable.
+ let var = if_chain! {
+ if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind;
+ if let Some(var) = path_to_local(lhs);
+ if expr.span.desugaring_kind().is_none();
+ then { var } else { return; }
+ };
+ let mut visitor = ReadVisitor {
+ cx,
+ var,
+ write_expr: expr,
+ last_expr: expr,
+ };
+ check_for_unsequenced_reads(&mut visitor);
+ }
+ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+ match stmt.kind {
+ StmtKind::Local(local) => {
+ if let Local { init: Some(e), .. } = local {
+ DivergenceVisitor { cx }.visit_expr(e);
+ }
+ },
+ StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e),
+ StmtKind::Item(..) => {},
+ }
+ }
+}
+
+struct DivergenceVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
+ fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
+ match e.kind {
+ ExprKind::Closure(..) => {},
+ ExprKind::Match(e, arms, _) => {
+ self.visit_expr(e);
+ for arm in arms {
+ if let Some(Guard::If(if_expr)) = arm.guard {
+ self.visit_expr(if_expr);
+ }
+ // make sure top level arm expressions aren't linted
+ self.maybe_walk_expr(&*arm.body);
+ }
+ },
+ _ => walk_expr(self, e),
+ }
+ }
+ fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
+ span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ match e.kind {
+ ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
+ ExprKind::Call(func, _) => {
+ let typ = self.cx.typeck_results().expr_ty(func);
+ match typ.kind() {
+ ty::FnDef(..) | ty::FnPtr(_) => {
+ let sig = typ.fn_sig(self.cx.tcx);
++ if self.cx.tcx.erase_late_bound_regions(sig).output().kind() == &ty::Never {
+ self.report_diverging_sub_expr(e);
+ }
+ },
+ _ => {},
+ }
+ },
+ ExprKind::MethodCall(..) => {
+ let borrowed_table = self.cx.typeck_results();
+ if borrowed_table.expr_ty(e).is_never() {
+ self.report_diverging_sub_expr(e);
+ }
+ },
+ _ => {
+ // do not lint expressions referencing objects of type `!`, as that required a
+ // diverging expression
+ // to begin with
+ },
+ }
+ self.maybe_walk_expr(e);
+ }
+ fn visit_block(&mut self, _: &'tcx Block<'_>) {
+ // don't continue over blocks, LateLintPass already does that
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Walks up the AST from the given write expression (`vis.write_expr`) looking
+/// for reads to the same variable that are unsequenced relative to the write.
+///
+/// This means reads for which there is a common ancestor between the read and
+/// the write such that
+///
+/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x`
+/// and `|| x = 1` don't necessarily evaluate `x`), and
+///
+/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s,
+/// loops, `match`es, and the short-circuiting logical operators are considered to have a defined
+/// evaluation order.
+///
+/// When such a read is found, the lint is triggered.
+fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
+ let map = &vis.cx.tcx.hir();
+ let mut cur_id = vis.write_expr.hir_id;
+ loop {
+ let parent_id = map.get_parent_node(cur_id);
+ if parent_id == cur_id {
+ break;
+ }
+ let parent_node = match map.find(parent_id) {
+ Some(parent) => parent,
+ None => break,
+ };
+
+ let stop_early = match parent_node {
+ Node::Expr(expr) => check_expr(vis, expr),
+ Node::Stmt(stmt) => check_stmt(vis, stmt),
+ Node::Item(_) => {
+ // We reached the top of the function, stop.
+ break;
+ },
+ _ => StopEarly::KeepGoing,
+ };
+ match stop_early {
+ StopEarly::Stop => break,
+ StopEarly::KeepGoing => {},
+ }
+
+ cur_id = parent_id;
+ }
+}
+
+/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If
+/// `check_expr` weren't an independent function, this would be unnecessary and
+/// we could just use `break`).
+enum StopEarly {
+ KeepGoing,
+ Stop,
+}
+
+fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
+ if expr.hir_id == vis.last_expr.hir_id {
+ return StopEarly::KeepGoing;
+ }
+
+ match expr.kind {
+ ExprKind::Array(_)
+ | ExprKind::Tup(_)
+ | ExprKind::MethodCall(..)
+ | ExprKind::Call(_, _)
+ | ExprKind::Assign(..)
+ | ExprKind::Index(_, _)
+ | ExprKind::Repeat(_, _)
+ | ExprKind::Struct(_, _, _) => {
+ walk_expr(vis, expr);
+ },
+ ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
+ if op.node == BinOpKind::And || op.node == BinOpKind::Or {
+ // x && y and x || y always evaluate x first, so these are
+ // strictly sequenced.
+ } else {
+ walk_expr(vis, expr);
+ }
+ },
+ ExprKind::Closure(_, _, _, _, _) => {
+ // Either
+ //
+ // * `var` is defined in the closure body, in which case we've reached the top of the enclosing
+ // function and can stop, or
+ //
+ // * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate
+ // its body, we don't necessarily have a write, so we need to stop to avoid generating false
+ // positives.
+ //
+ // This is also the only place we need to stop early (grrr).
+ return StopEarly::Stop;
+ },
+ // All other expressions either have only one child or strictly
+ // sequence the evaluation order of their sub-expressions.
+ _ => {},
+ }
+
+ vis.last_expr = expr;
+
+ StopEarly::KeepGoing
+}
+
+fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
+ match stmt.kind {
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
+ // If the declaration is of a local variable, check its initializer
+ // expression if it has one. Otherwise, keep going.
+ StmtKind::Local(local) => local
+ .init
+ .as_ref()
+ .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
+ StmtKind::Item(..) => StopEarly::KeepGoing,
+ }
+}
+
+/// A visitor that looks for reads from a variable.
+struct ReadVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ /// The ID of the variable we're looking for.
+ var: HirId,
+ /// The expressions where the write to the variable occurred (for reporting
+ /// in the lint).
+ write_expr: &'tcx Expr<'tcx>,
+ /// The last (highest in the AST) expression we've checked, so we know not
+ /// to recheck it.
+ last_expr: &'tcx Expr<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if expr.hir_id == self.last_expr.hir_id {
+ return;
+ }
+
+ if path_to_local_id(expr, self.var) {
+ // Check that this is a read, not a write.
+ if !is_in_assignment_position(self.cx, expr) {
+ span_lint_and_note(
+ self.cx,
+ EVAL_ORDER_DEPENDENCE,
+ expr.span,
+ &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)),
+ Some(self.write_expr.span),
+ "whether read occurs before this write depends on evaluation order",
+ );
+ }
+ }
+ match expr.kind {
+ // We're about to descend a closure. Since we don't know when (or
+ // if) the closure will be evaluated, any reads in it might not
+ // occur here (or ever). Like above, bail to avoid false positives.
+ ExprKind::Closure(_, _, _, _, _) |
+
+ // We want to avoid a false positive when a variable name occurs
+ // only to have its address taken, so we stop here. Technically,
+ // this misses some weird cases, eg.
+ //
+ // ```rust
+ // let mut x = 0;
+ // let a = foo(&{x = 1; x}, x);
+ // ```
+ //
+ // TODO: fix this
+ ExprKind::AddrOf(_, _, _) => {
+ return;
+ }
+ _ => {}
+ }
+
+ walk_expr(self, expr);
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
+fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let Some(parent) = get_parent_expr(cx, expr) {
+ if let ExprKind::Assign(lhs, ..) = parent.kind {
+ return lhs.hir_id == expr.hir_id;
+ }
+ }
+ false
+}
--- /dev/null
- if t.ident.name.as_str() == "Display";
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `format!("string literal with no
+ /// argument")` and `format!("{}", foo)` where `foo` is a string.
+ ///
+ /// ### Why is this bad?
+ /// There is no point of doing that. `format!("foo")` can
+ /// be replaced by `"foo".to_owned()` if you really need a `String`. The even
+ /// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
+ /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
+ /// if `foo: &str`.
+ ///
+ /// ### Examples
+ /// ```rust
+ ///
+ /// // Bad
+ /// let foo = "foo";
+ /// format!("{}", foo);
+ ///
+ /// // Good
+ /// foo.to_owned();
+ /// ```
+ pub USELESS_FORMAT,
+ complexity,
+ "useless use of `format!`"
+}
+
+declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
+
+impl<'tcx> LateLintPass<'tcx> for UselessFormat {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
+ Some(e) if !e.call_site.from_expansion() => e,
+ _ => return,
+ };
+
+ let mut applicability = Applicability::MachineApplicable;
+ if format_args.value_args.is_empty() {
+ if_chain! {
+ if let [e] = &*format_args.format_string_parts;
+ if let ExprKind::Lit(lit) = &e.kind;
+ if let Some(s_src) = snippet_opt(cx, lit.span);
+ then {
+ // Simulate macro expansion, converting {{ and }} to { and }.
+ let s_expand = s_src.replace("{{", "{").replace("}}", "}");
+ let sugg = format!("{}.to_string()", s_expand);
+ span_useless_format(cx, call_site, sugg, applicability);
+ }
+ }
+ } else if let [value] = *format_args.value_args {
+ if_chain! {
+ if format_args.format_string_symbols == [kw::Empty];
+ if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did),
+ ty::Str => true,
+ _ => false,
+ };
+ if format_args.args.iter().all(is_display_arg);
+ if format_args.fmt_expr.map_or(true, check_unformatted);
+ then {
+ let is_new_string = match value.kind {
+ ExprKind::Binary(..) => true,
+ ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
+ _ => false,
+ };
+ let sugg = if is_new_string {
+ snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+ } else {
+ let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+ format!("{}.to_string()", sugg.maybe_par())
+ };
+ span_useless_format(cx, call_site, sugg, applicability);
+ }
+ }
+ };
+ }
+}
+
+fn span_useless_format(cx: &LateContext<'_>, span: Span, mut sugg: String, mut applicability: Applicability) {
+ // The callsite span contains the statement semicolon for some reason.
+ if snippet_with_applicability(cx, span, "..", &mut applicability).ends_with(';') {
+ sugg.push(';');
+ }
+
+ span_lint_and_sugg(
+ cx,
+ USELESS_FORMAT,
+ span,
+ "useless use of `format!`",
+ "consider using `.to_string()`",
+ sugg,
+ applicability,
+ );
+}
+
+fn is_display_arg(expr: &Expr<'_>) -> bool {
+ if_chain! {
+ if let ExprKind::Call(_, [_, fmt]) = expr.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind;
+ if let [.., t, _] = path.segments;
++ if t.ident.name == sym::Display;
+ then { true } else { false }
+ }
+}
+
+/// Checks if the expression matches
+/// ```rust,ignore
+/// &[_ {
+/// format: _ {
+/// width: _::Implied,
+/// precision: _::Implied,
+/// ...
+/// },
+/// ...,
+/// }]
+/// ```
+fn check_unformatted(expr: &Expr<'_>) -> bool {
+ if_chain! {
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
+ if let ExprKind::Array([expr]) = expr.kind;
+ // struct `core::fmt::rt::v1::Argument`
+ if let ExprKind::Struct(_, fields, _) = expr.kind;
+ if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
+ // struct `core::fmt::rt::v1::FormatSpec`
+ if let ExprKind::Struct(_, fields, _) = format_field.expr.kind;
+ if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision);
+ if let ExprKind::Path(ref precision_path) = precision_field.expr.kind;
+ if last_path_segment(precision_path).ident.name == sym::Implied;
+ if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width);
+ if let ExprKind::Path(ref width_qpath) = width_field.expr.kind;
+ if last_path_segment(width_qpath).ident.name == sym::Implied;
+ then {
+ return true;
+ }
+ }
+
+ false
+}
--- /dev/null
- use if_chain::if_chain;
+use clippy_utils::source::snippet;
- if_chain! {
- if let BinOpKind::Shl = cmp.node;
- if let Some(Constant::Int(0)) = constant_simple(cx, cx.typeck_results(), right);
- if let Some(Constant::Int(1)) = constant_simple(cx, cx.typeck_results(), left);
- then {
- return true;
- }
- }
-
- false
+use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{clip, unsext};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for identity operations, e.g., `x + 0`.
+ ///
+ /// ### Why is this bad?
+ /// This code can be removed without changing the
+ /// meaning. So it just obscures what's going on. Delete it mercilessly.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = 1;
+ /// x / 1 + 0 * 1 - 0 | 0;
+ /// ```
+ pub IDENTITY_OP,
+ complexity,
+ "using identity operations, e.g., `x + 0` or `y / 1`"
+}
+
+declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
+
+impl<'tcx> LateLintPass<'tcx> for IdentityOp {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if e.span.from_expansion() {
+ return;
+ }
+ if let ExprKind::Binary(cmp, left, right) = e.kind {
+ if is_allowed(cx, cmp, left, right) {
+ return;
+ }
+ match cmp.node {
+ BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
+ check(cx, left, 0, e.span, right.span);
+ check(cx, right, 0, e.span, left.span);
+ },
+ BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
+ BinOpKind::Mul => {
+ check(cx, left, 1, e.span, right.span);
+ check(cx, right, 1, e.span, left.span);
+ },
+ BinOpKind::Div => check(cx, right, 1, e.span, left.span),
+ BinOpKind::BitAnd => {
+ check(cx, left, -1, e.span, right.span);
+ check(cx, right, -1, e.span, left.span);
+ },
+ _ => (),
+ }
+ }
+ }
+}
+
+fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+ // `1 << 0` is a common pattern in bit manipulation code
++ cmp.node == BinOpKind::Shl
++ && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
++ && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))
+}
+
+#[allow(clippy::cast_possible_wrap)]
+fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
+ if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) {
+ let check = match *cx.typeck_results().expr_ty(e).kind() {
+ ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
+ ty::Uint(uty) => clip(cx.tcx, !0, uty),
+ _ => return,
+ };
+ if match m {
+ 0 => v == 0,
+ -1 => v == check,
+ 1 => v == 1,
+ _ => unreachable!(),
+ } {
+ span_lint(
+ cx,
+ IDENTITY_OP,
+ span,
+ &format!(
+ "the operation is ineffective. Consider reducing it to `{}`",
+ snippet(cx, arg, "..")
+ ),
+ );
+ }
+ }
+}
--- /dev/null
- use clippy_utils::is_expn_of;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::PanicExpn;
-
- let cond_sugg =
- if let ExprKind::DropTemps(Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..}) = cond.kind {
- snippet_with_applicability(cx, not_expr.span, "..", &mut applicability).to_string()
+use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{is_expn_of, sugg};
+use rustc_errors::Applicability;
+use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects `if`-then-`panic!` that can be replaced with `assert!`.
+ ///
+ /// ### Why is this bad?
+ /// `assert!` is simpler than `if`-then-`panic!`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let sad_people: Vec<&str> = vec![];
+ /// if !sad_people.is_empty() {
+ /// panic!("there are sad people: {:?}", sad_people);
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let sad_people: Vec<&str> = vec![];
+ /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
+ /// ```
+ pub IF_THEN_PANIC,
+ style,
+ "`panic!` and only a `panic!` in `if`-then statement"
+}
+
+declare_lint_pass!(IfThenPanic => [IF_THEN_PANIC]);
+
+impl LateLintPass<'_> for IfThenPanic {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let Expr {
+ kind: ExprKind:: If(cond, Expr {
+ kind: ExprKind::Block(
+ Block {
+ stmts: [stmt],
+ ..
+ },
+ _),
+ ..
+ }, None),
+ ..
+ } = &expr;
+ if is_expn_of(stmt.span, "panic").is_some();
+ if !matches!(cond.kind, ExprKind::Let(_, _, _));
+ if let StmtKind::Semi(semi) = stmt.kind;
+ if !cx.tcx.sess.source_map().is_multiline(cond.span);
+
+ then {
+ let span = if let Some(panic_expn) = PanicExpn::parse(semi) {
+ match *panic_expn.format_args.value_args {
+ [] => panic_expn.format_args.format_string_span,
+ [.., last] => panic_expn.format_args.format_string_span.to(last.span),
+ }
+ } else {
+ if_chain! {
+ if let ExprKind::Block(block, _) = semi.kind;
+ if let Some(init) = block.expr;
+ if let ExprKind::Call(_, [format_args]) = init.kind;
+
+ then {
+ format_args.span
+ } else {
+ return
+ }
+ }
+ };
+ let mut applicability = Applicability::MachineApplicable;
+ let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
- format!("!{}", snippet_with_applicability(cx, cond.span, "..", &mut applicability))
++ let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind {
++ if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e {
++ sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string()
++ } else {
++ format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par().to_string())
++ }
+ } else {
++ format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par().to_string())
+ };
+
+ span_lint_and_sugg(
+ cx,
+ IF_THEN_PANIC,
+ expr.span,
+ "only a `panic!` in `if`-then statement",
+ "try",
+ format!("assert!({}, {});", cond_sugg, sugg),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
--- /dev/null
- let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span))
- .and_then(|snip| {
- let i = snip.find("fn")?;
- Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32))
- })
- .expect("failed to create span for type parameters");
+use std::borrow::Cow;
+use std::collections::BTreeMap;
+
+use rustc_errors::DiagnosticBuilder;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, NestedVisitorMap, Visitor};
+use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{Ty, TyS, TypeckResults};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::symbol::sym;
+use rustc_typeck::hir_ty_to_ty;
+
+use if_chain::if_chain;
+
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::differing_macro_contexts;
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for public `impl` or `fn` missing generalization
+ /// over different hashers and implicitly defaulting to the default hashing
+ /// algorithm (`SipHash`).
+ ///
+ /// ### Why is this bad?
+ /// `HashMap` or `HashSet` with custom hashers cannot be
+ /// used with them.
+ ///
+ /// ### Known problems
+ /// Suggestions for replacing constructors can contain
+ /// false-positives. Also applying suggestions can require modification of other
+ /// pieces of code, possibly including external crates.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::collections::HashMap;
+ /// # use std::hash::{Hash, BuildHasher};
+ /// # trait Serialize {};
+ /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
+ ///
+ /// pub fn foo(map: &mut HashMap<i32, i32>) { }
+ /// ```
+ /// could be rewritten as
+ /// ```rust
+ /// # use std::collections::HashMap;
+ /// # use std::hash::{Hash, BuildHasher};
+ /// # trait Serialize {};
+ /// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
+ ///
+ /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
+ /// ```
+ pub IMPLICIT_HASHER,
+ pedantic,
+ "missing generalization over different hashers"
+}
+
+declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
+
+impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
+ #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ use rustc_span::BytePos;
+
+ fn suggestion<'tcx>(
+ cx: &LateContext<'tcx>,
+ diag: &mut DiagnosticBuilder<'_>,
+ generics_span: Span,
+ generics_suggestion_span: Span,
+ target: &ImplicitHasherType<'_>,
+ vis: ImplicitHasherConstructorVisitor<'_, '_, '_>,
+ ) {
+ let generics_snip = snippet(cx, generics_span, "");
+ // trim `<` `>`
+ let generics_snip = if generics_snip.is_empty() {
+ ""
+ } else {
+ &generics_snip[1..generics_snip.len() - 1]
+ };
+
+ multispan_sugg(
+ diag,
+ "consider adding a type parameter",
+ vec![
+ (
+ generics_suggestion_span,
+ format!(
+ "<{}{}S: ::std::hash::BuildHasher{}>",
+ generics_snip,
+ if generics_snip.is_empty() { "" } else { ", " },
+ if vis.suggestions.is_empty() {
+ ""
+ } else {
+ // request users to add `Default` bound so that generic constructors can be used
+ " + Default"
+ },
+ ),
+ ),
+ (
+ target.span(),
+ format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
+ ),
+ ],
+ );
+
+ if !vis.suggestions.is_empty() {
+ multispan_sugg(diag, "...and use generic constructor", vis.suggestions);
+ }
+ }
+
+ if !cx.access_levels.is_exported(item.def_id) {
+ return;
+ }
+
+ match item.kind {
+ ItemKind::Impl(ref impl_) => {
+ let mut vis = ImplicitHasherTypeVisitor::new(cx);
+ vis.visit_ty(impl_.self_ty);
+
+ for target in &vis.found {
+ if differing_macro_contexts(item.span, target.span()) {
+ return;
+ }
+
+ let generics_suggestion_span = impl_.generics.span.substitute_dummy({
+ let pos = snippet_opt(cx, item.span.until(target.span()))
+ .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
+ if let Some(pos) = pos {
+ Span::new(pos, pos, item.span.ctxt(), item.span.parent())
+ } else {
+ return;
+ }
+ });
+
+ let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
+ for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) {
+ ctr_vis.visit_impl_item(item);
+ }
+
+ span_lint_and_then(
+ cx,
+ IMPLICIT_HASHER,
+ target.span(),
+ &format!(
+ "impl for `{}` should be generalized over different hashers",
+ target.type_name()
+ ),
+ move |diag| {
+ suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis);
+ },
+ );
+ }
+ },
+ ItemKind::Fn(ref sig, ref generics, body_id) => {
+ let body = cx.tcx.hir().body(body_id);
+
+ for ty in sig.decl.inputs {
+ let mut vis = ImplicitHasherTypeVisitor::new(cx);
+ vis.visit_ty(ty);
+
+ for target in &vis.found {
+ if in_external_macro(cx.sess(), generics.span) {
+ continue;
+ }
+ let generics_suggestion_span = generics.span.substitute_dummy({
++ let pos = snippet_opt(
++ cx,
++ Span::new(
++ item.span.lo(),
++ body.params[0].pat.span.lo(),
++ item.span.ctxt(),
++ item.span.parent(),
++ ),
++ )
++ .and_then(|snip| {
++ let i = snip.find("fn")?;
++ Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32))
++ })
++ .expect("failed to create span for type parameters");
+ Span::new(pos, pos, item.span.ctxt(), item.span.parent())
+ });
+
+ let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
+ ctr_vis.visit_body(body);
+
+ span_lint_and_then(
+ cx,
+ IMPLICIT_HASHER,
+ target.span(),
+ &format!(
+ "parameter of type `{}` should be generalized over different hashers",
+ target.type_name()
+ ),
+ move |diag| {
+ suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
+ },
+ );
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+}
+
+enum ImplicitHasherType<'tcx> {
+ HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>),
+ HashSet(Span, Ty<'tcx>, Cow<'static, str>),
+}
+
+impl<'tcx> ImplicitHasherType<'tcx> {
+ /// Checks that `ty` is a target type without a `BuildHasher`.
+ fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> {
+ if let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind {
+ let params: Vec<_> = path
+ .segments
+ .last()
+ .as_ref()?
+ .args
+ .as_ref()?
+ .args
+ .iter()
+ .filter_map(|arg| match arg {
+ GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ })
+ .collect();
+ let params_len = params.len();
+
+ let ty = hir_ty_to_ty(cx.tcx, hir_ty);
+
+ if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 {
+ Some(ImplicitHasherType::HashMap(
+ hir_ty.span,
+ ty,
+ snippet(cx, params[0].span, "K"),
+ snippet(cx, params[1].span, "V"),
+ ))
+ } else if is_type_diagnostic_item(cx, ty, sym::HashSet) && params_len == 1 {
+ Some(ImplicitHasherType::HashSet(
+ hir_ty.span,
+ ty,
+ snippet(cx, params[0].span, "T"),
+ ))
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ }
+
+ fn type_name(&self) -> &'static str {
+ match *self {
+ ImplicitHasherType::HashMap(..) => "HashMap",
+ ImplicitHasherType::HashSet(..) => "HashSet",
+ }
+ }
+
+ fn type_arguments(&self) -> String {
+ match *self {
+ ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v),
+ ImplicitHasherType::HashSet(.., ref t) => format!("{}", t),
+ }
+ }
+
+ fn ty(&self) -> Ty<'tcx> {
+ match *self {
+ ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty,
+ }
+ }
+
+ fn span(&self) -> Span {
+ match *self {
+ ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span,
+ }
+ }
+}
+
+struct ImplicitHasherTypeVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ found: Vec<ImplicitHasherType<'tcx>>,
+}
+
+impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
+ fn new(cx: &'a LateContext<'tcx>) -> Self {
+ Self { cx, found: vec![] }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
+ if let Some(target) = ImplicitHasherType::new(self.cx, t) {
+ self.found.push(target);
+ }
+
+ walk_ty(self, t);
+ }
+
+ fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
+ if let Some(target) = ImplicitHasherType::new(self.cx, &inf.to_ty()) {
+ self.found.push(target);
+ }
+
+ walk_inf(self, inf);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Looks for default-hasher-dependent constructors like `HashMap::new`.
+struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+ target: &'b ImplicitHasherType<'tcx>,
+ suggestions: BTreeMap<Span, String>,
+}
+
+impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
+ fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self {
+ Self {
+ cx,
+ maybe_typeck_results: cx.maybe_typeck_results(),
+ target,
+ suggestions: BTreeMap::new(),
+ }
+ }
+}
+
+impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_body(&mut self, body: &'tcx Body<'_>) {
+ let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id()));
+ walk_body(self, body);
+ self.maybe_typeck_results = old_maybe_typeck_results;
+ }
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Call(fun, args) = e.kind;
+ if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
+ if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
+ if let Some(ty_did) = ty_path.res.opt_def_id();
+ then {
+ if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) {
+ return;
+ }
+
+ if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) {
+ if method.ident.name == sym::new {
+ self.suggestions
+ .insert(e.span, "HashMap::default()".to_string());
+ } else if method.ident.name == sym!(with_capacity) {
+ self.suggestions.insert(
+ e.span,
+ format!(
+ "HashMap::with_capacity_and_hasher({}, Default::default())",
+ snippet(self.cx, args[0].span, "capacity"),
+ ),
+ );
+ }
+ } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) {
+ if method.ident.name == sym::new {
+ self.suggestions
+ .insert(e.span, "HashSet::default()".to_string());
+ } else if method.ident.name == sym!(with_capacity) {
+ self.suggestions.insert(
+ e.span,
+ format!(
+ "HashSet::with_capacity_and_hasher({}, Default::default())",
+ snippet(self.cx, args[0].span, "capacity"),
+ ),
+ );
+ }
+ }
+ }
+ }
+
+ walk_expr(self, e);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+}
--- /dev/null
- if let hir::BinOpKind::Div = &binop.node;
+use clippy_utils::diagnostics::span_lint_and_help;
+use if_chain::if_chain;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for division of integers
+ ///
+ /// ### Why is this bad?
+ /// When outside of some very specific algorithms,
+ /// integer division is very often a mistake because it discards the
+ /// remainder.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let x = 3 / 2;
+ /// println!("{}", x);
+ ///
+ /// // Good
+ /// let x = 3f32 / 2f32;
+ /// println!("{}", x);
+ /// ```
+ pub INTEGER_DIVISION,
+ restriction,
+ "integer division may cause loss of precision"
+}
+
+declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]);
+
+impl<'tcx> LateLintPass<'tcx> for IntegerDivision {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if is_integer_division(cx, expr) {
+ span_lint_and_help(
+ cx,
+ INTEGER_DIVISION,
+ expr.span,
+ "integer division",
+ None,
+ "division of integers may cause loss of precision. consider using floats",
+ );
+ }
+ }
+}
+
+fn is_integer_division<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
+ if_chain! {
+ if let hir::ExprKind::Binary(binop, left, right) = &expr.kind;
++ if binop.node == hir::BinOpKind::Div;
+ then {
+ let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
+ return left_ty.is_integral() && right_ty.is_integral();
+ }
+ }
+
+ false
+}
--- /dev/null
- use clippy_utils::source::snippet_opt;
+//! lint when there is a large size difference between variants on an enum
+
+use clippy_utils::diagnostics::span_lint_and_then;
- use rustc_hir::{Item, ItemKind, VariantData};
++use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::Applicability;
-
- let mut largest_variant: Option<(_, _)> = None;
- let mut second_variant: Option<(_, _)> = None;
-
- for (i, variant) in adt.variants.iter().enumerate() {
- let size: u64 = variant
- .fields
- .iter()
- .filter_map(|f| {
- let ty = cx.tcx.type_of(f.did);
- // don't count generics by filtering out everything
- // that does not have a layout
- cx.layout_of(ty).ok().map(|l| l.size.bytes())
- })
- .sum();
-
- let grouped = (size, (i, variant));
-
- if grouped.0 >= largest_variant.map_or(0, |x| x.0) {
- second_variant = largest_variant;
- largest_variant = Some(grouped);
- }
++use rustc_hir::{Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for large size differences between variants on
+ /// `enum`s.
+ ///
+ /// ### Why is this bad?
+ /// Enum size is bounded by the largest variant. Having a
+ /// large variant can penalize the memory layout of that enum.
+ ///
+ /// ### Known problems
+ /// This lint obviously cannot take the distribution of
+ /// variants in your running program into account. It is possible that the
+ /// smaller variants make up less than 1% of all instances, in which case
+ /// the overhead is negligible and the boxing is counter-productive. Always
+ /// measure the change this lint suggests.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// enum Test {
+ /// A(i32),
+ /// B([i32; 8000]),
+ /// }
+ ///
+ /// // Possibly better
+ /// enum Test2 {
+ /// A(i32),
+ /// B(Box<[i32; 8000]>),
+ /// }
+ /// ```
+ pub LARGE_ENUM_VARIANT,
+ perf,
+ "large size difference between variants on an enum"
+}
+
+#[derive(Copy, Clone)]
+pub struct LargeEnumVariant {
+ maximum_size_difference_allowed: u64,
+}
+
+impl LargeEnumVariant {
+ #[must_use]
+ pub fn new(maximum_size_difference_allowed: u64) -> Self {
+ Self {
+ maximum_size_difference_allowed,
+ }
+ }
+}
+
++struct FieldInfo {
++ ind: usize,
++ size: u64,
++}
++
++struct VariantInfo {
++ ind: usize,
++ size: u64,
++ fields_size: Vec<FieldInfo>,
++}
++
+impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
+
+impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+ if in_external_macro(cx.tcx.sess, item.span) {
+ return;
+ }
+ if let ItemKind::Enum(ref def, _) = item.kind {
+ let ty = cx.tcx.type_of(item.def_id);
+ let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
- if let (Some(largest), Some(second)) = (largest_variant, second_variant) {
- let difference = largest.0 - second.0;
++ if adt.variants.len() <= 1 {
++ return;
+ }
++ let mut variants_size: Vec<VariantInfo> = adt
++ .variants
++ .iter()
++ .enumerate()
++ .map(|(i, variant)| {
++ let mut fields_size = Vec::new();
++ let size: u64 = variant
++ .fields
++ .iter()
++ .enumerate()
++ .filter_map(|(i, f)| {
++ let ty = cx.tcx.type_of(f.did);
++ // don't count generics by filtering out everything
++ // that does not have a layout
++ cx.layout_of(ty).ok().map(|l| {
++ let size = l.size.bytes();
++ fields_size.push(FieldInfo { ind: i, size });
++ size
++ })
++ })
++ .sum();
++ VariantInfo {
++ ind: i,
++ size,
++ fields_size,
++ }
++ })
++ .collect();
+
- if difference > self.maximum_size_difference_allowed {
- let (i, variant) = largest.1;
++ variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
+
- let help_text = "consider boxing the large fields to reduce the total size of the enum";
- span_lint_and_then(
- cx,
- LARGE_ENUM_VARIANT,
- def.variants[i].span,
- "large size difference between variants",
- |diag| {
- diag.span_label(
- def.variants[(largest.1).0].span,
- &format!("this variant is {} bytes", largest.0),
- );
- diag.span_note(
- def.variants[(second.1).0].span,
- &format!("and the second-largest variant is {} bytes:", second.0),
- );
- if variant.fields.len() == 1 {
- let span = match def.variants[i].data {
- VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) => {
- fields[0].ty.span
- },
- VariantData::Unit(..) => unreachable!(),
- };
- if let Some(snip) = snippet_opt(cx, span) {
- diag.span_suggestion(
- span,
- help_text,
- format!("Box<{}>", snip),
- Applicability::MaybeIncorrect,
- );
- return;
++ let mut difference = variants_size[0].size - variants_size[1].size;
++ if difference > self.maximum_size_difference_allowed {
++ let help_text = "consider boxing the large fields to reduce the total size of the enum";
++ span_lint_and_then(
++ cx,
++ LARGE_ENUM_VARIANT,
++ def.variants[variants_size[0].ind].span,
++ "large size difference between variants",
++ |diag| {
++ diag.span_label(
++ def.variants[variants_size[0].ind].span,
++ &format!("this variant is {} bytes", variants_size[0].size),
++ );
++ diag.span_note(
++ def.variants[variants_size[1].ind].span,
++ &format!("and the second-largest variant is {} bytes:", variants_size[1].size),
++ );
+
- }
- diag.span_help(def.variants[i].span, help_text);
- },
- );
- }
++ let fields = def.variants[variants_size[0].ind].data.fields();
++ variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
++ let mut applicability = Applicability::MaybeIncorrect;
++ let sugg: Vec<(Span, String)> = variants_size[0]
++ .fields_size
++ .iter()
++ .rev()
++ .map_while(|val| {
++ if difference > self.maximum_size_difference_allowed {
++ difference = difference.saturating_sub(val.size);
++ Some((
++ fields[val.ind].ty.span,
++ format!(
++ "Box<{}>",
++ snippet_with_applicability(
++ cx,
++ fields[val.ind].ty.span,
++ "..",
++ &mut applicability
++ )
++ .into_owned()
++ ),
++ ))
++ } else {
++ None
+ }
++ })
++ .collect();
++
++ if !sugg.is_empty() {
++ diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect);
++ return;
++ }
++
++ diag.span_help(def.variants[variants_size[0].ind].span, help_text);
++ },
++ );
+ }
+ }
+ }
+}
--- /dev/null
- if let ty::AssocKind::Fn = item.kind {
- if item.ident.name.as_str() == "is_empty" {
- let sig = cx.tcx.fn_sig(item.def_id);
- let ty = sig.skip_binder();
- ty.inputs().len() == 1
- } else {
- false
- }
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::def_id::DefIdSet;
+use rustc_hir::{
+ def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
+ ItemKind, Mutability, Node, TraitItemRef, TyKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, AssocKind, FnSig, Ty, TyS};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{
+ source_map::{Span, Spanned, Symbol},
+ symbol::sym,
+};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for getting the length of something via `.len()`
+ /// just to compare to zero, and suggests using `.is_empty()` where applicable.
+ ///
+ /// ### Why is this bad?
+ /// Some structures can answer `.is_empty()` much faster
+ /// than calculating their length. So it is good to get into the habit of using
+ /// `.is_empty()`, and having it is cheap.
+ /// Besides, it makes the intent clearer than a manual comparison in some contexts.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// if x.len() == 0 {
+ /// ..
+ /// }
+ /// if y.len() != 0 {
+ /// ..
+ /// }
+ /// ```
+ /// instead use
+ /// ```ignore
+ /// if x.is_empty() {
+ /// ..
+ /// }
+ /// if !y.is_empty() {
+ /// ..
+ /// }
+ /// ```
+ pub LEN_ZERO,
+ style,
+ "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for items that implement `.len()` but not
+ /// `.is_empty()`.
+ ///
+ /// ### Why is this bad?
+ /// It is good custom to have both methods, because for
+ /// some data structures, asking about the length will be a costly operation,
+ /// whereas `.is_empty()` can usually answer in constant time. Also it used to
+ /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
+ /// lint will ignore such entities.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// impl X {
+ /// pub fn len(&self) -> usize {
+ /// ..
+ /// }
+ /// }
+ /// ```
+ pub LEN_WITHOUT_IS_EMPTY,
+ style,
+ "traits or impls with a public `len` method but no corresponding `is_empty` method"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for comparing to an empty slice such as `""` or `[]`,
+ /// and suggests using `.is_empty()` where applicable.
+ ///
+ /// ### Why is this bad?
+ /// Some structures can answer `.is_empty()` much faster
+ /// than checking for equality. So it is good to get into the habit of using
+ /// `.is_empty()`, and having it is cheap.
+ /// Besides, it makes the intent clearer than a manual comparison in some contexts.
+ ///
+ /// ### Example
+ ///
+ /// ```ignore
+ /// if s == "" {
+ /// ..
+ /// }
+ ///
+ /// if arr == [] {
+ /// ..
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```ignore
+ /// if s.is_empty() {
+ /// ..
+ /// }
+ ///
+ /// if arr.is_empty() {
+ /// ..
+ /// }
+ /// ```
+ pub COMPARISON_TO_EMPTY,
+ style,
+ "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
+}
+
+declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
+
+impl<'tcx> LateLintPass<'tcx> for LenZero {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ if item.span.from_expansion() {
+ return;
+ }
+
+ if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind {
+ check_trait_items(cx, item, trait_items);
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
+ if_chain! {
+ if item.ident.name == sym::len;
+ if let ImplItemKind::Fn(sig, _) = &item.kind;
+ if sig.decl.implicit_self.has_implicit_self();
+ if cx.access_levels.is_exported(item.def_id);
+ if matches!(sig.decl.output, FnRetTy::Return(_));
+ if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id());
+ if imp.of_trait.is_none();
+ if let TyKind::Path(ty_path) = &imp.self_ty.kind;
+ if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id();
+ if let Some(local_id) = ty_id.as_local();
+ let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
+ if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
+ if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder());
+ then {
+ let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
+ Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
+ Some(Node::Item(x)) => match x.kind {
+ ItemKind::Struct(..) => (x.ident.name, "struct"),
+ ItemKind::Enum(..) => (x.ident.name, "enum"),
+ ItemKind::Union(..) => (x.ident.name, "union"),
+ _ => (x.ident.name, "type"),
+ }
+ _ => return,
+ };
+ check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind)
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
+ match cmp {
+ BinOpKind::Eq => {
+ check_cmp(cx, expr.span, left, right, "", 0); // len == 0
+ check_cmp(cx, expr.span, right, left, "", 0); // 0 == len
+ },
+ BinOpKind::Ne => {
+ check_cmp(cx, expr.span, left, right, "!", 0); // len != 0
+ check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len
+ },
+ BinOpKind::Gt => {
+ check_cmp(cx, expr.span, left, right, "!", 0); // len > 0
+ check_cmp(cx, expr.span, right, left, "", 1); // 1 > len
+ },
+ BinOpKind::Lt => {
+ check_cmp(cx, expr.span, left, right, "", 1); // len < 1
+ check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len
+ },
+ BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1
+ BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len
+ _ => (),
+ }
+ }
+ }
+}
+
+fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
+ fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
+ item.ident.name == name
+ && if let AssocItemKind::Fn { has_self } = item.kind {
+ has_self && { cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 }
+ } else {
+ false
+ }
+ }
+
+ // fill the set with current and super traits
+ fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
+ if set.insert(traitt) {
+ for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) {
+ fill_trait_set(supertrait, set, cx);
+ }
+ }
+ }
+
+ if cx.access_levels.is_exported(visited_trait.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
+ {
+ let mut current_and_super_traits = DefIdSet::default();
+ fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx);
+
+ let is_empty_method_found = current_and_super_traits
+ .iter()
+ .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order())
+ .any(|i| {
+ i.kind == ty::AssocKind::Fn
+ && i.fn_has_self_parameter
+ && i.ident.name == sym!(is_empty)
+ && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
+ });
+
+ if !is_empty_method_found {
+ span_lint(
+ cx,
+ LEN_WITHOUT_IS_EMPTY,
+ visited_trait.span,
+ &format!(
+ "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
+ visited_trait.ident.name
+ ),
+ );
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum LenOutput<'tcx> {
+ Integral,
+ Option(DefId),
+ Result(DefId, Ty<'tcx>),
+}
+fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
+ match *sig.output().kind() {
+ ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
+ ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => {
+ subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did))
+ },
+ ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did) => subs
+ .type_at(0)
+ .is_integral()
+ .then(|| LenOutput::Result(adt.did, subs.type_at(1))),
+ _ => None,
+ }
+}
+
+impl LenOutput<'_> {
+ fn matches_is_empty_output(self, ty: Ty<'_>) -> bool {
+ match (self, ty.kind()) {
+ (_, &ty::Bool) => true,
+ (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did => subs.type_at(0).is_bool(),
+ (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did => {
+ subs.type_at(0).is_bool() && TyS::same_type(subs.type_at(1), err_ty)
+ },
+ _ => false,
+ }
+ }
+
+ fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
+ let self_ref = match self_kind {
+ ImplicitSelfKind::ImmRef => "&",
+ ImplicitSelfKind::MutRef => "&mut ",
+ _ => "",
+ };
+ match self {
+ Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref),
+ Self::Option(_) => format!(
+ "expected signature: `({}self) -> bool` or `({}self) -> Option<bool>",
+ self_ref, self_ref
+ ),
+ Self::Result(..) => format!(
+ "expected signature: `({}self) -> bool` or `({}self) -> Result<bool>",
+ self_ref, self_ref
+ ),
+ }
+ }
+}
+
+/// Checks if the given signature matches the expectations for `is_empty`
+fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: LenOutput<'_>) -> bool {
+ match &**sig.inputs_and_output {
+ [arg, res] if len_output.matches_is_empty_output(res) => {
+ matches!(
+ (arg.kind(), self_kind),
+ (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
+ | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef)
+ ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut))
+ },
+ _ => false,
+ }
+}
+
+/// Checks if the given type has an `is_empty` method with the appropriate signature.
+fn check_for_is_empty(
+ cx: &LateContext<'_>,
+ span: Span,
+ self_kind: ImplicitSelfKind,
+ output: LenOutput<'_>,
+ impl_ty: DefId,
+ item_name: Symbol,
+ item_kind: &str,
+) {
+ let is_empty = Symbol::intern("is_empty");
+ let is_empty = cx
+ .tcx
+ .inherent_impls(impl_ty)
+ .iter()
+ .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty))
+ .find(|item| item.kind == AssocKind::Fn);
+
+ let (msg, is_empty_span, self_kind) = match is_empty {
+ None => (
+ format!(
+ "{} `{}` has a public `len` method, but no `is_empty` method",
+ item_kind,
+ item_name.as_str(),
+ ),
+ None,
+ None,
+ ),
+ Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => (
+ format!(
+ "{} `{}` has a public `len` method, but a private `is_empty` method",
+ item_kind,
+ item_name.as_str(),
+ ),
+ Some(cx.tcx.def_span(is_empty.def_id)),
+ None,
+ ),
+ Some(is_empty)
+ if !(is_empty.fn_has_self_parameter
+ && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) =>
+ {
+ (
+ format!(
+ "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
+ item_kind,
+ item_name.as_str(),
+ ),
+ Some(cx.tcx.def_span(is_empty.def_id)),
+ Some(self_kind),
+ )
+ },
+ Some(_) => return,
+ };
+
+ span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, &msg, |db| {
+ if let Some(span) = is_empty_span {
+ db.span_note(span, "`is_empty` defined here");
+ }
+ if let Some(self_kind) = self_kind {
+ db.note(&output.expected_sig(self_kind));
+ }
+ });
+}
+
+fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
+ if let (&ExprKind::MethodCall(method_path, _, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
+ // check if we are in an is_empty() method
+ if let Some(name) = get_item_name(cx, method) {
+ if name.as_str() == "is_empty" {
+ return;
+ }
+ }
+
+ check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to);
+ } else {
+ check_empty_expr(cx, span, method, lit, op);
+ }
+}
+
+fn check_len(
+ cx: &LateContext<'_>,
+ span: Span,
+ method_name: Symbol,
+ args: &[Expr<'_>],
+ lit: &LitKind,
+ op: &str,
+ compare_to: u32,
+) {
+ if let LitKind::Int(lit, _) = *lit {
+ // check if length is compared to the specified number
+ if lit != u128::from(compare_to) {
+ return;
+ }
+
+ if method_name == sym::len && args.len() == 1 && has_is_empty(cx, &args[0]) {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ LEN_ZERO,
+ span,
+ &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
+ &format!("using `{}is_empty` is clearer and more explicit", op),
+ format!(
+ "{}{}.is_empty()",
+ op,
+ snippet_with_applicability(cx, args[0].span, "_", &mut applicability)
+ ),
+ applicability,
+ );
+ }
+ }
+}
+
+fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
+ if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ COMPARISON_TO_EMPTY,
+ span,
+ "comparison to empty slice",
+ &format!("using `{}is_empty` is clearer and more explicit", op),
+ format!(
+ "{}{}.is_empty()",
+ op,
+ snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
+ ),
+ applicability,
+ );
+ }
+}
+
+fn is_empty_string(expr: &Expr<'_>) -> bool {
+ if let ExprKind::Lit(ref lit) = expr.kind {
+ if let LitKind::Str(lit, _) = lit.node {
+ let lit = lit.as_str();
+ return lit == "";
+ }
+ }
+ false
+}
+
+fn is_empty_array(expr: &Expr<'_>) -> bool {
+ if let ExprKind::Array(arr) = expr.kind {
+ return arr.is_empty();
+ }
+ false
+}
+
+/// Checks if this type has an `is_empty` method.
+fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
+ fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
++ if item.kind == ty::AssocKind::Fn && item.ident.name.as_str() == "is_empty" {
++ let sig = cx.tcx.fn_sig(item.def_id);
++ let ty = sig.skip_binder();
++ ty.inputs().len() == 1
+ } else {
+ false
+ }
+ }
+
+ /// Checks the inherent impl's items for an `is_empty(self)` method.
+ fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
+ cx.tcx.inherent_impls(id).iter().any(|imp| {
+ cx.tcx
+ .associated_items(*imp)
+ .in_definition_order()
+ .any(|item| is_is_empty(cx, item))
+ })
+ }
+
+ let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
+ match ty.kind() {
+ ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| {
+ cx.tcx
+ .associated_items(principal.def_id())
+ .in_definition_order()
+ .any(|item| is_is_empty(cx, item))
+ }),
+ ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
+ ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
+ ty::Array(..) | ty::Slice(..) | ty::Str => true,
+ _ => false,
+ }
+}
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++{
++ store.register_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::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::unused_collect",
++ "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
++ );
++ store.register_removed(
++ "clippy::replace_consts",
++ "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
++ );
++ store.register_removed(
++ "clippy::regex_macro",
++ "the regex! macro has been removed from the regex crate in 2018",
++ );
++ store.register_removed(
++ "clippy::find_map",
++ "this lint has been replaced by `manual_find_map`, a more specific lint",
++ );
++ store.register_removed(
++ "clippy::filter_map",
++ "this lint has been replaced by `manual_filter_map`, a more specific lint",
++ );
++ store.register_removed(
++ "clippy::pub_enum_variant_names",
++ "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
++ );
++ store.register_removed(
++ "clippy::wrong_pub_self_convention",
++ "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
++ );
++}
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::all", Some("clippy_all"), vec![
++ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
++ 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(async_yields_async::ASYNC_YIELDS_ASYNC),
++ LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
++ LintId::of(attrs::DEPRECATED_CFG_ATTR),
++ LintId::of(attrs::DEPRECATED_SEMVER),
++ LintId::of(attrs::MISMATCHED_TARGET_OS),
++ LintId::of(attrs::USELESS_ATTRIBUTE),
++ LintId::of(bit_mask::BAD_BIT_MASK),
++ LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
++ LintId::of(blacklisted_name::BLACKLISTED_NAME),
++ LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
++ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
++ LintId::of(booleans::LOGIC_BUG),
++ LintId::of(booleans::NONMINIMAL_BOOL),
++ LintId::of(casts::CAST_REF_TO_MUT),
++ LintId::of(casts::CHAR_LIT_AS_U8),
++ LintId::of(casts::FN_TO_NUMERIC_CAST),
++ LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
++ LintId::of(casts::UNNECESSARY_CAST),
++ LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
++ LintId::of(collapsible_if::COLLAPSIBLE_IF),
++ LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
++ LintId::of(comparison_chain::COMPARISON_CHAIN),
++ LintId::of(copies::IFS_SAME_COND),
++ LintId::of(copies::IF_SAME_THEN_ELSE),
++ LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
++ LintId::of(derivable_impls::DERIVABLE_IMPLS),
++ LintId::of(derive::DERIVE_HASH_XOR_EQ),
++ LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
++ 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_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_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
++ 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(from_over_into::FROM_OVER_INTO),
++ LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
++ LintId::of(functions::DOUBLE_MUST_USE),
++ LintId::of(functions::MUST_USE_UNIT),
++ LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
++ LintId::of(functions::RESULT_UNIT_ERR),
++ LintId::of(functions::TOO_MANY_ARGUMENTS),
++ LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
++ LintId::of(identity_op::IDENTITY_OP),
++ LintId::of(if_let_mutex::IF_LET_MUTEX),
++ LintId::of(if_then_panic::IF_THEN_PANIC),
++ 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::COMPARISON_TO_EMPTY),
++ LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
++ LintId::of(len_zero::LEN_ZERO),
++ LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
++ LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
++ LintId::of(lifetimes::NEEDLESS_LIFETIMES),
++ LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
++ LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
++ LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
++ LintId::of(loops::EMPTY_LOOP),
++ LintId::of(loops::EXPLICIT_COUNTER_LOOP),
++ LintId::of(loops::FOR_KV_MAP),
++ LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
++ LintId::of(loops::ITER_NEXT_LOOP),
++ LintId::of(loops::MANUAL_FLATTEN),
++ 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::SAME_ITEM_PUSH),
++ LintId::of(loops::SINGLE_ELEMENT_LOOP),
++ LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
++ LintId::of(loops::WHILE_LET_LOOP),
++ LintId::of(loops::WHILE_LET_ON_ITERATOR),
++ LintId::of(main_recursion::MAIN_RECURSION),
++ LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
++ LintId::of(manual_map::MANUAL_MAP),
++ LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
++ LintId::of(manual_strip::MANUAL_STRIP),
++ LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
++ 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_result_ok::MATCH_RESULT_OK),
++ LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
++ LintId::of(matches::MATCH_AS_REF),
++ LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
++ LintId::of(matches::MATCH_OVERLAPPING_ARM),
++ LintId::of(matches::MATCH_REF_PATS),
++ LintId::of(matches::MATCH_SINGLE_BINDING),
++ LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
++ 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::BIND_INSTEAD_OF_MAP),
++ LintId::of(methods::BYTES_NTH),
++ LintId::of(methods::CHARS_LAST_CMP),
++ LintId::of(methods::CHARS_NEXT_CMP),
++ LintId::of(methods::CLONE_DOUBLE_REF),
++ LintId::of(methods::CLONE_ON_COPY),
++ LintId::of(methods::EXPECT_FUN_CALL),
++ LintId::of(methods::EXTEND_WITH_DRAIN),
++ LintId::of(methods::FILTER_MAP_IDENTITY),
++ LintId::of(methods::FILTER_NEXT),
++ LintId::of(methods::FLAT_MAP_IDENTITY),
++ LintId::of(methods::INSPECT_FOR_EACH),
++ LintId::of(methods::INTO_ITER_ON_REF),
++ LintId::of(methods::ITERATOR_STEP_BY_ZERO),
++ LintId::of(methods::ITER_CLONED_COLLECT),
++ LintId::of(methods::ITER_COUNT),
++ LintId::of(methods::ITER_NEXT_SLICE),
++ LintId::of(methods::ITER_NTH),
++ LintId::of(methods::ITER_NTH_ZERO),
++ LintId::of(methods::ITER_SKIP_NEXT),
++ LintId::of(methods::MANUAL_FILTER_MAP),
++ LintId::of(methods::MANUAL_FIND_MAP),
++ LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
++ LintId::of(methods::MANUAL_SPLIT_ONCE),
++ LintId::of(methods::MANUAL_STR_REPEAT),
++ LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
++ LintId::of(methods::MAP_IDENTITY),
++ LintId::of(methods::NEW_RET_NO_SELF),
++ LintId::of(methods::OK_EXPECT),
++ LintId::of(methods::OPTION_AS_REF_DEREF),
++ LintId::of(methods::OPTION_FILTER_MAP),
++ LintId::of(methods::OPTION_MAP_OR_NONE),
++ LintId::of(methods::OR_FUN_CALL),
++ LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
++ LintId::of(methods::SEARCH_IS_SOME),
++ LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
++ LintId::of(methods::SINGLE_CHAR_ADD_STR),
++ LintId::of(methods::SINGLE_CHAR_PATTERN),
++ LintId::of(methods::SKIP_WHILE_NEXT),
++ LintId::of(methods::STRING_EXTEND_CHARS),
++ LintId::of(methods::SUSPICIOUS_MAP),
++ LintId::of(methods::SUSPICIOUS_SPLITN),
++ LintId::of(methods::UNINIT_ASSUMED_INIT),
++ LintId::of(methods::UNNECESSARY_FILTER_MAP),
++ LintId::of(methods::UNNECESSARY_FOLD),
++ LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
++ LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
++ 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::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_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_mutex_lock::MUT_MUTEX_LOCK),
++ LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
++ LintId::of(mutex_atomic::MUTEX_ATOMIC),
++ LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
++ LintId::of(needless_bool::BOOL_COMPARISON),
++ LintId::of(needless_bool::NEEDLESS_BOOL),
++ LintId::of(needless_borrow::NEEDLESS_BORROW),
++ LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++ LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
++ LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
++ LintId::of(needless_update::NEEDLESS_UPDATE),
++ LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
++ LintId::of(neg_multiply::NEG_MULTIPLY),
++ LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
++ LintId::of(no_effect::NO_EFFECT),
++ LintId::of(no_effect::UNNECESSARY_OPERATION),
++ LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
++ LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
++ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
++ LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
++ LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
++ LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
++ LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
++ LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
++ LintId::of(precedence::PRECEDENCE),
++ LintId::of(ptr::CMP_NULL),
++ LintId::of(ptr::INVALID_NULL_PTR_USAGE),
++ LintId::of(ptr::MUT_FROM_REF),
++ LintId::of(ptr::PTR_ARG),
++ LintId::of(ptr_eq::PTR_EQ),
++ LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
++ LintId::of(question_mark::QUESTION_MARK),
++ LintId::of(ranges::MANUAL_RANGE_CONTAINS),
++ LintId::of(ranges::RANGE_ZIP_WITH_LEN),
++ LintId::of(ranges::REVERSED_EMPTY_RANGES),
++ LintId::of(redundant_clone::REDUNDANT_CLONE),
++ LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
++ LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
++ LintId::of(redundant_slicing::REDUNDANT_SLICING),
++ LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
++ LintId::of(reference::DEREF_ADDROF),
++ LintId::of(reference::REF_IN_DEREF),
++ LintId::of(regex::INVALID_REGEX),
++ LintId::of(repeat_once::REPEAT_ONCE),
++ LintId::of(returns::LET_AND_RETURN),
++ LintId::of(returns::NEEDLESS_RETURN),
++ LintId::of(self_assignment::SELF_ASSIGNMENT),
++ LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
++ LintId::of(serde_api::SERDE_API_MISUSE),
++ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
++ LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
++ LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
++ LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
++ LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
++ LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
++ LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
++ LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
++ LintId::of(swap::ALMOST_SWAPPED),
++ LintId::of(swap::MANUAL_SWAP),
++ LintId::of(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(to_string_in_display::TO_STRING_IN_DISPLAY),
++ LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
++ LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
++ LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
++ LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
++ LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
++ LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
++ LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
++ LintId::of(transmute::TRANSMUTE_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::BORROWED_BOX),
++ LintId::of(types::BOX_COLLECTION),
++ LintId::of(types::REDUNDANT_ALLOCATION),
++ LintId::of(types::TYPE_COMPLEXITY),
++ LintId::of(types::VEC_BOX),
++ LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
++ LintId::of(unicode::INVISIBLE_CHARACTERS),
++ LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
++ LintId::of(unit_types::UNIT_ARG),
++ LintId::of(unit_types::UNIT_CMP),
++ LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
++ LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
++ LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
++ LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
++ LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
++ LintId::of(unused_unit::UNUSED_UNIT),
++ LintId::of(unwrap::PANICKING_UNWRAP),
++ LintId::of(unwrap::UNNECESSARY_UNWRAP),
++ LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
++ LintId::of(useless_conversion::USELESS_CONVERSION),
++ LintId::of(vec::USELESS_VEC),
++ LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
++ LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
++ 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),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
++ LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
++ LintId::of(feature_name::NEGATIVE_FEATURE_NAMES),
++ LintId::of(feature_name::REDUNDANT_FEATURE_NAMES),
++ LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
++ LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
++ LintId::of(attrs::DEPRECATED_CFG_ATTR),
++ LintId::of(booleans::NONMINIMAL_BOOL),
++ LintId::of(casts::CHAR_LIT_AS_U8),
++ LintId::of(casts::UNNECESSARY_CAST),
++ LintId::of(derivable_impls::DERIVABLE_IMPLS),
++ 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(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_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::MANUAL_FLATTEN),
++ LintId::of(loops::SINGLE_ELEMENT_LOOP),
++ LintId::of(loops::WHILE_LET_LOOP),
++ LintId::of(manual_strip::MANUAL_STRIP),
++ LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
++ 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::BIND_INSTEAD_OF_MAP),
++ LintId::of(methods::CLONE_ON_COPY),
++ LintId::of(methods::FILTER_MAP_IDENTITY),
++ LintId::of(methods::FILTER_NEXT),
++ LintId::of(methods::FLAT_MAP_IDENTITY),
++ LintId::of(methods::INSPECT_FOR_EACH),
++ LintId::of(methods::ITER_COUNT),
++ LintId::of(methods::MANUAL_FILTER_MAP),
++ LintId::of(methods::MANUAL_FIND_MAP),
++ LintId::of(methods::MANUAL_SPLIT_ONCE),
++ LintId::of(methods::MAP_IDENTITY),
++ LintId::of(methods::OPTION_AS_REF_DEREF),
++ LintId::of(methods::OPTION_FILTER_MAP),
++ LintId::of(methods::SEARCH_IS_SOME),
++ LintId::of(methods::SKIP_WHILE_NEXT),
++ LintId::of(methods::UNNECESSARY_FILTER_MAP),
++ LintId::of(methods::USELESS_ASREF),
++ LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
++ LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
++ LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
++ LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
++ LintId::of(needless_bool::BOOL_COMPARISON),
++ LintId::of(needless_bool::NEEDLESS_BOOL),
++ LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++ LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
++ LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
++ LintId::of(needless_update::NEEDLESS_UPDATE),
++ LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
++ LintId::of(no_effect::NO_EFFECT),
++ LintId::of(no_effect::UNNECESSARY_OPERATION),
++ LintId::of(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_ZIP_WITH_LEN),
++ LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
++ LintId::of(redundant_slicing::REDUNDANT_SLICING),
++ LintId::of(reference::DEREF_ADDROF),
++ LintId::of(reference::REF_IN_DEREF),
++ LintId::of(repeat_once::REPEAT_ONCE),
++ LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
++ LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
++ LintId::of(swap::MANUAL_SWAP),
++ LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
++ LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
++ LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
++ LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
++ LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
++ LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
++ LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
++ LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
++ LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
++ LintId::of(types::BORROWED_BOX),
++ LintId::of(types::TYPE_COMPLEXITY),
++ LintId::of(types::VEC_BOX),
++ LintId::of(unit_types::UNIT_ARG),
++ LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
++ LintId::of(unwrap::UNNECESSARY_UNWRAP),
++ LintId::of(useless_conversion::USELESS_CONVERSION),
++ LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
++ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
++ LintId::of(approx_const::APPROX_CONSTANT),
++ LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
++ LintId::of(attrs::DEPRECATED_SEMVER),
++ LintId::of(attrs::MISMATCHED_TARGET_OS),
++ LintId::of(attrs::USELESS_ATTRIBUTE),
++ LintId::of(bit_mask::BAD_BIT_MASK),
++ LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
++ LintId::of(booleans::LOGIC_BUG),
++ LintId::of(casts::CAST_REF_TO_MUT),
++ LintId::of(copies::IFS_SAME_COND),
++ LintId::of(copies::IF_SAME_THEN_ELSE),
++ LintId::of(derive::DERIVE_HASH_XOR_EQ),
++ LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
++ LintId::of(drop_forget_ref::DROP_COPY),
++ LintId::of(drop_forget_ref::DROP_REF),
++ LintId::of(drop_forget_ref::FORGET_COPY),
++ LintId::of(drop_forget_ref::FORGET_REF),
++ LintId::of(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::ITER_NEXT_LOOP),
++ LintId::of(loops::NEVER_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::SUSPICIOUS_SPLITN),
++ LintId::of(methods::UNINIT_ASSUMED_INIT),
++ LintId::of(methods::ZST_OFFSET),
++ LintId::of(minmax::MIN_MAX),
++ LintId::of(misc::CMP_NAN),
++ LintId::of(misc::MODULO_ONE),
++ LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
++ LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
++ LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
++ LintId::of(ptr::INVALID_NULL_PTR_USAGE),
++ LintId::of(ptr::MUT_FROM_REF),
++ LintId::of(ranges::REVERSED_EMPTY_RANGES),
++ LintId::of(regex::INVALID_REGEX),
++ LintId::of(self_assignment::SELF_ASSIGNMENT),
++ LintId::of(serde_api::SERDE_API_MISUSE),
++ LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
++ LintId::of(swap::ALMOST_SWAPPED),
++ LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
++ LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
++ LintId::of(transmute::WRONG_TRANSMUTE),
++ LintId::of(transmuting_null::TRANSMUTING_NULL),
++ LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
++ LintId::of(unicode::INVISIBLE_CHARACTERS),
++ LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
++ LintId::of(unit_types::UNIT_CMP),
++ LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
++ LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
++ LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
++ LintId::of(unwrap::PANICKING_UNWRAP),
++ LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
++ LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
++ 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::IF_CHAIN_STYLE),
++ LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
++ LintId::of(utils::internal_lints::INVALID_PATHS),
++ LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
++ LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
++ LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
++ LintId::of(utils::internal_lints::PRODUCE_ICE),
++ LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_lints(&[
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::CLIPPY_LINTS_INTERNAL,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::COMPILER_LINT_FUNCTIONS,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::DEFAULT_LINT,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::IF_CHAIN_STYLE,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::INTERNING_DEFINED_SYMBOL,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::INVALID_PATHS,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::LINT_WITHOUT_LINT_PASS,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::OUTER_EXPN_EXPN_DATA,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::PRODUCE_ICE,
++ #[cfg(feature = "internal-lints")]
++ utils::internal_lints::UNNECESSARY_SYMBOL_STR,
++ absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
++ approx_const::APPROX_CONSTANT,
++ arithmetic::FLOAT_ARITHMETIC,
++ arithmetic::INTEGER_ARITHMETIC,
++ as_conversions::AS_CONVERSIONS,
++ asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
++ asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
++ assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
++ assign_ops::ASSIGN_OP_PATTERN,
++ assign_ops::MISREFACTORED_ASSIGN_OP,
++ async_yields_async::ASYNC_YIELDS_ASYNC,
++ attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
++ attrs::DEPRECATED_CFG_ATTR,
++ attrs::DEPRECATED_SEMVER,
++ attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
++ attrs::INLINE_ALWAYS,
++ attrs::MISMATCHED_TARGET_OS,
++ attrs::USELESS_ATTRIBUTE,
++ await_holding_invalid::AWAIT_HOLDING_LOCK,
++ await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
++ bit_mask::BAD_BIT_MASK,
++ bit_mask::INEFFECTIVE_BIT_MASK,
++ bit_mask::VERBOSE_BIT_MASK,
++ blacklisted_name::BLACKLISTED_NAME,
++ blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
++ bool_assert_comparison::BOOL_ASSERT_COMPARISON,
++ booleans::LOGIC_BUG,
++ booleans::NONMINIMAL_BOOL,
++ bytecount::NAIVE_BYTECOUNT,
++ cargo_common_metadata::CARGO_COMMON_METADATA,
++ case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
++ casts::CAST_LOSSLESS,
++ casts::CAST_POSSIBLE_TRUNCATION,
++ casts::CAST_POSSIBLE_WRAP,
++ casts::CAST_PRECISION_LOSS,
++ casts::CAST_PTR_ALIGNMENT,
++ casts::CAST_REF_TO_MUT,
++ casts::CAST_SIGN_LOSS,
++ casts::CHAR_LIT_AS_U8,
++ casts::FN_TO_NUMERIC_CAST,
++ casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++ casts::PTR_AS_PTR,
++ casts::UNNECESSARY_CAST,
++ checked_conversions::CHECKED_CONVERSIONS,
++ cognitive_complexity::COGNITIVE_COMPLEXITY,
++ collapsible_if::COLLAPSIBLE_ELSE_IF,
++ collapsible_if::COLLAPSIBLE_IF,
++ collapsible_match::COLLAPSIBLE_MATCH,
++ comparison_chain::COMPARISON_CHAIN,
++ copies::BRANCHES_SHARING_CODE,
++ copies::IFS_SAME_COND,
++ copies::IF_SAME_THEN_ELSE,
++ copies::SAME_FUNCTIONS_IN_IF_CONDITION,
++ copy_iterator::COPY_ITERATOR,
++ create_dir::CREATE_DIR,
++ dbg_macro::DBG_MACRO,
++ default::DEFAULT_TRAIT_ACCESS,
++ default::FIELD_REASSIGN_WITH_DEFAULT,
++ default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
++ dereference::EXPLICIT_DEREF_METHODS,
++ derivable_impls::DERIVABLE_IMPLS,
++ derive::DERIVE_HASH_XOR_EQ,
++ derive::DERIVE_ORD_XOR_PARTIAL_ORD,
++ derive::EXPL_IMPL_CLONE_ON_COPY,
++ derive::UNSAFE_DERIVE_DESERIALIZE,
++ disallowed_method::DISALLOWED_METHOD,
++ disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
++ disallowed_type::DISALLOWED_TYPE,
++ doc::DOC_MARKDOWN,
++ doc::MISSING_ERRORS_DOC,
++ doc::MISSING_PANICS_DOC,
++ doc::MISSING_SAFETY_DOC,
++ doc::NEEDLESS_DOCTEST_MAIN,
++ double_comparison::DOUBLE_COMPARISONS,
++ double_parens::DOUBLE_PARENS,
++ 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,
++ eq_op::EQ_OP,
++ eq_op::OP_REF,
++ equatable_if_let::EQUATABLE_IF_LET,
++ 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,
++ exhaustive_items::EXHAUSTIVE_ENUMS,
++ exhaustive_items::EXHAUSTIVE_STRUCTS,
++ exit::EXIT,
++ explicit_write::EXPLICIT_WRITE,
++ fallible_impl_from::FALLIBLE_IMPL_FROM,
++ feature_name::NEGATIVE_FEATURE_NAMES,
++ feature_name::REDUNDANT_FEATURE_NAMES,
++ float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
++ 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,
++ from_over_into::FROM_OVER_INTO,
++ from_str_radix_10::FROM_STR_RADIX_10,
++ functions::DOUBLE_MUST_USE,
++ functions::MUST_USE_CANDIDATE,
++ functions::MUST_USE_UNIT,
++ functions::NOT_UNSAFE_PTR_ARG_DEREF,
++ functions::RESULT_UNIT_ERR,
++ functions::TOO_MANY_ARGUMENTS,
++ functions::TOO_MANY_LINES,
++ future_not_send::FUTURE_NOT_SEND,
++ get_last_with_len::GET_LAST_WITH_LEN,
++ identity_op::IDENTITY_OP,
++ if_let_mutex::IF_LET_MUTEX,
++ if_not_else::IF_NOT_ELSE,
++ if_then_panic::IF_THEN_PANIC,
++ if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
++ implicit_hasher::IMPLICIT_HASHER,
++ implicit_return::IMPLICIT_RETURN,
++ implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
++ inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
++ 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,
++ invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
++ items_after_statements::ITEMS_AFTER_STATEMENTS,
++ iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
++ large_const_arrays::LARGE_CONST_ARRAYS,
++ large_enum_variant::LARGE_ENUM_VARIANT,
++ large_stack_arrays::LARGE_STACK_ARRAYS,
++ len_zero::COMPARISON_TO_EMPTY,
++ len_zero::LEN_WITHOUT_IS_EMPTY,
++ len_zero::LEN_ZERO,
++ let_if_seq::USELESS_LET_IF_SEQ,
++ let_underscore::LET_UNDERSCORE_DROP,
++ let_underscore::LET_UNDERSCORE_LOCK,
++ let_underscore::LET_UNDERSCORE_MUST_USE,
++ lifetimes::EXTRA_UNUSED_LIFETIMES,
++ lifetimes::NEEDLESS_LIFETIMES,
++ literal_representation::DECIMAL_LITERAL_REPRESENTATION,
++ literal_representation::INCONSISTENT_DIGIT_GROUPING,
++ literal_representation::LARGE_DIGIT_GROUPS,
++ literal_representation::MISTYPED_LITERAL_SUFFIXES,
++ literal_representation::UNREADABLE_LITERAL,
++ literal_representation::UNUSUAL_BYTE_GROUPINGS,
++ loops::EMPTY_LOOP,
++ loops::EXPLICIT_COUNTER_LOOP,
++ loops::EXPLICIT_INTO_ITER_LOOP,
++ loops::EXPLICIT_ITER_LOOP,
++ loops::FOR_KV_MAP,
++ loops::FOR_LOOPS_OVER_FALLIBLES,
++ loops::ITER_NEXT_LOOP,
++ loops::MANUAL_FLATTEN,
++ loops::MANUAL_MEMCPY,
++ loops::MUT_RANGE_BOUND,
++ loops::NEEDLESS_COLLECT,
++ loops::NEEDLESS_RANGE_LOOP,
++ loops::NEVER_LOOP,
++ loops::SAME_ITEM_PUSH,
++ loops::SINGLE_ELEMENT_LOOP,
++ loops::WHILE_IMMUTABLE_CONDITION,
++ loops::WHILE_LET_LOOP,
++ loops::WHILE_LET_ON_ITERATOR,
++ macro_use::MACRO_USE_IMPORTS,
++ main_recursion::MAIN_RECURSION,
++ manual_async_fn::MANUAL_ASYNC_FN,
++ manual_map::MANUAL_MAP,
++ manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
++ manual_ok_or::MANUAL_OK_OR,
++ manual_strip::MANUAL_STRIP,
++ manual_unwrap_or::MANUAL_UNWRAP_OR,
++ map_clone::MAP_CLONE,
++ map_err_ignore::MAP_ERR_IGNORE,
++ map_unit_fn::OPTION_MAP_UNIT_FN,
++ map_unit_fn::RESULT_MAP_UNIT_FN,
++ match_on_vec_items::MATCH_ON_VEC_ITEMS,
++ match_result_ok::MATCH_RESULT_OK,
++ matches::INFALLIBLE_DESTRUCTURING_MATCH,
++ matches::MATCH_AS_REF,
++ matches::MATCH_BOOL,
++ matches::MATCH_LIKE_MATCHES_MACRO,
++ matches::MATCH_OVERLAPPING_ARM,
++ matches::MATCH_REF_PATS,
++ matches::MATCH_SAME_ARMS,
++ matches::MATCH_SINGLE_BINDING,
++ matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
++ matches::MATCH_WILD_ERR_ARM,
++ matches::REDUNDANT_PATTERN_MATCHING,
++ 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::BIND_INSTEAD_OF_MAP,
++ methods::BYTES_NTH,
++ methods::CHARS_LAST_CMP,
++ methods::CHARS_NEXT_CMP,
++ methods::CLONED_INSTEAD_OF_COPIED,
++ methods::CLONE_DOUBLE_REF,
++ methods::CLONE_ON_COPY,
++ methods::CLONE_ON_REF_PTR,
++ methods::EXPECT_FUN_CALL,
++ methods::EXPECT_USED,
++ methods::EXTEND_WITH_DRAIN,
++ methods::FILETYPE_IS_FILE,
++ methods::FILTER_MAP_IDENTITY,
++ methods::FILTER_MAP_NEXT,
++ methods::FILTER_NEXT,
++ methods::FLAT_MAP_IDENTITY,
++ methods::FLAT_MAP_OPTION,
++ methods::FROM_ITER_INSTEAD_OF_COLLECT,
++ methods::GET_UNWRAP,
++ methods::IMPLICIT_CLONE,
++ methods::INEFFICIENT_TO_STRING,
++ methods::INSPECT_FOR_EACH,
++ methods::INTO_ITER_ON_REF,
++ methods::ITERATOR_STEP_BY_ZERO,
++ methods::ITER_CLONED_COLLECT,
++ methods::ITER_COUNT,
++ methods::ITER_NEXT_SLICE,
++ methods::ITER_NTH,
++ methods::ITER_NTH_ZERO,
++ methods::ITER_SKIP_NEXT,
++ methods::MANUAL_FILTER_MAP,
++ methods::MANUAL_FIND_MAP,
++ methods::MANUAL_SATURATING_ARITHMETIC,
++ methods::MANUAL_SPLIT_ONCE,
++ methods::MANUAL_STR_REPEAT,
++ methods::MAP_COLLECT_RESULT_UNIT,
++ methods::MAP_FLATTEN,
++ methods::MAP_IDENTITY,
++ methods::MAP_UNWRAP_OR,
++ methods::NEW_RET_NO_SELF,
++ methods::OK_EXPECT,
++ methods::OPTION_AS_REF_DEREF,
++ methods::OPTION_FILTER_MAP,
++ methods::OPTION_MAP_OR_NONE,
++ methods::OR_FUN_CALL,
++ methods::RESULT_MAP_OR_INTO_OPTION,
++ methods::SEARCH_IS_SOME,
++ methods::SHOULD_IMPLEMENT_TRAIT,
++ methods::SINGLE_CHAR_ADD_STR,
++ methods::SINGLE_CHAR_PATTERN,
++ methods::SKIP_WHILE_NEXT,
++ methods::STRING_EXTEND_CHARS,
++ methods::SUSPICIOUS_MAP,
++ methods::SUSPICIOUS_SPLITN,
++ methods::UNINIT_ASSUMED_INIT,
++ methods::UNNECESSARY_FILTER_MAP,
++ methods::UNNECESSARY_FOLD,
++ methods::UNNECESSARY_LAZY_EVALUATIONS,
++ methods::UNWRAP_OR_ELSE_DEFAULT,
++ methods::UNWRAP_USED,
++ methods::USELESS_ASREF,
++ 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_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_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
++ missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
++ module_style::MOD_MODULE_FILES,
++ module_style::SELF_NAMED_MODULE_FILES,
++ modulo_arithmetic::MODULO_ARITHMETIC,
++ multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
++ mut_key::MUTABLE_KEY_TYPE,
++ mut_mut::MUT_MUT,
++ mut_mutex_lock::MUT_MUTEX_LOCK,
++ mut_reference::UNNECESSARY_MUT_PASSED,
++ mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
++ mutex_atomic::MUTEX_ATOMIC,
++ mutex_atomic::MUTEX_INTEGER,
++ needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
++ needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
++ needless_bool::BOOL_COMPARISON,
++ needless_bool::NEEDLESS_BOOL,
++ needless_borrow::NEEDLESS_BORROW,
++ needless_borrow::REF_BINDING_TO_REFERENCE,
++ needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
++ needless_continue::NEEDLESS_CONTINUE,
++ needless_for_each::NEEDLESS_FOR_EACH,
++ needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
++ needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
++ needless_question_mark::NEEDLESS_QUESTION_MARK,
++ needless_update::NEEDLESS_UPDATE,
++ neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
++ neg_multiply::NEG_MULTIPLY,
++ new_without_default::NEW_WITHOUT_DEFAULT,
++ no_effect::NO_EFFECT,
++ no_effect::UNNECESSARY_OPERATION,
++ non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
++ non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
++ non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
++ non_expressive_names::MANY_SINGLE_CHAR_NAMES,
++ non_expressive_names::SIMILAR_NAMES,
++ non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
++ non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
++ nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
++ open_options::NONSENSICAL_OPEN_OPTIONS,
++ option_env_unwrap::OPTION_ENV_UNWRAP,
++ option_if_let_else::OPTION_IF_LET_ELSE,
++ overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
++ panic_in_result_fn::PANIC_IN_RESULT_FN,
++ panic_unimplemented::PANIC,
++ panic_unimplemented::TODO,
++ panic_unimplemented::UNIMPLEMENTED,
++ panic_unimplemented::UNREACHABLE,
++ partialeq_ne_impl::PARTIALEQ_NE_IMPL,
++ pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
++ pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
++ path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
++ pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
++ precedence::PRECEDENCE,
++ ptr::CMP_NULL,
++ ptr::INVALID_NULL_PTR_USAGE,
++ ptr::MUT_FROM_REF,
++ ptr::PTR_ARG,
++ ptr_eq::PTR_EQ,
++ ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
++ question_mark::QUESTION_MARK,
++ ranges::MANUAL_RANGE_CONTAINS,
++ ranges::RANGE_MINUS_ONE,
++ ranges::RANGE_PLUS_ONE,
++ ranges::RANGE_ZIP_WITH_LEN,
++ ranges::REVERSED_EMPTY_RANGES,
++ redundant_clone::REDUNDANT_CLONE,
++ redundant_closure_call::REDUNDANT_CLOSURE_CALL,
++ redundant_else::REDUNDANT_ELSE,
++ redundant_field_names::REDUNDANT_FIELD_NAMES,
++ redundant_pub_crate::REDUNDANT_PUB_CRATE,
++ redundant_slicing::REDUNDANT_SLICING,
++ redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
++ ref_option_ref::REF_OPTION_REF,
++ reference::DEREF_ADDROF,
++ reference::REF_IN_DEREF,
++ regex::INVALID_REGEX,
++ regex::TRIVIAL_REGEX,
++ repeat_once::REPEAT_ONCE,
++ returns::LET_AND_RETURN,
++ returns::NEEDLESS_RETURN,
++ same_name_method::SAME_NAME_METHOD,
++ self_assignment::SELF_ASSIGNMENT,
++ self_named_constructors::SELF_NAMED_CONSTRUCTORS,
++ semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
++ serde_api::SERDE_API_MISUSE,
++ shadow::SHADOW_REUSE,
++ shadow::SHADOW_SAME,
++ shadow::SHADOW_UNRELATED,
++ single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
++ size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
++ slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
++ stable_sort_primitive::STABLE_SORT_PRIMITIVE,
++ strings::STRING_ADD,
++ strings::STRING_ADD_ASSIGN,
++ strings::STRING_FROM_UTF8_AS_BYTES,
++ strings::STRING_LIT_AS_BYTES,
++ strings::STRING_TO_STRING,
++ strings::STR_TO_STRING,
++ strlen_on_c_strings::STRLEN_ON_C_STRINGS,
++ suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
++ suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
++ suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
++ swap::ALMOST_SWAPPED,
++ swap::MANUAL_SWAP,
++ tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
++ temporary_assignment::TEMPORARY_ASSIGNMENT,
++ to_digit_is_some::TO_DIGIT_IS_SOME,
++ to_string_in_display::TO_STRING_IN_DISPLAY,
++ trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
++ trait_bounds::TYPE_REPETITION_IN_BOUNDS,
++ transmute::CROSSPOINTER_TRANSMUTE,
++ transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
++ transmute::TRANSMUTE_BYTES_TO_STR,
++ transmute::TRANSMUTE_FLOAT_TO_INT,
++ transmute::TRANSMUTE_INT_TO_BOOL,
++ transmute::TRANSMUTE_INT_TO_CHAR,
++ transmute::TRANSMUTE_INT_TO_FLOAT,
++ transmute::TRANSMUTE_PTR_TO_PTR,
++ transmute::TRANSMUTE_PTR_TO_REF,
++ transmute::UNSOUND_COLLECTION_TRANSMUTE,
++ transmute::USELESS_TRANSMUTE,
++ transmute::WRONG_TRANSMUTE,
++ transmuting_null::TRANSMUTING_NULL,
++ try_err::TRY_ERR,
++ types::BORROWED_BOX,
++ types::BOX_COLLECTION,
++ types::LINKEDLIST,
++ types::OPTION_OPTION,
++ types::RC_BUFFER,
++ types::RC_MUTEX,
++ types::REDUNDANT_ALLOCATION,
++ types::TYPE_COMPLEXITY,
++ types::VEC_BOX,
++ undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
++ unicode::INVISIBLE_CHARACTERS,
++ unicode::NON_ASCII_LITERAL,
++ unicode::UNICODE_NOT_NFC,
++ unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
++ unit_types::LET_UNIT_VALUE,
++ unit_types::UNIT_ARG,
++ unit_types::UNIT_CMP,
++ unnamed_address::FN_ADDRESS_COMPARISONS,
++ unnamed_address::VTABLE_ADDRESS_COMPARISONS,
++ unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
++ unnecessary_sort_by::UNNECESSARY_SORT_BY,
++ unnecessary_wraps::UNNECESSARY_WRAPS,
++ unnested_or_patterns::UNNESTED_OR_PATTERNS,
++ unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
++ unused_async::UNUSED_ASYNC,
++ unused_io_amount::UNUSED_IO_AMOUNT,
++ unused_self::UNUSED_SELF,
++ unused_unit::UNUSED_UNIT,
++ unwrap::PANICKING_UNWRAP,
++ unwrap::UNNECESSARY_UNWRAP,
++ unwrap_in_result::UNWRAP_IN_RESULT,
++ upper_case_acronyms::UPPER_CASE_ACRONYMS,
++ use_self::USE_SELF,
++ useless_conversion::USELESS_CONVERSION,
++ vec::USELESS_VEC,
++ vec_init_then_push::VEC_INIT_THEN_PUSH,
++ vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
++ 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_STDERR,
++ write::PRINT_STDOUT,
++ write::PRINT_WITH_NEWLINE,
++ write::USE_DEBUG,
++ write::WRITELN_EMPTY_STRING,
++ write::WRITE_LITERAL,
++ write::WRITE_WITH_NEWLINE,
++ zero_div_zero::ZERO_DIVIDED_BY_ZERO,
++ zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
++ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
++ LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
++ LintId::of(copies::BRANCHES_SHARING_CODE),
++ LintId::of(disallowed_method::DISALLOWED_METHOD),
++ LintId::of(disallowed_type::DISALLOWED_TYPE),
++ LintId::of(equatable_if_let::EQUATABLE_IF_LET),
++ LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
++ LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
++ LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
++ LintId::of(future_not_send::FUTURE_NOT_SEND),
++ LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
++ 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(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
++ LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
++ LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
++ LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
++ LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
++ LintId::of(regex::TRIVIAL_REGEX),
++ LintId::of(strings::STRING_LIT_AS_BYTES),
++ LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
++ LintId::of(transmute::USELESS_TRANSMUTE),
++ LintId::of(use_self::USE_SELF),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
++ LintId::of(attrs::INLINE_ALWAYS),
++ LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
++ LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
++ LintId::of(bit_mask::VERBOSE_BIT_MASK),
++ LintId::of(bytecount::NAIVE_BYTECOUNT),
++ LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
++ LintId::of(casts::CAST_LOSSLESS),
++ LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
++ LintId::of(casts::CAST_POSSIBLE_WRAP),
++ LintId::of(casts::CAST_PRECISION_LOSS),
++ LintId::of(casts::CAST_PTR_ALIGNMENT),
++ LintId::of(casts::CAST_SIGN_LOSS),
++ LintId::of(casts::PTR_AS_PTR),
++ LintId::of(checked_conversions::CHECKED_CONVERSIONS),
++ LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
++ LintId::of(copy_iterator::COPY_ITERATOR),
++ LintId::of(default::DEFAULT_TRAIT_ACCESS),
++ LintId::of(dereference::EXPLICIT_DEREF_METHODS),
++ LintId::of(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(doc::MISSING_PANICS_DOC),
++ LintId::of(empty_enum::EMPTY_ENUM),
++ LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
++ LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
++ LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
++ LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
++ LintId::of(functions::MUST_USE_CANDIDATE),
++ LintId::of(functions::TOO_MANY_LINES),
++ LintId::of(if_not_else::IF_NOT_ELSE),
++ LintId::of(implicit_hasher::IMPLICIT_HASHER),
++ LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
++ LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
++ LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
++ LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
++ LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
++ LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
++ LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
++ LintId::of(let_underscore::LET_UNDERSCORE_DROP),
++ LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
++ LintId::of(literal_representation::UNREADABLE_LITERAL),
++ LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
++ LintId::of(loops::EXPLICIT_ITER_LOOP),
++ LintId::of(macro_use::MACRO_USE_IMPORTS),
++ LintId::of(manual_ok_or::MANUAL_OK_OR),
++ LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS),
++ LintId::of(matches::MATCH_BOOL),
++ LintId::of(matches::MATCH_SAME_ARMS),
++ LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
++ LintId::of(matches::MATCH_WILD_ERR_ARM),
++ LintId::of(matches::SINGLE_MATCH_ELSE),
++ LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
++ LintId::of(methods::FILTER_MAP_NEXT),
++ LintId::of(methods::FLAT_MAP_OPTION),
++ LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
++ LintId::of(methods::IMPLICIT_CLONE),
++ LintId::of(methods::INEFFICIENT_TO_STRING),
++ LintId::of(methods::MAP_FLATTEN),
++ LintId::of(methods::MAP_UNWRAP_OR),
++ LintId::of(misc::FLOAT_CMP),
++ LintId::of(misc::USED_UNDERSCORE_BINDING),
++ LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
++ LintId::of(mut_mut::MUT_MUT),
++ LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
++ LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
++ LintId::of(needless_continue::NEEDLESS_CONTINUE),
++ LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
++ LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
++ LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
++ LintId::of(non_expressive_names::SIMILAR_NAMES),
++ LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
++ LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
++ LintId::of(ranges::RANGE_MINUS_ONE),
++ LintId::of(ranges::RANGE_PLUS_ONE),
++ LintId::of(redundant_else::REDUNDANT_ELSE),
++ LintId::of(ref_option_ref::REF_OPTION_REF),
++ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
++ LintId::of(strings::STRING_ADD_ASSIGN),
++ LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
++ LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
++ LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
++ LintId::of(types::LINKEDLIST),
++ LintId::of(types::OPTION_OPTION),
++ LintId::of(unicode::NON_ASCII_LITERAL),
++ LintId::of(unicode::UNICODE_NOT_NFC),
++ LintId::of(unit_types::LET_UNIT_VALUE),
++ LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
++ LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
++ LintId::of(unused_async::UNUSED_ASYNC),
++ LintId::of(unused_self::UNUSED_SELF),
++ LintId::of(wildcard_imports::ENUM_GLOB_USE),
++ LintId::of(wildcard_imports::WILDCARD_IMPORTS),
++ LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
++ LintId::of(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::EXTEND_WITH_DRAIN),
++ LintId::of(methods::ITER_NTH),
++ LintId::of(methods::MANUAL_STR_REPEAT),
++ 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(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
++ LintId::of(types::BOX_COLLECTION),
++ LintId::of(types::REDUNDANT_ALLOCATION),
++ LintId::of(vec::USELESS_VEC),
++ LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
++ LintId::of(arithmetic::FLOAT_ARITHMETIC),
++ LintId::of(arithmetic::INTEGER_ARITHMETIC),
++ LintId::of(as_conversions::AS_CONVERSIONS),
++ LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
++ LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
++ LintId::of(create_dir::CREATE_DIR),
++ LintId::of(dbg_macro::DBG_MACRO),
++ LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
++ LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
++ LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
++ LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
++ LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
++ LintId::of(exit::EXIT),
++ LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
++ LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
++ LintId::of(implicit_return::IMPLICIT_RETURN),
++ LintId::of(indexing_slicing::INDEXING_SLICING),
++ LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
++ LintId::of(integer_division::INTEGER_DIVISION),
++ LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
++ LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
++ LintId::of(map_err_ignore::MAP_ERR_IGNORE),
++ 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::EXPECT_USED),
++ LintId::of(methods::FILETYPE_IS_FILE),
++ LintId::of(methods::GET_UNWRAP),
++ LintId::of(methods::UNWRAP_USED),
++ 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_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
++ LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
++ LintId::of(module_style::MOD_MODULE_FILES),
++ LintId::of(module_style::SELF_NAMED_MODULE_FILES),
++ LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
++ LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
++ LintId::of(panic_unimplemented::PANIC),
++ LintId::of(panic_unimplemented::TODO),
++ LintId::of(panic_unimplemented::UNIMPLEMENTED),
++ LintId::of(panic_unimplemented::UNREACHABLE),
++ LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
++ LintId::of(same_name_method::SAME_NAME_METHOD),
++ LintId::of(shadow::SHADOW_REUSE),
++ LintId::of(shadow::SHADOW_SAME),
++ LintId::of(shadow::SHADOW_UNRELATED),
++ LintId::of(strings::STRING_ADD),
++ LintId::of(strings::STRING_TO_STRING),
++ LintId::of(strings::STR_TO_STRING),
++ LintId::of(types::RC_BUFFER),
++ LintId::of(types::RC_MUTEX),
++ LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
++ LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
++ LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
++ LintId::of(write::PRINT_STDERR),
++ LintId::of(write::PRINT_STDOUT),
++ LintId::of(write::USE_DEBUG),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::style", Some("clippy_style"), vec![
++ LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
++ LintId::of(assign_ops::ASSIGN_OP_PATTERN),
++ LintId::of(blacklisted_name::BLACKLISTED_NAME),
++ LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
++ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
++ LintId::of(casts::FN_TO_NUMERIC_CAST),
++ LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
++ LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
++ LintId::of(collapsible_if::COLLAPSIBLE_IF),
++ LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
++ LintId::of(comparison_chain::COMPARISON_CHAIN),
++ LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
++ 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(from_over_into::FROM_OVER_INTO),
++ LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
++ LintId::of(functions::DOUBLE_MUST_USE),
++ LintId::of(functions::MUST_USE_UNIT),
++ LintId::of(functions::RESULT_UNIT_ERR),
++ LintId::of(if_then_panic::IF_THEN_PANIC),
++ LintId::of(inherent_to_string::INHERENT_TO_STRING),
++ LintId::of(len_zero::COMPARISON_TO_EMPTY),
++ LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
++ LintId::of(len_zero::LEN_ZERO),
++ LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
++ LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
++ LintId::of(loops::FOR_KV_MAP),
++ LintId::of(loops::NEEDLESS_RANGE_LOOP),
++ LintId::of(loops::SAME_ITEM_PUSH),
++ LintId::of(loops::WHILE_LET_ON_ITERATOR),
++ LintId::of(main_recursion::MAIN_RECURSION),
++ LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
++ LintId::of(manual_map::MANUAL_MAP),
++ LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
++ LintId::of(map_clone::MAP_CLONE),
++ LintId::of(match_result_ok::MATCH_RESULT_OK),
++ LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
++ LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
++ LintId::of(matches::MATCH_OVERLAPPING_ARM),
++ LintId::of(matches::MATCH_REF_PATS),
++ LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
++ LintId::of(matches::SINGLE_MATCH),
++ LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
++ LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
++ LintId::of(methods::BYTES_NTH),
++ LintId::of(methods::CHARS_LAST_CMP),
++ LintId::of(methods::CHARS_NEXT_CMP),
++ LintId::of(methods::INTO_ITER_ON_REF),
++ LintId::of(methods::ITER_CLONED_COLLECT),
++ LintId::of(methods::ITER_NEXT_SLICE),
++ LintId::of(methods::ITER_NTH_ZERO),
++ LintId::of(methods::ITER_SKIP_NEXT),
++ LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
++ LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
++ 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::SINGLE_CHAR_ADD_STR),
++ LintId::of(methods::STRING_EXTEND_CHARS),
++ LintId::of(methods::UNNECESSARY_FOLD),
++ LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
++ LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
++ LintId::of(methods::WRONG_SELF_CONVENTION),
++ LintId::of(misc::TOPLEVEL_REF_ARG),
++ LintId::of(misc::ZERO_PTR),
++ LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
++ LintId::of(misc_early::DOUBLE_NEG),
++ LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
++ LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
++ LintId::of(misc_early::REDUNDANT_PATTERN),
++ LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
++ LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
++ LintId::of(needless_borrow::NEEDLESS_BORROW),
++ LintId::of(neg_multiply::NEG_MULTIPLY),
++ LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
++ LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
++ LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
++ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
++ LintId::of(ptr::CMP_NULL),
++ LintId::of(ptr::PTR_ARG),
++ LintId::of(ptr_eq::PTR_EQ),
++ LintId::of(question_mark::QUESTION_MARK),
++ LintId::of(ranges::MANUAL_RANGE_CONTAINS),
++ LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
++ LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
++ LintId::of(returns::LET_AND_RETURN),
++ LintId::of(returns::NEEDLESS_RETURN),
++ LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
++ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
++ LintId::of(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(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
++ LintId::of(unused_unit::UNUSED_UNIT),
++ LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
++ LintId::of(write::PRINTLN_EMPTY_STRING),
++ LintId::of(write::PRINT_LITERAL),
++ LintId::of(write::PRINT_WITH_NEWLINE),
++ LintId::of(write::WRITELN_EMPTY_STRING),
++ LintId::of(write::WRITE_LITERAL),
++ LintId::of(write::WRITE_WITH_NEWLINE),
++])
--- /dev/null
--- /dev/null
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
++store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
++ LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
++ LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
++ LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
++ LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
++ LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
++ LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
++ LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
++ LintId::of(loops::EMPTY_LOOP),
++ LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
++ LintId::of(loops::MUT_RANGE_BOUND),
++ LintId::of(methods::SUSPICIOUS_MAP),
++ LintId::of(mut_key::MUTABLE_KEY_TYPE),
++ LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
++ LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
++])
--- /dev/null
- // 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::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::unused_collect",
- "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
- );
- store.register_removed(
- "clippy::replace_consts",
- "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
- );
- store.register_removed(
- "clippy::regex_macro",
- "the regex! macro has been removed from the regex crate in 2018",
- );
- store.register_removed(
- "clippy::find_map",
- "this lint has been replaced by `manual_find_map`, a more specific lint",
- );
- store.register_removed(
- "clippy::filter_map",
- "this lint has been replaced by `manual_filter_map`, a more specific lint",
- );
- store.register_removed(
- "clippy::pub_enum_variant_names",
- "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
- );
- store.register_removed(
- "clippy::wrong_pub_self_convention",
- "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
- );
- // end deprecated lints, do not remove this comment, it’s used in `update_lints`
+// error-pattern:cargo-clippy
+
+#![feature(box_patterns)]
+#![feature(drain_filter)]
+#![feature(in_band_lifetimes)]
+#![feature(iter_zip)]
+#![feature(once_cell)]
+#![feature(rustc_private)]
+#![feature(stmt_expr_attributes)]
+#![feature(control_flow_enum)]
+#![recursion_limit = "512"]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
+#![warn(trivial_casts, trivial_numeric_casts)]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![warn(rustc::internal)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate rustc_ast;
+extern crate rustc_ast_pretty;
+extern crate rustc_data_structures;
+extern crate rustc_driver;
+extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_hir_pretty;
+extern crate rustc_index;
+extern crate rustc_infer;
+extern crate rustc_lexer;
+extern crate rustc_lint;
+extern crate rustc_middle;
+extern crate rustc_mir_dataflow;
+extern crate rustc_parse;
+extern crate rustc_parse_format;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_target;
+extern crate rustc_trait_selection;
+extern crate rustc_typeck;
+
+#[macro_use]
+extern crate clippy_utils;
+
+use clippy_utils::parse_msrv;
+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`, `suspicious`, `complexity` and `perf` are
+/// enabled by default. As said in the README.md of this repository, if the lint level mapping
+/// changes, please update README.md.
+///
+/// # Example
+///
+/// ```
+/// #![feature(rustc_private)]
+/// extern crate rustc_session;
+/// use rustc_session::declare_tool_lint;
+/// use clippy_lints::declare_clippy_lint;
+///
+/// declare_clippy_lint! {
+/// /// ### What it does
+/// /// Checks for ... (describe what the lint matches).
+/// ///
+/// /// ### Why is this bad?
+/// /// Supply the reason for linting the code.
+/// ///
+/// /// ### Example
+/// /// ```rust
+/// /// // 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, suspicious, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+}
+
+#[cfg(feature = "metadata-collector-lint")]
+mod deprecated_lints;
+mod utils;
+
+// begin lints modules, do not remove this comment, it’s used in `update_lints`
+mod absurd_extreme_comparisons;
+mod approx_const;
+mod arithmetic;
+mod as_conversions;
+mod asm_syntax;
+mod assertions_on_constants;
+mod assign_ops;
+mod async_yields_async;
+mod attrs;
+mod await_holding_invalid;
+mod bit_mask;
+mod blacklisted_name;
+mod blocks_in_if_conditions;
+mod bool_assert_comparison;
+mod booleans;
+mod bytecount;
+mod cargo_common_metadata;
+mod case_sensitive_file_extension_comparisons;
+mod casts;
+mod checked_conversions;
+mod cognitive_complexity;
+mod collapsible_if;
+mod collapsible_match;
+mod comparison_chain;
+mod copies;
+mod copy_iterator;
+mod create_dir;
+mod dbg_macro;
+mod default;
+mod default_numeric_fallback;
+mod dereference;
+mod derivable_impls;
+mod derive;
+mod disallowed_method;
+mod disallowed_script_idents;
+mod disallowed_type;
+mod doc;
+mod double_comparison;
+mod double_parens;
+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 equatable_if_let;
+mod erasing_op;
+mod escape;
+mod eta_reduction;
+mod eval_order_dependence;
+mod excessive_bools;
+mod exhaustive_items;
+mod exit;
+mod explicit_write;
+mod fallible_impl_from;
+mod feature_name;
+mod float_equality_without_abs;
+mod float_literal;
+mod floating_point_arithmetic;
+mod format;
+mod formatting;
+mod from_over_into;
+mod from_str_radix_10;
+mod functions;
+mod future_not_send;
+mod get_last_with_len;
+mod identity_op;
+mod if_let_mutex;
+mod if_not_else;
+mod if_then_panic;
+mod if_then_some_else_none;
+mod implicit_hasher;
+mod implicit_return;
+mod implicit_saturating_sub;
+mod inconsistent_struct_constructor;
+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 invalid_upcast_comparisons;
+mod items_after_statements;
+mod iter_not_returning_iterator;
+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_map;
+mod manual_non_exhaustive;
+mod manual_ok_or;
+mod manual_strip;
+mod manual_unwrap_or;
+mod map_clone;
+mod map_err_ignore;
+mod map_unit_fn;
+mod match_on_vec_items;
+mod match_result_ok;
+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_enforced_import_rename;
+mod missing_inline;
+mod module_style;
+mod modulo_arithmetic;
+mod multiple_crate_versions;
+mod mut_key;
+mod mut_mut;
+mod mut_mutex_lock;
+mod mut_reference;
+mod mutable_debug_assertion;
+mod mutex_atomic;
+mod needless_arbitrary_self_type;
+mod needless_bitwise_bool;
+mod needless_bool;
+mod needless_borrow;
+mod needless_borrowed_ref;
+mod needless_continue;
+mod needless_for_each;
+mod needless_option_as_deref;
+mod needless_pass_by_value;
+mod needless_question_mark;
+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 non_octal_unix_permissions;
++mod non_send_fields_in_send_ty;
+mod nonstandard_macro_braces;
+mod open_options;
+mod option_env_unwrap;
+mod option_if_let_else;
+mod overflow_check_conditional;
+mod panic_in_result_fn;
+mod panic_unimplemented;
+mod partialeq_ne_impl;
+mod pass_by_ref_or_value;
+mod path_buf_push_overwrite;
+mod pattern_type_mismatch;
+mod precedence;
+mod ptr;
+mod ptr_eq;
+mod ptr_offset_with_cast;
+mod question_mark;
+mod ranges;
+mod redundant_clone;
+mod redundant_closure_call;
+mod redundant_else;
+mod redundant_field_names;
+mod redundant_pub_crate;
+mod redundant_slicing;
+mod redundant_static_lifetimes;
+mod ref_option_ref;
+mod reference;
+mod regex;
+mod repeat_once;
+mod returns;
+mod same_name_method;
+mod self_assignment;
+mod self_named_constructors;
+mod semicolon_if_nothing_returned;
+mod serde_api;
+mod shadow;
+mod single_component_path_imports;
+mod size_of_in_element_count;
+mod slow_vector_initialization;
+mod stable_sort_primitive;
+mod strings;
+mod strlen_on_c_strings;
+mod suspicious_operation_groupings;
+mod suspicious_trait_impl;
+mod swap;
+mod tabs_in_doc_comments;
+mod temporary_assignment;
+mod to_digit_is_some;
+mod to_string_in_display;
+mod trait_bounds;
+mod transmute;
+mod transmuting_null;
+mod try_err;
+mod types;
+mod undropped_manually_drops;
+mod unicode;
+mod unit_return_expecting_ord;
+mod unit_types;
+mod unnamed_address;
+mod unnecessary_self_imports;
+mod unnecessary_sort_by;
+mod unnecessary_wraps;
+mod unnested_or_patterns;
+mod unsafe_removed_from_name;
+mod unused_async;
+mod unused_io_amount;
+mod unused_self;
+mod unused_unit;
+mod unwrap;
+mod unwrap_in_result;
+mod upper_case_acronyms;
+mod use_self;
+mod useless_conversion;
+mod vec;
+mod vec_init_then_push;
+mod vec_resize_to_zero;
+mod verbose_file_reads;
+mod wildcard_dependencies;
+mod wildcard_imports;
+mod write;
+mod zero_div_zero;
+mod zero_sized_map_values;
+// end lints modules, do not remove this comment, it’s used in `update_lints`
+
+pub use crate::utils::conf::Conf;
+use crate::utils::conf::TryConf;
+
+/// 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) {
+ // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
+ store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
+ store.register_pre_expansion_pass(|| Box::new(attrs::EarlyAttributes));
+ store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro));
+}
+
+#[doc(hidden)]
+pub fn read_conf(sess: &Session) -> Conf {
+ let file_name = 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 TryConf { 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
+}
+
+/// 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 register lints, do not remove this comment, it’s used in `update_lints`
- store.register_lints(&[
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::CLIPPY_LINTS_INTERNAL,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::COMPILER_LINT_FUNCTIONS,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::DEFAULT_LINT,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::IF_CHAIN_STYLE,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::INTERNING_DEFINED_SYMBOL,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::INVALID_PATHS,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::LINT_WITHOUT_LINT_PASS,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::OUTER_EXPN_EXPN_DATA,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::PRODUCE_ICE,
- #[cfg(feature = "internal-lints")]
- utils::internal_lints::UNNECESSARY_SYMBOL_STR,
- absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
- approx_const::APPROX_CONSTANT,
- arithmetic::FLOAT_ARITHMETIC,
- arithmetic::INTEGER_ARITHMETIC,
- as_conversions::AS_CONVERSIONS,
- asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
- asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
- assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
- assign_ops::ASSIGN_OP_PATTERN,
- assign_ops::MISREFACTORED_ASSIGN_OP,
- async_yields_async::ASYNC_YIELDS_ASYNC,
- attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
- attrs::DEPRECATED_CFG_ATTR,
- attrs::DEPRECATED_SEMVER,
- attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
- attrs::INLINE_ALWAYS,
- attrs::MISMATCHED_TARGET_OS,
- attrs::USELESS_ATTRIBUTE,
- await_holding_invalid::AWAIT_HOLDING_LOCK,
- await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
- bit_mask::BAD_BIT_MASK,
- bit_mask::INEFFECTIVE_BIT_MASK,
- bit_mask::VERBOSE_BIT_MASK,
- blacklisted_name::BLACKLISTED_NAME,
- blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
- bool_assert_comparison::BOOL_ASSERT_COMPARISON,
- booleans::LOGIC_BUG,
- booleans::NONMINIMAL_BOOL,
- bytecount::NAIVE_BYTECOUNT,
- cargo_common_metadata::CARGO_COMMON_METADATA,
- case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
- casts::CAST_LOSSLESS,
- casts::CAST_POSSIBLE_TRUNCATION,
- casts::CAST_POSSIBLE_WRAP,
- casts::CAST_PRECISION_LOSS,
- casts::CAST_PTR_ALIGNMENT,
- casts::CAST_REF_TO_MUT,
- casts::CAST_SIGN_LOSS,
- casts::CHAR_LIT_AS_U8,
- casts::FN_TO_NUMERIC_CAST,
- casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
- casts::PTR_AS_PTR,
- casts::UNNECESSARY_CAST,
- checked_conversions::CHECKED_CONVERSIONS,
- cognitive_complexity::COGNITIVE_COMPLEXITY,
- collapsible_if::COLLAPSIBLE_ELSE_IF,
- collapsible_if::COLLAPSIBLE_IF,
- collapsible_match::COLLAPSIBLE_MATCH,
- comparison_chain::COMPARISON_CHAIN,
- copies::BRANCHES_SHARING_CODE,
- copies::IFS_SAME_COND,
- copies::IF_SAME_THEN_ELSE,
- copies::SAME_FUNCTIONS_IN_IF_CONDITION,
- copy_iterator::COPY_ITERATOR,
- create_dir::CREATE_DIR,
- dbg_macro::DBG_MACRO,
- default::DEFAULT_TRAIT_ACCESS,
- default::FIELD_REASSIGN_WITH_DEFAULT,
- default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
- dereference::EXPLICIT_DEREF_METHODS,
- derivable_impls::DERIVABLE_IMPLS,
- derive::DERIVE_HASH_XOR_EQ,
- derive::DERIVE_ORD_XOR_PARTIAL_ORD,
- derive::EXPL_IMPL_CLONE_ON_COPY,
- derive::UNSAFE_DERIVE_DESERIALIZE,
- disallowed_method::DISALLOWED_METHOD,
- disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
- disallowed_type::DISALLOWED_TYPE,
- doc::DOC_MARKDOWN,
- doc::MISSING_ERRORS_DOC,
- doc::MISSING_PANICS_DOC,
- doc::MISSING_SAFETY_DOC,
- doc::NEEDLESS_DOCTEST_MAIN,
- double_comparison::DOUBLE_COMPARISONS,
- double_parens::DOUBLE_PARENS,
- 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,
- 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,
- exhaustive_items::EXHAUSTIVE_ENUMS,
- exhaustive_items::EXHAUSTIVE_STRUCTS,
- exit::EXIT,
- explicit_write::EXPLICIT_WRITE,
- fallible_impl_from::FALLIBLE_IMPL_FROM,
- feature_name::NEGATIVE_FEATURE_NAMES,
- feature_name::REDUNDANT_FEATURE_NAMES,
- float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
- 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,
- from_over_into::FROM_OVER_INTO,
- from_str_radix_10::FROM_STR_RADIX_10,
- functions::DOUBLE_MUST_USE,
- functions::MUST_USE_CANDIDATE,
- functions::MUST_USE_UNIT,
- functions::NOT_UNSAFE_PTR_ARG_DEREF,
- functions::RESULT_UNIT_ERR,
- functions::TOO_MANY_ARGUMENTS,
- functions::TOO_MANY_LINES,
- future_not_send::FUTURE_NOT_SEND,
- get_last_with_len::GET_LAST_WITH_LEN,
- identity_op::IDENTITY_OP,
- if_let_mutex::IF_LET_MUTEX,
- if_not_else::IF_NOT_ELSE,
- if_then_panic::IF_THEN_PANIC,
- if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
- implicit_hasher::IMPLICIT_HASHER,
- implicit_return::IMPLICIT_RETURN,
- implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
- inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
- 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,
- invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
- items_after_statements::ITEMS_AFTER_STATEMENTS,
- iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
- large_const_arrays::LARGE_CONST_ARRAYS,
- large_enum_variant::LARGE_ENUM_VARIANT,
- large_stack_arrays::LARGE_STACK_ARRAYS,
- len_zero::COMPARISON_TO_EMPTY,
- len_zero::LEN_WITHOUT_IS_EMPTY,
- len_zero::LEN_ZERO,
- let_if_seq::USELESS_LET_IF_SEQ,
- let_underscore::LET_UNDERSCORE_DROP,
- let_underscore::LET_UNDERSCORE_LOCK,
- let_underscore::LET_UNDERSCORE_MUST_USE,
- lifetimes::EXTRA_UNUSED_LIFETIMES,
- lifetimes::NEEDLESS_LIFETIMES,
- literal_representation::DECIMAL_LITERAL_REPRESENTATION,
- literal_representation::INCONSISTENT_DIGIT_GROUPING,
- literal_representation::LARGE_DIGIT_GROUPS,
- literal_representation::MISTYPED_LITERAL_SUFFIXES,
- literal_representation::UNREADABLE_LITERAL,
- literal_representation::UNUSUAL_BYTE_GROUPINGS,
- loops::EMPTY_LOOP,
- loops::EXPLICIT_COUNTER_LOOP,
- loops::EXPLICIT_INTO_ITER_LOOP,
- loops::EXPLICIT_ITER_LOOP,
- loops::FOR_KV_MAP,
- loops::FOR_LOOPS_OVER_FALLIBLES,
- loops::ITER_NEXT_LOOP,
- loops::MANUAL_FLATTEN,
- loops::MANUAL_MEMCPY,
- loops::MUT_RANGE_BOUND,
- loops::NEEDLESS_COLLECT,
- loops::NEEDLESS_RANGE_LOOP,
- loops::NEVER_LOOP,
- loops::SAME_ITEM_PUSH,
- loops::SINGLE_ELEMENT_LOOP,
- loops::WHILE_IMMUTABLE_CONDITION,
- loops::WHILE_LET_LOOP,
- loops::WHILE_LET_ON_ITERATOR,
- macro_use::MACRO_USE_IMPORTS,
- main_recursion::MAIN_RECURSION,
- manual_async_fn::MANUAL_ASYNC_FN,
- manual_map::MANUAL_MAP,
- manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
- manual_ok_or::MANUAL_OK_OR,
- manual_strip::MANUAL_STRIP,
- manual_unwrap_or::MANUAL_UNWRAP_OR,
- map_clone::MAP_CLONE,
- map_err_ignore::MAP_ERR_IGNORE,
- map_unit_fn::OPTION_MAP_UNIT_FN,
- map_unit_fn::RESULT_MAP_UNIT_FN,
- match_on_vec_items::MATCH_ON_VEC_ITEMS,
- match_result_ok::MATCH_RESULT_OK,
- matches::INFALLIBLE_DESTRUCTURING_MATCH,
- matches::MATCH_AS_REF,
- matches::MATCH_BOOL,
- matches::MATCH_LIKE_MATCHES_MACRO,
- matches::MATCH_OVERLAPPING_ARM,
- matches::MATCH_REF_PATS,
- matches::MATCH_SAME_ARMS,
- matches::MATCH_SINGLE_BINDING,
- matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
- matches::MATCH_WILD_ERR_ARM,
- matches::REDUNDANT_PATTERN_MATCHING,
- 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::BIND_INSTEAD_OF_MAP,
- methods::BYTES_NTH,
- methods::CHARS_LAST_CMP,
- methods::CHARS_NEXT_CMP,
- methods::CLONED_INSTEAD_OF_COPIED,
- methods::CLONE_DOUBLE_REF,
- methods::CLONE_ON_COPY,
- methods::CLONE_ON_REF_PTR,
- methods::EXPECT_FUN_CALL,
- methods::EXPECT_USED,
- methods::EXTEND_WITH_DRAIN,
- methods::FILETYPE_IS_FILE,
- methods::FILTER_MAP_IDENTITY,
- methods::FILTER_MAP_NEXT,
- methods::FILTER_NEXT,
- methods::FLAT_MAP_IDENTITY,
- methods::FLAT_MAP_OPTION,
- methods::FROM_ITER_INSTEAD_OF_COLLECT,
- methods::GET_UNWRAP,
- methods::IMPLICIT_CLONE,
- methods::INEFFICIENT_TO_STRING,
- methods::INSPECT_FOR_EACH,
- methods::INTO_ITER_ON_REF,
- methods::ITERATOR_STEP_BY_ZERO,
- methods::ITER_CLONED_COLLECT,
- methods::ITER_COUNT,
- methods::ITER_NEXT_SLICE,
- methods::ITER_NTH,
- methods::ITER_NTH_ZERO,
- methods::ITER_SKIP_NEXT,
- methods::MANUAL_FILTER_MAP,
- methods::MANUAL_FIND_MAP,
- methods::MANUAL_SATURATING_ARITHMETIC,
- methods::MANUAL_SPLIT_ONCE,
- methods::MANUAL_STR_REPEAT,
- methods::MAP_COLLECT_RESULT_UNIT,
- methods::MAP_FLATTEN,
- methods::MAP_IDENTITY,
- methods::MAP_UNWRAP_OR,
- methods::NEW_RET_NO_SELF,
- methods::OK_EXPECT,
- methods::OPTION_AS_REF_DEREF,
- methods::OPTION_FILTER_MAP,
- methods::OPTION_MAP_OR_NONE,
- methods::OR_FUN_CALL,
- methods::RESULT_MAP_OR_INTO_OPTION,
- methods::SEARCH_IS_SOME,
- methods::SHOULD_IMPLEMENT_TRAIT,
- methods::SINGLE_CHAR_ADD_STR,
- methods::SINGLE_CHAR_PATTERN,
- methods::SKIP_WHILE_NEXT,
- methods::STRING_EXTEND_CHARS,
- methods::SUSPICIOUS_MAP,
- methods::SUSPICIOUS_SPLITN,
- methods::UNINIT_ASSUMED_INIT,
- methods::UNNECESSARY_FILTER_MAP,
- methods::UNNECESSARY_FOLD,
- methods::UNNECESSARY_LAZY_EVALUATIONS,
- methods::UNWRAP_OR_ELSE_DEFAULT,
- methods::UNWRAP_USED,
- methods::USELESS_ASREF,
- 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_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_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
- missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
- module_style::MOD_MODULE_FILES,
- module_style::SELF_NAMED_MODULE_FILES,
- modulo_arithmetic::MODULO_ARITHMETIC,
- multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
- mut_key::MUTABLE_KEY_TYPE,
- mut_mut::MUT_MUT,
- mut_mutex_lock::MUT_MUTEX_LOCK,
- mut_reference::UNNECESSARY_MUT_PASSED,
- mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
- mutex_atomic::MUTEX_ATOMIC,
- mutex_atomic::MUTEX_INTEGER,
- needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
- needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
- needless_bool::BOOL_COMPARISON,
- needless_bool::NEEDLESS_BOOL,
- needless_borrow::NEEDLESS_BORROW,
- needless_borrow::REF_BINDING_TO_REFERENCE,
- needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
- needless_continue::NEEDLESS_CONTINUE,
- needless_for_each::NEEDLESS_FOR_EACH,
- needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
- needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
- needless_question_mark::NEEDLESS_QUESTION_MARK,
- needless_update::NEEDLESS_UPDATE,
- neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
- neg_multiply::NEG_MULTIPLY,
- new_without_default::NEW_WITHOUT_DEFAULT,
- no_effect::NO_EFFECT,
- no_effect::UNNECESSARY_OPERATION,
- non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
- non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
- non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
- non_expressive_names::MANY_SINGLE_CHAR_NAMES,
- non_expressive_names::SIMILAR_NAMES,
- non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
- nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
- open_options::NONSENSICAL_OPEN_OPTIONS,
- option_env_unwrap::OPTION_ENV_UNWRAP,
- option_if_let_else::OPTION_IF_LET_ELSE,
- overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
- panic_in_result_fn::PANIC_IN_RESULT_FN,
- panic_unimplemented::PANIC,
- panic_unimplemented::TODO,
- panic_unimplemented::UNIMPLEMENTED,
- panic_unimplemented::UNREACHABLE,
- partialeq_ne_impl::PARTIALEQ_NE_IMPL,
- pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
- pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
- path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
- pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
- precedence::PRECEDENCE,
- ptr::CMP_NULL,
- ptr::INVALID_NULL_PTR_USAGE,
- ptr::MUT_FROM_REF,
- ptr::PTR_ARG,
- ptr_eq::PTR_EQ,
- ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
- question_mark::QUESTION_MARK,
- ranges::MANUAL_RANGE_CONTAINS,
- ranges::RANGE_MINUS_ONE,
- ranges::RANGE_PLUS_ONE,
- ranges::RANGE_ZIP_WITH_LEN,
- ranges::REVERSED_EMPTY_RANGES,
- redundant_clone::REDUNDANT_CLONE,
- redundant_closure_call::REDUNDANT_CLOSURE_CALL,
- redundant_else::REDUNDANT_ELSE,
- redundant_field_names::REDUNDANT_FIELD_NAMES,
- redundant_pub_crate::REDUNDANT_PUB_CRATE,
- redundant_slicing::REDUNDANT_SLICING,
- redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
- ref_option_ref::REF_OPTION_REF,
- reference::DEREF_ADDROF,
- reference::REF_IN_DEREF,
- regex::INVALID_REGEX,
- regex::TRIVIAL_REGEX,
- repeat_once::REPEAT_ONCE,
- returns::LET_AND_RETURN,
- returns::NEEDLESS_RETURN,
- same_name_method::SAME_NAME_METHOD,
- self_assignment::SELF_ASSIGNMENT,
- self_named_constructors::SELF_NAMED_CONSTRUCTORS,
- semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
- serde_api::SERDE_API_MISUSE,
- shadow::SHADOW_REUSE,
- shadow::SHADOW_SAME,
- shadow::SHADOW_UNRELATED,
- single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
- size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
- slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
- stable_sort_primitive::STABLE_SORT_PRIMITIVE,
- strings::STRING_ADD,
- strings::STRING_ADD_ASSIGN,
- strings::STRING_FROM_UTF8_AS_BYTES,
- strings::STRING_LIT_AS_BYTES,
- strings::STRING_TO_STRING,
- strings::STR_TO_STRING,
- strlen_on_c_strings::STRLEN_ON_C_STRINGS,
- suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
- suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
- suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
- swap::ALMOST_SWAPPED,
- swap::MANUAL_SWAP,
- tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
- temporary_assignment::TEMPORARY_ASSIGNMENT,
- to_digit_is_some::TO_DIGIT_IS_SOME,
- to_string_in_display::TO_STRING_IN_DISPLAY,
- trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
- trait_bounds::TYPE_REPETITION_IN_BOUNDS,
- transmute::CROSSPOINTER_TRANSMUTE,
- transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
- transmute::TRANSMUTE_BYTES_TO_STR,
- transmute::TRANSMUTE_FLOAT_TO_INT,
- transmute::TRANSMUTE_INT_TO_BOOL,
- transmute::TRANSMUTE_INT_TO_CHAR,
- transmute::TRANSMUTE_INT_TO_FLOAT,
- transmute::TRANSMUTE_PTR_TO_PTR,
- transmute::TRANSMUTE_PTR_TO_REF,
- transmute::UNSOUND_COLLECTION_TRANSMUTE,
- transmute::USELESS_TRANSMUTE,
- transmute::WRONG_TRANSMUTE,
- transmuting_null::TRANSMUTING_NULL,
- try_err::TRY_ERR,
- types::BORROWED_BOX,
- types::BOX_COLLECTION,
- types::LINKEDLIST,
- types::OPTION_OPTION,
- types::RC_BUFFER,
- types::RC_MUTEX,
- types::REDUNDANT_ALLOCATION,
- types::TYPE_COMPLEXITY,
- types::VEC_BOX,
- undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
- unicode::INVISIBLE_CHARACTERS,
- unicode::NON_ASCII_LITERAL,
- unicode::UNICODE_NOT_NFC,
- unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
- unit_types::LET_UNIT_VALUE,
- unit_types::UNIT_ARG,
- unit_types::UNIT_CMP,
- unnamed_address::FN_ADDRESS_COMPARISONS,
- unnamed_address::VTABLE_ADDRESS_COMPARISONS,
- unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
- unnecessary_sort_by::UNNECESSARY_SORT_BY,
- unnecessary_wraps::UNNECESSARY_WRAPS,
- unnested_or_patterns::UNNESTED_OR_PATTERNS,
- unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
- unused_async::UNUSED_ASYNC,
- unused_io_amount::UNUSED_IO_AMOUNT,
- unused_self::UNUSED_SELF,
- unused_unit::UNUSED_UNIT,
- unwrap::PANICKING_UNWRAP,
- unwrap::UNNECESSARY_UNWRAP,
- unwrap_in_result::UNWRAP_IN_RESULT,
- upper_case_acronyms::UPPER_CASE_ACRONYMS,
- use_self::USE_SELF,
- useless_conversion::USELESS_CONVERSION,
- vec::USELESS_VEC,
- vec_init_then_push::VEC_INIT_THEN_PUSH,
- vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
- 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_STDERR,
- write::PRINT_STDOUT,
- write::PRINT_WITH_NEWLINE,
- write::USE_DEBUG,
- write::WRITELN_EMPTY_STRING,
- write::WRITE_LITERAL,
- write::WRITE_WITH_NEWLINE,
- zero_div_zero::ZERO_DIVIDED_BY_ZERO,
- zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
- ]);
- // end register lints, do not remove this comment, it’s used in `update_lints`
-
- 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(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
- LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
- LintId::of(create_dir::CREATE_DIR),
- LintId::of(dbg_macro::DBG_MACRO),
- LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
- LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
- LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
- LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
- LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
- LintId::of(exit::EXIT),
- LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
- LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
- LintId::of(implicit_return::IMPLICIT_RETURN),
- LintId::of(indexing_slicing::INDEXING_SLICING),
- LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
- LintId::of(integer_division::INTEGER_DIVISION),
- LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
- LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
- LintId::of(map_err_ignore::MAP_ERR_IGNORE),
- 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::EXPECT_USED),
- LintId::of(methods::FILETYPE_IS_FILE),
- LintId::of(methods::GET_UNWRAP),
- LintId::of(methods::UNWRAP_USED),
- 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_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
- LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
- LintId::of(module_style::MOD_MODULE_FILES),
- LintId::of(module_style::SELF_NAMED_MODULE_FILES),
- LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
- LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
- LintId::of(panic_unimplemented::PANIC),
- LintId::of(panic_unimplemented::TODO),
- LintId::of(panic_unimplemented::UNIMPLEMENTED),
- LintId::of(panic_unimplemented::UNREACHABLE),
- LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
- LintId::of(same_name_method::SAME_NAME_METHOD),
- LintId::of(shadow::SHADOW_REUSE),
- LintId::of(shadow::SHADOW_SAME),
- LintId::of(strings::STRING_ADD),
- LintId::of(strings::STRING_TO_STRING),
- LintId::of(strings::STR_TO_STRING),
- LintId::of(types::RC_BUFFER),
- LintId::of(types::RC_MUTEX),
- LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
- LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
- LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
- LintId::of(write::PRINT_STDERR),
- 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_invalid::AWAIT_HOLDING_LOCK),
- LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
- LintId::of(bit_mask::VERBOSE_BIT_MASK),
- LintId::of(bytecount::NAIVE_BYTECOUNT),
- LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
- LintId::of(casts::CAST_LOSSLESS),
- LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
- LintId::of(casts::CAST_POSSIBLE_WRAP),
- LintId::of(casts::CAST_PRECISION_LOSS),
- LintId::of(casts::CAST_PTR_ALIGNMENT),
- LintId::of(casts::CAST_SIGN_LOSS),
- LintId::of(casts::PTR_AS_PTR),
- LintId::of(checked_conversions::CHECKED_CONVERSIONS),
- LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
- LintId::of(copy_iterator::COPY_ITERATOR),
- LintId::of(default::DEFAULT_TRAIT_ACCESS),
- LintId::of(dereference::EXPLICIT_DEREF_METHODS),
- LintId::of(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(doc::MISSING_PANICS_DOC),
- LintId::of(empty_enum::EMPTY_ENUM),
- LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
- LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
- LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
- LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
- LintId::of(functions::MUST_USE_CANDIDATE),
- LintId::of(functions::TOO_MANY_LINES),
- LintId::of(if_not_else::IF_NOT_ELSE),
- LintId::of(implicit_hasher::IMPLICIT_HASHER),
- LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
- LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
- LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
- LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
- LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
- LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
- LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
- LintId::of(let_underscore::LET_UNDERSCORE_DROP),
- LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
- LintId::of(literal_representation::UNREADABLE_LITERAL),
- LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
- LintId::of(loops::EXPLICIT_ITER_LOOP),
- LintId::of(macro_use::MACRO_USE_IMPORTS),
- LintId::of(manual_ok_or::MANUAL_OK_OR),
- LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS),
- LintId::of(matches::MATCH_BOOL),
- LintId::of(matches::MATCH_SAME_ARMS),
- LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
- LintId::of(matches::MATCH_WILD_ERR_ARM),
- LintId::of(matches::SINGLE_MATCH_ELSE),
- LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
- LintId::of(methods::FILTER_MAP_NEXT),
- LintId::of(methods::FLAT_MAP_OPTION),
- LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
- LintId::of(methods::IMPLICIT_CLONE),
- LintId::of(methods::INEFFICIENT_TO_STRING),
- LintId::of(methods::MAP_FLATTEN),
- LintId::of(methods::MAP_UNWRAP_OR),
- LintId::of(misc::FLOAT_CMP),
- LintId::of(misc::USED_UNDERSCORE_BINDING),
- LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
- LintId::of(mut_mut::MUT_MUT),
- LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
- LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
- LintId::of(needless_continue::NEEDLESS_CONTINUE),
- LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
- LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
- LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
- LintId::of(non_expressive_names::SIMILAR_NAMES),
- LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
- LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
- LintId::of(ranges::RANGE_MINUS_ONE),
- LintId::of(ranges::RANGE_PLUS_ONE),
- LintId::of(redundant_else::REDUNDANT_ELSE),
- LintId::of(ref_option_ref::REF_OPTION_REF),
- LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
- LintId::of(shadow::SHADOW_UNRELATED),
- LintId::of(strings::STRING_ADD_ASSIGN),
- LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
- LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
- LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
- LintId::of(types::LINKEDLIST),
- LintId::of(types::OPTION_OPTION),
- LintId::of(unicode::NON_ASCII_LITERAL),
- LintId::of(unicode::UNICODE_NOT_NFC),
- LintId::of(unit_types::LET_UNIT_VALUE),
- LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
- LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
- LintId::of(unused_async::UNUSED_ASYNC),
- LintId::of(unused_self::UNUSED_SELF),
- LintId::of(wildcard_imports::ENUM_GLOB_USE),
- LintId::of(wildcard_imports::WILDCARD_IMPORTS),
- LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
- ]);
++ include!("lib.deprecated.rs");
+
- 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::IF_CHAIN_STYLE),
- LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
- LintId::of(utils::internal_lints::INVALID_PATHS),
- LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
- LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
- LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
- LintId::of(utils::internal_lints::PRODUCE_ICE),
- LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
- ]);
-
- store.register_group(true, "clippy::all", Some("clippy"), vec![
- LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
- 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(async_yields_async::ASYNC_YIELDS_ASYNC),
- LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
- LintId::of(attrs::DEPRECATED_CFG_ATTR),
- LintId::of(attrs::DEPRECATED_SEMVER),
- LintId::of(attrs::MISMATCHED_TARGET_OS),
- LintId::of(attrs::USELESS_ATTRIBUTE),
- LintId::of(bit_mask::BAD_BIT_MASK),
- LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
- LintId::of(blacklisted_name::BLACKLISTED_NAME),
- LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
- LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
- LintId::of(booleans::LOGIC_BUG),
- LintId::of(booleans::NONMINIMAL_BOOL),
- LintId::of(casts::CAST_REF_TO_MUT),
- LintId::of(casts::CHAR_LIT_AS_U8),
- LintId::of(casts::FN_TO_NUMERIC_CAST),
- LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
- LintId::of(casts::UNNECESSARY_CAST),
- LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
- LintId::of(collapsible_if::COLLAPSIBLE_IF),
- LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
- LintId::of(comparison_chain::COMPARISON_CHAIN),
- LintId::of(copies::IFS_SAME_COND),
- LintId::of(copies::IF_SAME_THEN_ELSE),
- LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
- LintId::of(derivable_impls::DERIVABLE_IMPLS),
- LintId::of(derive::DERIVE_HASH_XOR_EQ),
- LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
- 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_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_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
- 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(from_over_into::FROM_OVER_INTO),
- LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
- LintId::of(functions::DOUBLE_MUST_USE),
- LintId::of(functions::MUST_USE_UNIT),
- LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
- LintId::of(functions::RESULT_UNIT_ERR),
- LintId::of(functions::TOO_MANY_ARGUMENTS),
- LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
- LintId::of(identity_op::IDENTITY_OP),
- LintId::of(if_let_mutex::IF_LET_MUTEX),
- LintId::of(if_then_panic::IF_THEN_PANIC),
- 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::COMPARISON_TO_EMPTY),
- LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
- LintId::of(len_zero::LEN_ZERO),
- LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
- LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
- LintId::of(lifetimes::NEEDLESS_LIFETIMES),
- LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
- LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
- LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
- LintId::of(loops::EMPTY_LOOP),
- LintId::of(loops::EXPLICIT_COUNTER_LOOP),
- LintId::of(loops::FOR_KV_MAP),
- LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
- LintId::of(loops::ITER_NEXT_LOOP),
- LintId::of(loops::MANUAL_FLATTEN),
- 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::SAME_ITEM_PUSH),
- LintId::of(loops::SINGLE_ELEMENT_LOOP),
- LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
- LintId::of(loops::WHILE_LET_LOOP),
- LintId::of(loops::WHILE_LET_ON_ITERATOR),
- LintId::of(main_recursion::MAIN_RECURSION),
- LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
- LintId::of(manual_map::MANUAL_MAP),
- LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
- LintId::of(manual_strip::MANUAL_STRIP),
- LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
- 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_result_ok::MATCH_RESULT_OK),
- LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
- LintId::of(matches::MATCH_AS_REF),
- LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
- LintId::of(matches::MATCH_OVERLAPPING_ARM),
- LintId::of(matches::MATCH_REF_PATS),
- LintId::of(matches::MATCH_SINGLE_BINDING),
- LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
- 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::BIND_INSTEAD_OF_MAP),
- LintId::of(methods::BYTES_NTH),
- LintId::of(methods::CHARS_LAST_CMP),
- LintId::of(methods::CHARS_NEXT_CMP),
- LintId::of(methods::CLONE_DOUBLE_REF),
- LintId::of(methods::CLONE_ON_COPY),
- LintId::of(methods::EXPECT_FUN_CALL),
- LintId::of(methods::EXTEND_WITH_DRAIN),
- LintId::of(methods::FILTER_MAP_IDENTITY),
- LintId::of(methods::FILTER_NEXT),
- LintId::of(methods::FLAT_MAP_IDENTITY),
- LintId::of(methods::INSPECT_FOR_EACH),
- LintId::of(methods::INTO_ITER_ON_REF),
- LintId::of(methods::ITERATOR_STEP_BY_ZERO),
- LintId::of(methods::ITER_CLONED_COLLECT),
- LintId::of(methods::ITER_COUNT),
- LintId::of(methods::ITER_NEXT_SLICE),
- LintId::of(methods::ITER_NTH),
- LintId::of(methods::ITER_NTH_ZERO),
- LintId::of(methods::ITER_SKIP_NEXT),
- LintId::of(methods::MANUAL_FILTER_MAP),
- LintId::of(methods::MANUAL_FIND_MAP),
- LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
- LintId::of(methods::MANUAL_SPLIT_ONCE),
- LintId::of(methods::MANUAL_STR_REPEAT),
- LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
- LintId::of(methods::MAP_IDENTITY),
- LintId::of(methods::NEW_RET_NO_SELF),
- LintId::of(methods::OK_EXPECT),
- LintId::of(methods::OPTION_AS_REF_DEREF),
- LintId::of(methods::OPTION_FILTER_MAP),
- LintId::of(methods::OPTION_MAP_OR_NONE),
- LintId::of(methods::OR_FUN_CALL),
- LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
- LintId::of(methods::SEARCH_IS_SOME),
- LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
- LintId::of(methods::SINGLE_CHAR_ADD_STR),
- LintId::of(methods::SINGLE_CHAR_PATTERN),
- LintId::of(methods::SKIP_WHILE_NEXT),
- LintId::of(methods::STRING_EXTEND_CHARS),
- LintId::of(methods::SUSPICIOUS_MAP),
- LintId::of(methods::SUSPICIOUS_SPLITN),
- LintId::of(methods::UNINIT_ASSUMED_INIT),
- LintId::of(methods::UNNECESSARY_FILTER_MAP),
- LintId::of(methods::UNNECESSARY_FOLD),
- LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
- LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
- 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::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_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_mutex_lock::MUT_MUTEX_LOCK),
- LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
- LintId::of(mutex_atomic::MUTEX_ATOMIC),
- LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
- LintId::of(needless_bool::BOOL_COMPARISON),
- LintId::of(needless_bool::NEEDLESS_BOOL),
- LintId::of(needless_borrow::NEEDLESS_BORROW),
- LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
- LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
- LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
- LintId::of(needless_update::NEEDLESS_UPDATE),
- LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
- LintId::of(neg_multiply::NEG_MULTIPLY),
- LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
- LintId::of(no_effect::NO_EFFECT),
- LintId::of(no_effect::UNNECESSARY_OPERATION),
- LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
- LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
- LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
- LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
- LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
- LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
- LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
- LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
- LintId::of(precedence::PRECEDENCE),
- LintId::of(ptr::CMP_NULL),
- LintId::of(ptr::INVALID_NULL_PTR_USAGE),
- LintId::of(ptr::MUT_FROM_REF),
- LintId::of(ptr::PTR_ARG),
- LintId::of(ptr_eq::PTR_EQ),
- LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
- LintId::of(question_mark::QUESTION_MARK),
- LintId::of(ranges::MANUAL_RANGE_CONTAINS),
- LintId::of(ranges::RANGE_ZIP_WITH_LEN),
- LintId::of(ranges::REVERSED_EMPTY_RANGES),
- LintId::of(redundant_clone::REDUNDANT_CLONE),
- LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
- LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
- LintId::of(redundant_slicing::REDUNDANT_SLICING),
- LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
- LintId::of(reference::DEREF_ADDROF),
- LintId::of(reference::REF_IN_DEREF),
- LintId::of(regex::INVALID_REGEX),
- LintId::of(repeat_once::REPEAT_ONCE),
- LintId::of(returns::LET_AND_RETURN),
- LintId::of(returns::NEEDLESS_RETURN),
- LintId::of(self_assignment::SELF_ASSIGNMENT),
- LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
- LintId::of(serde_api::SERDE_API_MISUSE),
- LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
- LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
- LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
- LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
- LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
- LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
- LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
- LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
- LintId::of(swap::ALMOST_SWAPPED),
- LintId::of(swap::MANUAL_SWAP),
- LintId::of(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(to_string_in_display::TO_STRING_IN_DISPLAY),
- LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
- LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
- LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
- LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
- LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
- LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
- LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
- LintId::of(transmute::TRANSMUTE_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::BORROWED_BOX),
- LintId::of(types::BOX_COLLECTION),
- LintId::of(types::REDUNDANT_ALLOCATION),
- LintId::of(types::TYPE_COMPLEXITY),
- LintId::of(types::VEC_BOX),
- LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
- LintId::of(unicode::INVISIBLE_CHARACTERS),
- LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
- LintId::of(unit_types::UNIT_ARG),
- LintId::of(unit_types::UNIT_CMP),
- LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
- LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
- LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
- LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
- LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
- LintId::of(unused_unit::UNUSED_UNIT),
- LintId::of(unwrap::PANICKING_UNWRAP),
- LintId::of(unwrap::UNNECESSARY_UNWRAP),
- LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
- LintId::of(useless_conversion::USELESS_CONVERSION),
- LintId::of(vec::USELESS_VEC),
- LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
- LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
- 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(blacklisted_name::BLACKLISTED_NAME),
- LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
- LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
- LintId::of(casts::FN_TO_NUMERIC_CAST),
- LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
- LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
- LintId::of(collapsible_if::COLLAPSIBLE_IF),
- LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
- LintId::of(comparison_chain::COMPARISON_CHAIN),
- LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
- 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(from_over_into::FROM_OVER_INTO),
- LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
- LintId::of(functions::DOUBLE_MUST_USE),
- LintId::of(functions::MUST_USE_UNIT),
- LintId::of(functions::RESULT_UNIT_ERR),
- LintId::of(if_then_panic::IF_THEN_PANIC),
- LintId::of(inherent_to_string::INHERENT_TO_STRING),
- LintId::of(len_zero::COMPARISON_TO_EMPTY),
- LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
- LintId::of(len_zero::LEN_ZERO),
- LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
- LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
- LintId::of(loops::FOR_KV_MAP),
- LintId::of(loops::NEEDLESS_RANGE_LOOP),
- LintId::of(loops::SAME_ITEM_PUSH),
- LintId::of(loops::WHILE_LET_ON_ITERATOR),
- LintId::of(main_recursion::MAIN_RECURSION),
- LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
- LintId::of(manual_map::MANUAL_MAP),
- LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
- LintId::of(map_clone::MAP_CLONE),
- LintId::of(match_result_ok::MATCH_RESULT_OK),
- LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
- LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
- LintId::of(matches::MATCH_OVERLAPPING_ARM),
- LintId::of(matches::MATCH_REF_PATS),
- LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
- LintId::of(matches::SINGLE_MATCH),
- LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
- LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
- LintId::of(methods::BYTES_NTH),
- LintId::of(methods::CHARS_LAST_CMP),
- LintId::of(methods::CHARS_NEXT_CMP),
- LintId::of(methods::INTO_ITER_ON_REF),
- LintId::of(methods::ITER_CLONED_COLLECT),
- LintId::of(methods::ITER_NEXT_SLICE),
- LintId::of(methods::ITER_NTH_ZERO),
- LintId::of(methods::ITER_SKIP_NEXT),
- LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
- LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
- 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::SINGLE_CHAR_ADD_STR),
- LintId::of(methods::STRING_EXTEND_CHARS),
- LintId::of(methods::UNNECESSARY_FOLD),
- LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
- LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
- LintId::of(methods::WRONG_SELF_CONVENTION),
- LintId::of(misc::TOPLEVEL_REF_ARG),
- LintId::of(misc::ZERO_PTR),
- LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
- LintId::of(misc_early::DOUBLE_NEG),
- LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
- LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
- LintId::of(misc_early::REDUNDANT_PATTERN),
- LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
- LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
- LintId::of(needless_borrow::NEEDLESS_BORROW),
- LintId::of(neg_multiply::NEG_MULTIPLY),
- LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
- LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
- LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
- LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
- LintId::of(ptr::CMP_NULL),
- LintId::of(ptr::PTR_ARG),
- LintId::of(ptr_eq::PTR_EQ),
- LintId::of(question_mark::QUESTION_MARK),
- LintId::of(ranges::MANUAL_RANGE_CONTAINS),
- LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
- LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
- LintId::of(returns::LET_AND_RETURN),
- LintId::of(returns::NEEDLESS_RETURN),
- LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
- LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
- LintId::of(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(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
- LintId::of(unused_unit::UNUSED_UNIT),
- LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
- LintId::of(write::PRINTLN_EMPTY_STRING),
- LintId::of(write::PRINT_LITERAL),
- LintId::of(write::PRINT_WITH_NEWLINE),
- LintId::of(write::WRITELN_EMPTY_STRING),
- LintId::of(write::WRITE_LITERAL),
- LintId::of(write::WRITE_WITH_NEWLINE),
- ]);
-
- store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
- LintId::of(attrs::DEPRECATED_CFG_ATTR),
- LintId::of(booleans::NONMINIMAL_BOOL),
- LintId::of(casts::CHAR_LIT_AS_U8),
- LintId::of(casts::UNNECESSARY_CAST),
- LintId::of(derivable_impls::DERIVABLE_IMPLS),
- 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(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_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::MANUAL_FLATTEN),
- LintId::of(loops::SINGLE_ELEMENT_LOOP),
- LintId::of(loops::WHILE_LET_LOOP),
- LintId::of(manual_strip::MANUAL_STRIP),
- LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
- 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::BIND_INSTEAD_OF_MAP),
- LintId::of(methods::CLONE_ON_COPY),
- LintId::of(methods::FILTER_MAP_IDENTITY),
- LintId::of(methods::FILTER_NEXT),
- LintId::of(methods::FLAT_MAP_IDENTITY),
- LintId::of(methods::INSPECT_FOR_EACH),
- LintId::of(methods::ITER_COUNT),
- LintId::of(methods::MANUAL_FILTER_MAP),
- LintId::of(methods::MANUAL_FIND_MAP),
- LintId::of(methods::MANUAL_SPLIT_ONCE),
- LintId::of(methods::MAP_IDENTITY),
- LintId::of(methods::OPTION_AS_REF_DEREF),
- LintId::of(methods::OPTION_FILTER_MAP),
- LintId::of(methods::SEARCH_IS_SOME),
- LintId::of(methods::SKIP_WHILE_NEXT),
- LintId::of(methods::UNNECESSARY_FILTER_MAP),
- LintId::of(methods::USELESS_ASREF),
- LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
- LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
- LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
- LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
- LintId::of(needless_bool::BOOL_COMPARISON),
- LintId::of(needless_bool::NEEDLESS_BOOL),
- LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
- LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
- LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
- LintId::of(needless_update::NEEDLESS_UPDATE),
- LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
- LintId::of(no_effect::NO_EFFECT),
- LintId::of(no_effect::UNNECESSARY_OPERATION),
- LintId::of(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_ZIP_WITH_LEN),
- LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
- LintId::of(redundant_slicing::REDUNDANT_SLICING),
- LintId::of(reference::DEREF_ADDROF),
- LintId::of(reference::REF_IN_DEREF),
- LintId::of(repeat_once::REPEAT_ONCE),
- LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
- LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
- LintId::of(swap::MANUAL_SWAP),
- LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
- LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
- LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
- LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
- LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
- LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
- LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
- LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
- LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
- LintId::of(types::BORROWED_BOX),
- LintId::of(types::TYPE_COMPLEXITY),
- LintId::of(types::VEC_BOX),
- LintId::of(unit_types::UNIT_ARG),
- LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
- LintId::of(unwrap::UNNECESSARY_UNWRAP),
- LintId::of(useless_conversion::USELESS_CONVERSION),
- LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
- ]);
-
- store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
- LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
- LintId::of(approx_const::APPROX_CONSTANT),
- LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
- LintId::of(attrs::DEPRECATED_SEMVER),
- LintId::of(attrs::MISMATCHED_TARGET_OS),
- LintId::of(attrs::USELESS_ATTRIBUTE),
- LintId::of(bit_mask::BAD_BIT_MASK),
- LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
- LintId::of(booleans::LOGIC_BUG),
- LintId::of(casts::CAST_REF_TO_MUT),
- LintId::of(copies::IFS_SAME_COND),
- LintId::of(copies::IF_SAME_THEN_ELSE),
- LintId::of(derive::DERIVE_HASH_XOR_EQ),
- LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
- LintId::of(drop_forget_ref::DROP_COPY),
- LintId::of(drop_forget_ref::DROP_REF),
- LintId::of(drop_forget_ref::FORGET_COPY),
- LintId::of(drop_forget_ref::FORGET_REF),
- LintId::of(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::ITER_NEXT_LOOP),
- LintId::of(loops::NEVER_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::SUSPICIOUS_SPLITN),
- LintId::of(methods::UNINIT_ASSUMED_INIT),
- LintId::of(methods::ZST_OFFSET),
- LintId::of(minmax::MIN_MAX),
- LintId::of(misc::CMP_NAN),
- LintId::of(misc::MODULO_ONE),
- LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
- LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
- LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
- LintId::of(ptr::INVALID_NULL_PTR_USAGE),
- LintId::of(ptr::MUT_FROM_REF),
- LintId::of(ranges::REVERSED_EMPTY_RANGES),
- LintId::of(regex::INVALID_REGEX),
- LintId::of(self_assignment::SELF_ASSIGNMENT),
- LintId::of(serde_api::SERDE_API_MISUSE),
- LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
- LintId::of(swap::ALMOST_SWAPPED),
- LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
- LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
- LintId::of(transmute::WRONG_TRANSMUTE),
- LintId::of(transmuting_null::TRANSMUTING_NULL),
- LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
- LintId::of(unicode::INVISIBLE_CHARACTERS),
- LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
- LintId::of(unit_types::UNIT_CMP),
- LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
- LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
- LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
- LintId::of(unwrap::PANICKING_UNWRAP),
- LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
- ]);
-
- store.register_group(true, "clippy::suspicious", None, vec![
- LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
- LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
- LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
- LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
- LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
- LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
- LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
- LintId::of(loops::EMPTY_LOOP),
- LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
- LintId::of(loops::MUT_RANGE_BOUND),
- LintId::of(methods::SUSPICIOUS_MAP),
- LintId::of(mut_key::MUTABLE_KEY_TYPE),
- LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
- LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
- ]);
-
- store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
- 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::EXTEND_WITH_DRAIN),
- LintId::of(methods::ITER_NTH),
- LintId::of(methods::MANUAL_STR_REPEAT),
- 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(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
- LintId::of(types::BOX_COLLECTION),
- LintId::of(types::REDUNDANT_ALLOCATION),
- LintId::of(vec::USELESS_VEC),
- LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
- ]);
-
- store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
- LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
- LintId::of(feature_name::NEGATIVE_FEATURE_NAMES),
- LintId::of(feature_name::REDUNDANT_FEATURE_NAMES),
- LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
- LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES),
- ]);
++ include!("lib.register_lints.rs");
++ include!("lib.register_restriction.rs");
++ include!("lib.register_pedantic.rs");
+
+ #[cfg(feature = "internal-lints")]
- 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(copies::BRANCHES_SHARING_CODE),
- LintId::of(disallowed_method::DISALLOWED_METHOD),
- LintId::of(disallowed_type::DISALLOWED_TYPE),
- 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(let_if_seq::USELESS_LET_IF_SEQ),
- 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(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
- LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
- LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
- LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
- LintId::of(regex::TRIVIAL_REGEX),
- LintId::of(strings::STRING_LIT_AS_BYTES),
- LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
- LintId::of(transmute::USELESS_TRANSMUTE),
- LintId::of(use_self::USE_SELF),
- ]);
++ include!("lib.register_internal.rs");
+
- store.register_late_pass(|| Box::new(shadow::Shadow));
++ include!("lib.register_all.rs");
++ include!("lib.register_style.rs");
++ include!("lib.register_complexity.rs");
++ include!("lib.register_correctness.rs");
++ include!("lib.register_suspicious.rs");
++ include!("lib.register_perf.rs");
++ include!("lib.register_cargo.rs");
++ include!("lib.register_nursery.rs");
+
+ #[cfg(feature = "metadata-collector-lint")]
+ {
+ if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
+ store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
+ return;
+ }
+ }
+
+ // all the internal lints
+ #[cfg(feature = "internal-lints")]
+ {
+ store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
+ store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
+ store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector));
+ store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
+ store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
+ store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
+ store.register_late_pass(|| Box::new(utils::internal_lints::InvalidPaths));
+ store.register_late_pass(|| Box::new(utils::internal_lints::InterningDefinedSymbol::default()));
+ store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
+ store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
+ store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass));
+ }
+
+ store.register_late_pass(|| Box::new(utils::author::Author));
+ store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
+ store.register_late_pass(|| Box::new(serde_api::SerdeApi));
+ let vec_box_size_threshold = conf.vec_box_size_threshold;
+ let type_complexity_threshold = conf.type_complexity_threshold;
+ let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
+ store.register_late_pass(move || Box::new(types::Types::new(
+ vec_box_size_threshold,
+ type_complexity_threshold,
+ avoid_breaking_exported_api,
+ )));
+ store.register_late_pass(|| Box::new(booleans::NonminimalBool));
+ store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
+ store.register_late_pass(|| Box::new(eq_op::EqOp));
+ store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
+ store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
+ let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
+ store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold)));
+ store.register_late_pass(|| Box::new(ptr::Ptr));
+ store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
+ store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
+ store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref));
+ store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
+ store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
+ store.register_late_pass(|| Box::new(misc::MiscLints));
+ store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
+ store.register_late_pass(|| Box::new(identity_op::IdentityOp));
+ store.register_late_pass(|| Box::new(erasing_op::ErasingOp));
+ store.register_late_pass(|| Box::new(mut_mut::MutMut));
+ store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed));
+ store.register_late_pass(|| Box::new(len_zero::LenZero));
+ store.register_late_pass(|| Box::new(attrs::Attributes));
+ store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
+ store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch));
+ store.register_late_pass(|| Box::new(unicode::Unicode));
+ store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
+ store.register_late_pass(|| Box::new(strings::StringAdd));
+ store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
+ store.register_late_pass(|| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
+ store.register_late_pass(|| Box::new(default_numeric_fallback::DefaultNumericFallback));
+ store.register_late_pass(|| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
+ store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
+ store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
+
+ let msrv = conf.msrv.as_ref().and_then(|s| {
+ parse_msrv(s, None, None).or_else(|| {
+ sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
+ None
+ })
+ });
+
+ let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
+ store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
+ store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
+ store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
+ store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
+ store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
+ store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
+ store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
+ store.register_late_pass(move || Box::new(checked_conversions::CheckedConversions::new(msrv)));
+ store.register_late_pass(move || Box::new(mem_replace::MemReplace::new(msrv)));
+ store.register_late_pass(move || Box::new(ranges::Ranges::new(msrv)));
+ store.register_late_pass(move || Box::new(from_over_into::FromOverInto::new(msrv)));
+ store.register_late_pass(move || Box::new(use_self::UseSelf::new(msrv)));
+ store.register_late_pass(move || Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
+ store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
+ store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
+ store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
+
+ store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
+ store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
+ store.register_late_pass(|| Box::new(map_clone::MapClone));
+ store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
++ store.register_late_pass(|| Box::new(shadow::Shadow::default()));
+ store.register_late_pass(|| Box::new(unit_types::UnitTypes));
+ store.register_late_pass(|| Box::new(loops::Loops));
+ store.register_late_pass(|| Box::new(main_recursion::MainRecursion::default()));
+ store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
+ store.register_late_pass(|| Box::new(entry::HashMapPass));
+ store.register_late_pass(|| Box::new(minmax::MinMaxPass));
+ store.register_late_pass(|| Box::new(open_options::OpenOptions));
+ store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
+ store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
+ store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
+ store.register_late_pass(|| Box::new(needless_borrow::NeedlessBorrow::default()));
+ store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
+ store.register_late_pass(|| Box::new(no_effect::NoEffect));
+ store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
+ store.register_late_pass(|| Box::new(transmute::Transmute));
+ let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
+ store.register_late_pass(move || Box::new(cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)));
+ let too_large_for_stack = conf.too_large_for_stack;
+ store.register_late_pass(move || Box::new(escape::BoxedLocal{too_large_for_stack}));
+ store.register_late_pass(move || Box::new(vec::UselessVec{too_large_for_stack}));
+ store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
+ store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
+ store.register_late_pass(|| Box::new(derive::Derive));
+ store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
+ store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen));
+ store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
+ store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
+ store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
+ store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
+ store.register_late_pass(|| Box::new(regex::Regex));
+ store.register_late_pass(|| Box::new(copies::CopyAndPaste));
+ store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
+ store.register_late_pass(|| Box::new(format::UselessFormat));
+ store.register_late_pass(|| Box::new(swap::Swap));
+ store.register_late_pass(|| Box::new(overflow_check_conditional::OverflowCheckConditional));
+ store.register_late_pass(|| Box::new(new_without_default::NewWithoutDefault::default()));
+ let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
+ let too_many_arguments_threshold = conf.too_many_arguments_threshold;
+ let too_many_lines_threshold = conf.too_many_lines_threshold;
+ store.register_late_pass(move || Box::new(functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)));
+ let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
+ store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
+ store.register_late_pass(|| Box::new(mem_discriminant::MemDiscriminant));
+ store.register_late_pass(|| Box::new(mem_forget::MemForget));
+ store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default()));
+ store.register_late_pass(|| Box::new(assign_ops::AssignOps));
+ store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
+ store.register_late_pass(|| Box::new(eval_order_dependence::EvalOrderDependence));
+ store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
+ store.register_late_pass(|| Box::new(missing_inline::MissingInline));
+ store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
+ store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
+ store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
+ store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
+ let enum_variant_size_threshold = conf.enum_variant_size_threshold;
+ store.register_late_pass(move || Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
+ store.register_late_pass(|| Box::new(explicit_write::ExplicitWrite));
+ store.register_late_pass(|| Box::new(needless_pass_by_value::NeedlessPassByValue));
+ let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
+ conf.trivial_copy_size_limit,
+ conf.pass_by_value_size_limit,
+ conf.avoid_breaking_exported_api,
+ &sess.target,
+ );
+ store.register_late_pass(move || Box::new(pass_by_ref_or_value));
+ store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
+ store.register_late_pass(|| Box::new(try_err::TryErr));
+ store.register_late_pass(|| Box::new(bytecount::ByteCount));
+ store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
+ store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
+ store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
+ store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
+ store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
+ store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons));
+ store.register_late_pass(|| Box::new(question_mark::QuestionMark));
+ store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
+ store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
+ store.register_late_pass(|| Box::new(map_unit_fn::MapUnit));
+ store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
+ store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
+ store.register_late_pass(|| Box::new(unwrap::Unwrap));
+ store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec));
+ store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
+ store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
+ store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
+ store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
+ store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
+ store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
+ store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
+ store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
+ store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
+ store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
+ store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
+ store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
+ let max_trait_bounds = conf.max_trait_bounds;
+ store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
+ store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
+ store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
+ store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
+ store.register_early_pass(|| Box::new(reference::DerefAddrOf));
+ store.register_early_pass(|| Box::new(reference::RefInDeref));
+ store.register_early_pass(|| Box::new(double_parens::DoubleParens));
+ store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new()));
+ store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
+ store.register_early_pass(|| Box::new(if_not_else::IfNotElse));
+ store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
+ store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
+ store.register_early_pass(|| Box::new(formatting::Formatting));
+ store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
+ store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
+ store.register_late_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
+ store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
+ store.register_late_pass(|| Box::new(returns::Return));
+ store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
+ store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
+ store.register_early_pass(|| Box::new(precedence::Precedence));
+ store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
+ store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
+ store.register_late_pass(|| Box::new(create_dir::CreateDir));
+ store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
+ let cargo_ignore_publish = conf.cargo_ignore_publish;
+ store.register_late_pass(move || Box::new(cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish)));
+ store.register_late_pass(|| Box::new(multiple_crate_versions::MultipleCrateVersions));
+ store.register_late_pass(|| Box::new(wildcard_dependencies::WildcardDependencies));
+ let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
+ store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)));
+ let literal_representation_threshold = conf.literal_representation_threshold;
+ store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)));
+ let enum_variant_name_threshold = conf.enum_variant_name_threshold;
+ store.register_late_pass(move || Box::new(enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)));
+ store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
+ let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
+ store.register_late_pass(move || Box::new(upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive)));
+ store.register_late_pass(|| Box::new(default::Default::default()));
+ store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
+ store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
+ store.register_late_pass(|| Box::new(exit::Exit));
+ store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
+ let array_size_threshold = conf.array_size_threshold;
+ store.register_late_pass(move || Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
+ store.register_late_pass(move || Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
+ store.register_late_pass(|| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
+ store.register_early_pass(|| Box::new(as_conversions::AsConversions));
+ store.register_late_pass(|| Box::new(let_underscore::LetUnderscore));
+ store.register_early_pass(|| Box::new(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::new(excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)));
+ store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
+ let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
+ store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
+ store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
+ store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
+ store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
+ store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
+ store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
+ store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
+ store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
++ store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
+ store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
+ store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems));
+ store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
+ store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
+ store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
+ let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
+ store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames {
+ single_char_binding_names_threshold,
+ }));
+ let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
+ store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
+ store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
+ store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
+ store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
+ store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
+ store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
+ store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr));
+ store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
+ store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
+ store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
+ store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
+ let disallowed_methods = conf.disallowed_methods.clone();
+ store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
+ store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
+ store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
+ store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
+ store.register_late_pass(|| Box::new(strings::StrToString));
+ store.register_late_pass(|| Box::new(strings::StringToString));
+ store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
+ store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
+ store.register_late_pass(|| Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons));
+ store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
+ store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
+ store.register_late_pass(|| Box::new(manual_map::ManualMap));
+ store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
+ store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
+ store.register_early_pass(move || Box::new(module_style::ModStyle));
+ store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
+ let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types)));
+ let import_renames = conf.enforced_import_renames.clone();
+ store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone())));
+ let scripts = conf.allowed_scripts.clone();
+ store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
+ store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
+ store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
+ store.register_late_pass(move || Box::new(feature_name::FeatureName));
+ store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
+ store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic));
++ let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
++ store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send)));
+}
+
+#[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(
+ "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",
+ );
+ store.register_removed(
+ "reverse_range_loop",
+ "this lint is now included in reversed_empty_ranges",
+ );
+}
+
+/// 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");
+ ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
+ ls.register_renamed("clippy::box_vec", "clippy::box_collection");
+ ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
+ ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
+ ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
+ ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
+ ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
+ ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
+ ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
+ ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
+ ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
+ ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
+ ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
+ ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
+ ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
+ ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
+ ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
+
+ // uplifted lints
+ ls.register_renamed("clippy::invalid_ref", "invalid_value");
+ ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
+ ls.register_renamed("clippy::unused_label", "unused_labels");
+ ls.register_renamed("clippy::drop_bounds", "drop_bounds");
+ ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
+ ls.register_renamed("clippy::panic_params", "non_fmt_panics");
+ ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
+ ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
+}
+
+// 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
- if let ty::BorrowKind::MutBorrow = bk {
+use super::MUT_RANGE_BOUND;
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{get_enclosing_block, higher, path_to_local};
+use if_chain::if_chain;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_middle::{mir::FakeReadCause, ty};
+use rustc_span::source_map::Span;
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+
+pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
+ if_chain! {
+ if let Some(higher::Range {
+ start: Some(start),
+ end: Some(end),
+ ..
+ }) = higher::Range::hir(arg);
+ let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end));
+ if mut_id_start.is_some() || mut_id_end.is_some();
+ then {
+ let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end);
+ 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_and_note(
+ cx,
+ MUT_RANGE_BOUND,
+ sp,
+ "attempt to mutate range bound within loop",
+ None,
+ "the range of the loop is unchanged",
+ );
+ }
+}
+
+fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
+ if_chain! {
+ if let Some(hir_id) = path_to_local(bound);
+ if let Node::Binding(pat) = cx.tcx.hir().get(hir_id);
+ if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
+ then {
+ return Some(hir_id);
+ }
+ }
+ None
+}
+
+fn check_for_mutation<'tcx>(
+ cx: &LateContext<'tcx>,
+ body: &Expr<'_>,
+ bound_id_start: Option<HirId>,
+ bound_id_end: Option<HirId>,
+) -> (Option<Span>, Option<Span>) {
+ let mut delegate = MutatePairDelegate {
+ cx,
+ hir_id_low: bound_id_start,
+ hir_id_high: bound_id_end,
+ span_low: None,
+ span_high: None,
+ };
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ ExprUseVisitor::new(
+ &mut delegate,
+ &infcx,
+ body.hir_id.owner,
+ cx.param_env,
+ cx.typeck_results(),
+ )
+ .walk_expr(body);
+ });
+
+ delegate.mutation_span()
+}
+
+struct MutatePairDelegate<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ hir_id_low: Option<HirId>,
+ hir_id_high: Option<HirId>,
+ span_low: Option<Span>,
+ span_high: Option<Span>,
+}
+
+impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
+ fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+ fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
++ if bk == ty::BorrowKind::MutBorrow {
+ if let PlaceBase::Local(id) = cmt.place.base {
+ if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
+ self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
+ }
+ if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
+ self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
+ }
+ }
+ }
+ }
+
+ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
+ if let PlaceBase::Local(id) = cmt.place.base {
+ if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
+ self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
+ }
+ if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
+ self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
+ }
+ }
+ }
+
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
+impl MutatePairDelegate<'_, '_> {
+ fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
+ (self.span_low, self.span_high)
+ }
+}
+
+struct BreakAfterExprVisitor {
+ hir_id: HirId,
+ past_expr: bool,
+ past_candidate: bool,
+ break_after_expr: bool,
+}
+
+impl BreakAfterExprVisitor {
+ pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+ let mut visitor = BreakAfterExprVisitor {
+ hir_id,
+ past_expr: false,
+ past_candidate: false,
+ break_after_expr: false,
+ };
+
+ get_enclosing_block(cx, hir_id).map_or(false, |block| {
+ visitor.visit_block(block);
+ visitor.break_after_expr
+ })
+ }
+}
+
+impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if self.past_candidate {
+ return;
+ }
+
+ if expr.hir_id == self.hir_id {
+ self.past_expr = true;
+ } else if self.past_expr {
+ if matches!(&expr.kind, ExprKind::Break(..)) {
+ self.break_after_expr = true;
+ }
+
+ self.past_candidate = true;
+ } else {
+ intravisit::walk_expr(self, expr);
+ }
+ }
+}
--- /dev/null
- if let BinOpKind::Add = op.node {
+use super::NEEDLESS_RANGE_LOOP;
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::has_iter_method;
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_middle::middle::region;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::symbol::{sym, Symbol};
+use std::iter::{self, Iterator};
+use std::mem;
+
+/// 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)]
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'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::hir(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 if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
+ return;
+ } 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, left, right) = end.kind {
++ if op.node == BinOpKind::Add {
+ 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 if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
+ return;
+ } 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,
+ arg.span,
+ &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
+ |diag| {
+ multispan_sugg(
+ diag,
+ "consider using an iterator",
+ 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,
+ arg.span,
+ &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed),
+ |diag| {
+ multispan_sugg(
+ diag,
+ "consider using an iterator",
+ vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
+ );
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
+fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
+ if_chain! {
+ if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind;
+ if len_args.len() == 1;
+ if method.ident.name == sym::len;
+ if let ExprKind::Path(QPath::Resolved(_, 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
+}
+
+struct VarVisitor<'a, 'tcx> {
+ /// context reference
+ cx: &'a LateContext<'tcx>,
+ /// var name to look for as index
+ var: HirId,
+ /// indexed variables that are used mutably
+ indexed_mut: FxHashSet<Symbol>,
+ /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
+ indexed_indirectly: FxHashMap<Symbol, 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<Symbol, (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<Symbol>,
+ /// 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, seqvar) = *seqpath;
+ if seqvar.segments.len() == 1;
+ if is_local_used(self.cx, idx, self.var);
+ then {
+ if self.prefer_mutable {
+ self.indexed_mut.insert(seqvar.segments[0].ident.name);
+ }
+ let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
+ let res = self.cx.qpath_res(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 index_used_directly {
+ self.indexed_directly.insert(
+ seqvar.segments[0].ident.name,
+ (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
+ );
+ } else {
+ self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
+ }
+ return false; // no need to walk further *on the variable*
+ }
+ Res::Def(DefKind::Static | DefKind::Const, ..) => {
+ if index_used_directly {
+ self.indexed_directly.insert(
+ seqvar.segments[0].ident.name,
+ (None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
+ );
+ } else {
+ self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
+ }
+ 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(meth, _, [args_0, args_1, ..], _) = &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(seqexpr, idx) = expr.kind;
+ if !self.check(idx, seqexpr, expr);
+ then { return }
+ }
+
+ if_chain! {
+ // directly using a variable
+ if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
+ if let Res::Local(local_id) = path.res;
+ then {
+ 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(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
+ self.prefer_mutable = true;
+ self.visit_expr(lhs);
+ self.prefer_mutable = false;
+ self.visit_expr(rhs);
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
+ if mutbl == Mutability::Mut {
+ self.prefer_mutable = true;
+ }
+ self.visit_expr(expr);
+ },
+ ExprKind::Call(f, args) => {
+ self.visit_expr(f);
+ for expr in args {
+ let ty = self.cx.typeck_results().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.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
+ for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), 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
+ }
+}
--- /dev/null
- if let LoopSource::ForLoop = source;
+use super::utils::make_iterator_snippet;
+use super::NEVER_LOOP;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher::ForLoop;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, Node, Pat, Stmt, StmtKind};
+use rustc_lint::LateContext;
+use std::iter::{once, Iterator};
+
+pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if let ExprKind::Loop(block, _, source, _) = expr.kind {
+ match never_loop_block(block, expr.hir_id) {
+ NeverLoopResult::AlwaysBreak => {
+ span_lint_and_then(cx, NEVER_LOOP, expr.span, "this loop never actually loops", |diag| {
+ if_chain! {
++ if source == LoopSource::ForLoop;
+ if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
+ if let Some(ForLoop { arg: iterator, pat, span: for_span, .. }) = ForLoop::hir(parent_match);
+ then {
+ // Suggests using an `if let` instead. This is `Unspecified` because the
+ // loop may (probably) contain `break` statements which would be invalid
+ // in an `if let`.
+ diag.span_suggestion_verbose(
+ for_span.with_hi(iterator.span.hi()),
+ "if you need the first element of the iterator, try writing",
+ for_to_if_let_sugg(cx, iterator, pat),
+ Applicability::Unspecified,
+ );
+ }
+ };
+ });
+ },
+ NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
+ }
+ }
+}
+
+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);
+ let mut iter = stmts.chain(expr).flatten();
+ never_loop_expr_seq(&mut iter, main_loop_id)
+}
+
+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 stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ match stmt.kind {
+ StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e),
+ StmtKind::Local(local) => local.init,
+ StmtKind::Item(..) => None,
+ }
+}
+
+fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
+ match expr.kind {
+ ExprKind::Box(e)
+ | ExprKind::Unary(_, e)
+ | ExprKind::Cast(e, _)
+ | ExprKind::Type(e, _)
+ | ExprKind::Let(_, e, _)
+ | ExprKind::Field(e, _)
+ | ExprKind::AddrOf(_, _, e)
+ | ExprKind::Struct(_, _, Some(e))
+ | ExprKind::Repeat(e, _)
+ | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
+ ExprKind::Array(es) | ExprKind::MethodCall(_, _, es, _) | ExprKind::Tup(es) => {
+ never_loop_expr_all(&mut es.iter(), main_loop_id)
+ },
+ ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
+ ExprKind::Binary(_, e1, e2)
+ | ExprKind::Assign(e1, e2, _)
+ | ExprKind::AssignOp(_, e1, e2)
+ | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
+ ExprKind::Loop(b, _, _, _) => {
+ // Break can come from the inner loop so remove them.
+ absorb_break(&never_loop_block(b, main_loop_id))
+ },
+ ExprKind::If(e, e2, e3) => {
+ let e1 = never_loop_expr(e, main_loop_id);
+ let e2 = never_loop_expr(e2, main_loop_id);
+ let e3 = e3
+ .as_ref()
+ .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
+ combine_seq(e1, combine_branches(e2, e3))
+ },
+ ExprKind::Match(e, 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(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(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
+ combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
+ }),
+ ExprKind::InlineAsm(asm) => asm
+ .operands
+ .iter()
+ .map(|(o, _)| match o {
+ InlineAsmOperand::In { expr, .. }
+ | InlineAsmOperand::InOut { expr, .. }
+ | InlineAsmOperand::Sym { expr } => never_loop_expr(expr, main_loop_id),
+ InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
+ InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
+ },
+ InlineAsmOperand::Const { .. } => NeverLoopResult::Otherwise,
+ })
+ .fold(NeverLoopResult::Otherwise, combine_both),
+ ExprKind::Struct(_, _, None)
+ | ExprKind::Yield(_, _)
+ | ExprKind::Closure(_, _, _, _, _)
+ | ExprKind::LlvmInlineAsm(_)
+ | ExprKind::Path(_)
+ | ExprKind::ConstBlock(_)
+ | ExprKind::Lit(_)
+ | ExprKind::Err => NeverLoopResult::Otherwise,
+ }
+}
+
+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 for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
+ let pat_snippet = snippet(cx, pat.span, "_");
+ let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified);
+
+ format!(
+ "if let Some({pat}) = {iter}.next()",
+ pat = pat_snippet,
+ iter = iter_snippet
+ )
+}
--- /dev/null
- if let IsAsync::NotAsync = header.asyncness;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::match_function_call;
+use clippy_utils::paths::FUTURE_FROM_GENERATOR;
+use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
+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, GenericArg, GenericBound, HirId,
+ IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, 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.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::future::Future;
+ ///
+ /// fn foo() -> impl Future<Output = i32> { async { 42 } }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// 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<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ kind: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ span: Span,
+ _: HirId,
+ ) {
+ if_chain! {
+ if let Some(header) = kind.header();
- if let Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) = closure_body.generator_kind;
++ if header.asyncness == IsAsync::NotAsync;
+ // Check that this function returns `impl Future`
+ if let FnRetTy::Return(ret_ty) = decl.output;
+ if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
+ if let Some(output) = future_output_ty(trait_ref);
+ if captures_all_lifetimes(decl.inputs, &output_lifetimes);
+ // 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) = position_before_rarrow(&header_snip);
+ 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>, Vec<LifetimeName>)> {
+ if_chain! {
+ if let TyKind::OpaqueDef(item_id, bounds) = ty.kind;
+ let item = cx.tcx.hir().item(item_id);
+ if let ItemKind::OpaqueTy(opaque) = &item.kind;
+ if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
+ if let GenericBound::Trait(poly, _) = bound {
+ Some(&poly.trait_ref)
+ } else {
+ None
+ }
+ });
+ if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
+ then {
+ let output_lifetimes = bounds
+ .iter()
+ .filter_map(|bound| {
+ if let GenericArg::Lifetime(lt) = bound {
+ Some(lt.name)
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ return Some((trait_ref, output_lifetimes));
+ }
+ }
+
+ 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.name == sym::Output;
+ if let TypeBindingKind::Equality{ty: output} = binding.kind;
+ then {
+ return Some(output)
+ }
+ }
+
+ None
+}
+
+fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
+ let input_lifetimes: Vec<LifetimeName> = inputs
+ .iter()
+ .filter_map(|ty| {
+ if let TyKind::Rptr(lt, _) = ty.kind {
+ Some(lt.name)
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ // The lint should trigger in one of these cases:
+ // - There are no input lifetimes
+ // - There's only one output lifetime bound using `+ '_`
+ // - All input lifetimes are explicitly bound to the output
+ input_lifetimes.is_empty()
+ || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore))
+ || input_lifetimes
+ .iter()
+ .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
+}
+
+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 closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
+ 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
- let (map_type, variant, lint) =
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) {
- ("Option", "Some", OPTION_MAP_UNIT_FN)
- } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) {
- ("Result", "Ok", RESULT_MAP_UNIT_FN)
- } else {
- return;
- };
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{iter_input_pats, method_chain_args};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `option.map(f)` where f is a function
+ /// or closure that returns the unit type `()`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more clearly with
+ /// an if let statement
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn do_stuff() -> Option<String> { Some(String::new()) }
+ /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
+ /// # fn format_msg(foo: String) -> String { String::new() }
+ /// let x: Option<String> = do_stuff();
+ /// x.map(log_err_msg);
+ /// # let x: Option<String> = do_stuff();
+ /// x.map(|msg| log_err_msg(format_msg(msg)));
+ /// ```
+ ///
+ /// The correct use would be:
+ ///
+ /// ```rust
+ /// # fn do_stuff() -> Option<String> { Some(String::new()) }
+ /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
+ /// # fn format_msg(foo: String) -> String { String::new() }
+ /// let x: Option<String> = do_stuff();
+ /// if let Some(msg) = x {
+ /// log_err_msg(msg);
+ /// }
+ ///
+ /// # let x: Option<String> = do_stuff();
+ /// if let Some(msg) = x {
+ /// log_err_msg(format_msg(msg));
+ /// }
+ /// ```
+ pub OPTION_MAP_UNIT_FN,
+ complexity,
+ "using `option.map(f)`, where `f` is a function or closure that returns `()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `result.map(f)` where f is a function
+ /// or closure that returns the unit type `()`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more clearly with
+ /// an if let statement
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
+ /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
+ /// # fn format_msg(foo: String) -> String { String::new() }
+ /// let x: Result<String, String> = do_stuff();
+ /// x.map(log_err_msg);
+ /// # let x: Result<String, String> = do_stuff();
+ /// x.map(|msg| log_err_msg(format_msg(msg)));
+ /// ```
+ ///
+ /// The correct use would be:
+ ///
+ /// ```rust
+ /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
+ /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
+ /// # fn format_msg(foo: String) -> String { String::new() }
+ /// let x: Result<String, String> = do_stuff();
+ /// if let Ok(msg) = x {
+ /// log_err_msg(msg);
+ /// };
+ /// # let x: Result<String, String> = do_stuff();
+ /// if let Ok(msg) = x {
+ /// log_err_msg(format_msg(msg));
+ /// };
+ /// ```
+ pub RESULT_MAP_UNIT_FN,
+ complexity,
+ "using `result.map(f)`, where `f` is a function or closure that returns `()`"
+}
+
+declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]);
+
+fn is_unit_type(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ ty::Tuple(slice) => slice.is_empty(),
+ ty::Never => true,
+ _ => false,
+ }
+}
+
+fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+ let ty = cx.typeck_results().expr_ty(expr);
+
+ if let ty::FnDef(id, _) = *ty.kind() {
+ if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
+ return is_unit_type(fn_type.output());
+ }
+ }
+ false
+}
+
+fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+ is_unit_type(cx.typeck_results().expr_ty(expr))
+}
+
+/// The expression inside a closure may or may not have surrounding braces and
+/// semicolons, which causes problems when generating a suggestion. Given an
+/// expression that evaluates to '()' or '!', recursively remove useless braces
+/// and semi-colons until is suitable for including in the suggestion template
+fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<Span> {
+ if !is_unit_expression(cx, expr) {
+ return None;
+ }
+
+ match expr.kind {
+ hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(_, _, _, _) => {
+ // Calls can't be reduced any more
+ Some(expr.span)
+ },
+ hir::ExprKind::Block(block, _) => {
+ match (block.stmts, block.expr.as_ref()) {
+ (&[], Some(inner_expr)) => {
+ // If block only contains an expression,
+ // reduce `{ X }` to `X`
+ reduce_unit_expression(cx, inner_expr)
+ },
+ (&[ref inner_stmt], None) => {
+ // If block only contains statements,
+ // reduce `{ X; }` to `X` or `X;`
+ match inner_stmt.kind {
+ hir::StmtKind::Local(local) => Some(local.span),
+ hir::StmtKind::Expr(e) => Some(e.span),
+ hir::StmtKind::Semi(..) => Some(inner_stmt.span),
+ hir::StmtKind::Item(..) => None,
+ }
+ },
+ _ => {
+ // For closures that contain multiple statements
+ // it's difficult to get a correct suggestion span
+ // for all cases (multi-line closures specifically)
+ //
+ // We do not attempt to build a suggestion for those right now.
+ None
+ },
+ }
+ },
+ _ => None,
+ }
+}
+
+fn unit_closure<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &hir::Expr<'_>,
+) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
+ if_chain! {
+ if let hir::ExprKind::Closure(_, decl, inner_expr_id, _, _) = expr.kind;
+ let body = cx.tcx.hir().body(inner_expr_id);
+ let body_expr = &body.value;
+ if decl.inputs.len() == 1;
+ if is_unit_expression(cx, body_expr);
+ if let Some(binding) = iter_input_pats(decl, body).next();
+ then {
+ return Some((binding, body_expr));
+ }
+ }
+ None
+}
+
+/// Builds a name for the let binding variable (`var_arg`)
+///
+/// `x.field` => `x_field`
+/// `y` => `_y`
+///
+/// Anything else will return `a`.
+fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String {
+ match &var_arg.kind {
+ hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
+ hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
+ _ => "a".to_string(),
+ }
+}
+
+#[must_use]
+fn suggestion_msg(function_type: &str, map_type: &str) -> String {
+ format!(
+ "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`",
+ map_type, function_type
+ )
+}
+
+fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr<'_>, map_args: &[hir::Expr<'_>]) {
+ let var_arg = &map_args[0];
+
++ let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) {
++ ("Option", "Some", OPTION_MAP_UNIT_FN)
++ } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) {
++ ("Result", "Ok", RESULT_MAP_UNIT_FN)
++ } else {
++ return;
++ };
+ let fn_arg = &map_args[1];
+
+ if is_unit_function(cx, fn_arg) {
+ let msg = suggestion_msg("function", map_type);
+ let suggestion = format!(
+ "if let {0}({binding}) = {1} {{ {2}({binding}) }}",
+ variant,
+ snippet(cx, var_arg.span, "_"),
+ snippet(cx, fn_arg.span, "_"),
+ binding = let_binding_name(cx, var_arg)
+ );
+
+ span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
+ diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable);
+ });
+ } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
+ let msg = suggestion_msg("closure", map_type);
+
+ span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
+ if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
+ let suggestion = format!(
+ "if let {0}({1}) = {2} {{ {3} }}",
+ variant,
+ snippet(cx, binding.pat.span, "_"),
+ snippet(cx, var_arg.span, "_"),
+ snippet(cx, reduced_expr_span, "_")
+ );
+ diag.span_suggestion(
+ stmt.span,
+ "try this",
+ suggestion,
+ Applicability::MachineApplicable, // snippet
+ );
+ } else {
+ let suggestion = format!(
+ "if let {0}({1}) = {2} {{ ... }}",
+ variant,
+ snippet(cx, binding.pat.span, "_"),
+ snippet(cx, var_arg.span, "_"),
+ );
+ diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::HasPlaceholders);
+ }
+ });
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for MapUnit {
+ fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
+ if stmt.span.from_expansion() {
+ return;
+ }
+
+ if let hir::StmtKind::Semi(expr) = stmt.kind {
+ if let Some(arglists) = method_chain_args(expr, &["map"]) {
+ lint_map_unit_fn(cx, stmt, expr, arglists[0]);
+ }
+ }
+ }
+}
--- /dev/null
- && !(is_type_diagnostic_item(cx, ty, sym::Option)
- || is_type_diagnostic_item(cx, ty, sym::Result)) =>
+use clippy_utils::consts::{constant, miri_to_const, Constant};
+use clippy_utils::diagnostics::{
+ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
+};
+use clippy_utils::higher;
+use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{
+ get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild,
+ meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
+ remove_blocks, strip_pat_refs,
+};
+use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
+use core::array;
+use core::iter::{once, ExactSizeIterator};
+use if_chain::if_chain;
+use rustc_ast::ast::{Attribute, LitKind};
+use rustc_errors::Applicability;
+use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{
+ self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
+ Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
+};
+use rustc_hir::{HirIdMap, HirIdSet};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty, TyS, VariantDef};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::{Span, Spanned};
+use rustc_span::sym;
+use std::cmp::Ordering;
+use std::collections::hash_map::Entry;
+use std::iter;
+use std::ops::Bound;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches with a single arm where an `if let`
+ /// will usually suffice.
+ ///
+ /// ### Why is this bad?
+ /// Just readability – `if let` nests less than a `match`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn bar(stool: &str) {}
+ /// # let x = Some("abc");
+ /// // Bad
+ /// match x {
+ /// Some(ref foo) => bar(foo),
+ /// _ => (),
+ /// }
+ ///
+ /// // Good
+ /// if let Some(ref foo) = x {
+ /// bar(foo);
+ /// }
+ /// ```
+ pub SINGLE_MATCH,
+ style,
+ "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches with two arms where an `if let else` will
+ /// usually suffice.
+ ///
+ /// ### Why is this bad?
+ /// Just readability – `if let` nests less than a `match`.
+ ///
+ /// ### Known problems
+ /// Personal style preferences may differ.
+ ///
+ /// ### Example
+ /// Using `match`:
+ ///
+ /// ```rust
+ /// # fn bar(foo: &usize) {}
+ /// # let other_ref: usize = 1;
+ /// # let x: Option<&usize> = Some(&1);
+ /// match x {
+ /// Some(ref foo) => bar(foo),
+ /// _ => bar(&other_ref),
+ /// }
+ /// ```
+ ///
+ /// Using `if let` with `else`:
+ ///
+ /// ```rust
+ /// # fn bar(foo: &usize) {}
+ /// # let other_ref: usize = 1;
+ /// # let x: Option<&usize> = Some(&1);
+ /// if let Some(ref foo) = x {
+ /// bar(foo);
+ /// } else {
+ /// bar(&other_ref);
+ /// }
+ /// ```
+ pub SINGLE_MATCH_ELSE,
+ pedantic,
+ "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches where all arms match a reference,
+ /// suggesting to remove the reference and deref the matched expression
+ /// instead. It also checks for `if let &foo = bar` blocks.
+ ///
+ /// ### Why is this bad?
+ /// It just makes the code less readable. That reference
+ /// destructuring adds nothing to the code.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// // Bad
+ /// match x {
+ /// &A(ref y) => foo(y),
+ /// &B => bar(),
+ /// _ => frob(&x),
+ /// }
+ ///
+ /// // Good
+ /// match *x {
+ /// A(ref y) => foo(y),
+ /// B => bar(),
+ /// _ => frob(x),
+ /// }
+ /// ```
+ pub MATCH_REF_PATS,
+ style,
+ "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches where match expression is a `bool`. It
+ /// suggests to replace the expression with an `if...else` block.
+ ///
+ /// ### Why is this bad?
+ /// It makes the code less readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn foo() {}
+ /// # fn bar() {}
+ /// let condition: bool = true;
+ /// match condition {
+ /// true => foo(),
+ /// false => bar(),
+ /// }
+ /// ```
+ /// Use if/else instead:
+ /// ```rust
+ /// # fn foo() {}
+ /// # fn bar() {}
+ /// let condition: bool = true;
+ /// if condition {
+ /// foo();
+ /// } else {
+ /// bar();
+ /// }
+ /// ```
+ pub MATCH_BOOL,
+ pedantic,
+ "a `match` on a boolean expression instead of an `if..else` block"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for overlapping match arms.
+ ///
+ /// ### Why is this bad?
+ /// It is likely to be an error and if not, makes the code
+ /// less obvious.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = 5;
+ /// match x {
+ /// 1..=10 => println!("1 ... 10"),
+ /// 5..=15 => println!("5 ... 15"),
+ /// _ => (),
+ /// }
+ /// ```
+ pub MATCH_OVERLAPPING_ARM,
+ style,
+ "a `match` with overlapping arms"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for arm which matches all errors with `Err(_)`
+ /// and take drastic actions like `panic!`.
+ ///
+ /// ### Why is this bad?
+ /// It is generally a bad practice, similar to
+ /// catching all exceptions in java with `catch(Exception)`
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x: Result<i32, &str> = Ok(3);
+ /// match x {
+ /// Ok(_) => println!("ok"),
+ /// Err(_) => panic!("err"),
+ /// }
+ /// ```
+ pub MATCH_WILD_ERR_ARM,
+ pedantic,
+ "a `match` with `Err(_)` arm and take drastic actions"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for match which is used to add a reference to an
+ /// `Option` value.
+ ///
+ /// ### Why is this bad?
+ /// Using `as_ref()` or `as_mut()` instead is shorter.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x: Option<()> = None;
+ ///
+ /// // Bad
+ /// let r: Option<&()> = match x {
+ /// None => None,
+ /// Some(ref v) => Some(v),
+ /// };
+ ///
+ /// // Good
+ /// let r: Option<&()> = x.as_ref();
+ /// ```
+ pub MATCH_AS_REF,
+ complexity,
+ "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for wildcard enum matches using `_`.
+ ///
+ /// ### Why is this bad?
+ /// New enum variants added by library updates can be missed.
+ ///
+ /// ### Known problems
+ /// Suggested replacements may be incorrect if guards exhaustively cover some
+ /// variants, and also may not use correct path to enum if it's not present in the current scope.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # enum Foo { A(usize), B(usize) }
+ /// # let x = Foo::B(1);
+ /// // Bad
+ /// match x {
+ /// Foo::A(_) => {},
+ /// _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match x {
+ /// Foo::A(_) => {},
+ /// Foo::B(_) => {},
+ /// }
+ /// ```
+ pub WILDCARD_ENUM_MATCH_ARM,
+ restriction,
+ "a wildcard enum match arm using `_`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for wildcard enum matches for a single variant.
+ ///
+ /// ### Why is this bad?
+ /// New enum variants added by library updates can be missed.
+ ///
+ /// ### Known problems
+ /// Suggested replacements may not use correct path to enum
+ /// if it's not present in the current scope.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # enum Foo { A, B, C }
+ /// # let x = Foo::B;
+ /// // Bad
+ /// match x {
+ /// Foo::A => {},
+ /// Foo::B => {},
+ /// _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match x {
+ /// Foo::A => {},
+ /// Foo::B => {},
+ /// Foo::C => {},
+ /// }
+ /// ```
+ pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ pedantic,
+ "a wildcard enum match for a single variant"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for wildcard pattern used with others patterns in same match arm.
+ ///
+ /// ### Why is this bad?
+ /// Wildcard pattern already covers any other pattern as it will match anyway.
+ /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// match "foo" {
+ /// "a" => {},
+ /// "bar" | _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match "foo" {
+ /// "a" => {},
+ /// _ => {},
+ /// }
+ /// ```
+ pub WILDCARD_IN_OR_PATTERNS,
+ complexity,
+ "a wildcard pattern used with others patterns in same match arm"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches being used to destructure a single-variant enum
+ /// or tuple struct where a `let` will suffice.
+ ///
+ /// ### Why is this bad?
+ /// Just readability – `let` doesn't nest, whereas a `match` does.
+ ///
+ /// ### Example
+ /// ```rust
+ /// enum Wrapper {
+ /// Data(i32),
+ /// }
+ ///
+ /// let wrapper = Wrapper::Data(42);
+ ///
+ /// let data = match wrapper {
+ /// Wrapper::Data(i) => i,
+ /// };
+ /// ```
+ ///
+ /// The correct use would be:
+ /// ```rust
+ /// enum Wrapper {
+ /// Data(i32),
+ /// }
+ ///
+ /// let wrapper = Wrapper::Data(42);
+ /// let Wrapper::Data(data) = wrapper;
+ /// ```
+ pub INFALLIBLE_DESTRUCTURING_MATCH,
+ style,
+ "a `match` statement with a single infallible arm instead of a `let`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for useless match that binds to only one value.
+ ///
+ /// ### Why is this bad?
+ /// Readability and needless complexity.
+ ///
+ /// ### Known problems
+ /// Suggested replacements may be incorrect when `match`
+ /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let a = 1;
+ /// # let b = 2;
+ ///
+ /// // Bad
+ /// match (a, b) {
+ /// (c, d) => {
+ /// // useless match
+ /// }
+ /// }
+ ///
+ /// // Good
+ /// let (c, d) = (a, b);
+ /// ```
+ pub MATCH_SINGLE_BINDING,
+ complexity,
+ "a match with a single binding instead of using `let` statement"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
+ ///
+ /// ### Why is this bad?
+ /// Correctness and readability. It's like having a wildcard pattern after
+ /// matching all enum variants explicitly.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # struct A { a: i32 }
+ /// let a = A { a: 5 };
+ ///
+ /// // Bad
+ /// match a {
+ /// A { a: 5, .. } => {},
+ /// _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match a {
+ /// A { a: 5 } => {},
+ /// _ => {},
+ /// }
+ /// ```
+ pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
+ restriction,
+ "a match on a struct that binds all fields but still uses the wildcard pattern"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Lint for redundant pattern matching over `Result`, `Option`,
+ /// `std::task::Poll` or `std::net::IpAddr`
+ ///
+ /// ### Why is this bad?
+ /// It's more concise and clear to just use the proper
+ /// utility function
+ ///
+ /// ### Known problems
+ /// This will change the drop order for the matched type. Both `if let` and
+ /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
+ /// value before entering the block. For most types this change will not matter, but for a few
+ /// types this will not be an acceptable change (e.g. locks). See the
+ /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
+ /// drop order.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::task::Poll;
+ /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ /// if let Ok(_) = Ok::<i32, i32>(42) {}
+ /// if let Err(_) = Err::<i32, i32>(42) {}
+ /// if let None = None::<()> {}
+ /// if let Some(_) = Some(42) {}
+ /// if let Poll::Pending = Poll::Pending::<()> {}
+ /// if let Poll::Ready(_) = Poll::Ready(42) {}
+ /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
+ /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
+ /// match Ok::<i32, i32>(42) {
+ /// Ok(_) => true,
+ /// Err(_) => false,
+ /// };
+ /// ```
+ ///
+ /// The more idiomatic use would be:
+ ///
+ /// ```rust
+ /// # use std::task::Poll;
+ /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ /// if Ok::<i32, i32>(42).is_ok() {}
+ /// if Err::<i32, i32>(42).is_err() {}
+ /// if None::<()>.is_none() {}
+ /// if Some(42).is_some() {}
+ /// if Poll::Pending::<()>.is_pending() {}
+ /// if Poll::Ready(42).is_ready() {}
+ /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+ /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+ /// Ok::<i32, i32>(42).is_ok();
+ /// ```
+ pub REDUNDANT_PATTERN_MATCHING,
+ style,
+ "use the proper utility function avoiding an `if let`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `match` or `if let` expressions producing a
+ /// `bool` that could be written using `matches!`
+ ///
+ /// ### Why is this bad?
+ /// Readability and needless complexity.
+ ///
+ /// ### Known problems
+ /// This lint falsely triggers, if there are arms with
+ /// `cfg` attributes that remove an arm evaluating to `false`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = Some(5);
+ ///
+ /// // Bad
+ /// let a = match x {
+ /// Some(0) => true,
+ /// _ => false,
+ /// };
+ ///
+ /// let a = if let Some(0) = x {
+ /// true
+ /// } else {
+ /// false
+ /// };
+ ///
+ /// // Good
+ /// let a = matches!(x, Some(0));
+ /// ```
+ pub MATCH_LIKE_MATCHES_MACRO,
+ style,
+ "a match that could be written with the matches! macro"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `match` with identical arm bodies.
+ ///
+ /// ### Why is this bad?
+ /// This is probably a copy & paste error. If arm bodies
+ /// are the same on purpose, you can factor them
+ /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
+ ///
+ /// ### Known problems
+ /// False positive possible with order dependent `match`
+ /// (see issue
+ /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => bar(), // <= oops
+ /// }
+ /// ```
+ ///
+ /// This should probably be
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => baz(), // <= fixed
+ /// }
+ /// ```
+ ///
+ /// or if the original code was not a typo:
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar | Baz => bar(), // <= shows the intent better
+ /// Quz => quz(),
+ /// }
+ /// ```
+ pub MATCH_SAME_ARMS,
+ pedantic,
+ "`match` with identical arm bodies"
+}
+
+#[derive(Default)]
+pub struct Matches {
+ msrv: Option<RustcVersion>,
+ infallible_destructuring_match_linted: bool,
+}
+
+impl Matches {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self {
+ msrv,
+ ..Matches::default()
+ }
+ }
+}
+
+impl_lint_pass!(Matches => [
+ SINGLE_MATCH,
+ MATCH_REF_PATS,
+ MATCH_BOOL,
+ SINGLE_MATCH_ELSE,
+ MATCH_OVERLAPPING_ARM,
+ MATCH_WILD_ERR_ARM,
+ MATCH_AS_REF,
+ WILDCARD_ENUM_MATCH_ARM,
+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ WILDCARD_IN_OR_PATTERNS,
+ MATCH_SINGLE_BINDING,
+ INFALLIBLE_DESTRUCTURING_MATCH,
+ REST_PAT_IN_FULLY_BOUND_STRUCTS,
+ REDUNDANT_PATTERN_MATCHING,
+ MATCH_LIKE_MATCHES_MACRO,
+ MATCH_SAME_ARMS,
+]);
+
+impl<'tcx> LateLintPass<'tcx> for Matches {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
+ return;
+ }
+
+ redundant_pattern_match::check(cx, expr);
+
+ if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
+ if !check_match_like_matches(cx, expr) {
+ lint_match_arms(cx, expr);
+ }
+ } else {
+ lint_match_arms(cx, expr);
+ }
+
+ if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
+ check_single_match(cx, ex, arms, expr);
+ check_match_bool(cx, ex, arms, expr);
+ check_overlapping_arms(cx, ex, arms);
+ check_wild_err_arm(cx, ex, arms);
+ check_wild_enum_match(cx, ex, arms);
+ check_match_as_ref(cx, ex, arms, expr);
+ check_wild_in_or_pats(cx, arms);
+
+ if self.infallible_destructuring_match_linted {
+ self.infallible_destructuring_match_linted = false;
+ } else {
+ check_match_single_binding(cx, ex, arms, expr);
+ }
+ }
+ if let ExprKind::Match(ex, arms, _) = expr.kind {
+ check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
+ }
+ if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
+ check_match_ref_pats(cx, let_expr, once(let_pat), expr);
+ }
+ }
+
+ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
+ if_chain! {
+ if !in_external_macro(cx.sess(), local.span);
+ if !in_macro(local.span);
+ if let Some(expr) = local.init;
+ if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
+ if arms.len() == 1 && arms[0].guard.is_none();
+ if let PatKind::TupleStruct(
+ QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
+ if args.len() == 1;
+ if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
+ let body = remove_blocks(arms[0].body);
+ if path_to_local_id(body, arg);
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ self.infallible_destructuring_match_linted = true;
+ span_lint_and_sugg(
+ cx,
+ INFALLIBLE_DESTRUCTURING_MATCH,
+ local.span,
+ "you seem to be trying to use `match` to destructure a single infallible pattern. \
+ Consider using `let`",
+ "try this",
+ format!(
+ "let {}({}) = {};",
+ snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
+ snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
+ snippet_with_applicability(cx, target.span, "..", &mut applicability),
+ ),
+ applicability,
+ );
+ }
+ }
+ }
+
+ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
+ if_chain! {
+ if !in_external_macro(cx.sess(), pat.span);
+ if !in_macro(pat.span);
+ if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
+ if let Some(def_id) = path.res.opt_def_id();
+ let ty = cx.tcx.type_of(def_id);
+ if let ty::Adt(def, _) = ty.kind();
+ if def.is_struct() || def.is_union();
+ if fields.len() == def.non_enum_variant().fields.len();
+
+ then {
+ span_lint_and_help(
+ cx,
+ REST_PAT_IN_FULLY_BOUND_STRUCTS,
+ pat.span,
+ "unnecessary use of `..` pattern in struct binding. All fields were already bound",
+ None,
+ "consider removing `..` from this binding",
+ );
+ }
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
+
+#[rustfmt::skip]
+fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
+ if in_macro(expr.span) {
+ // Don't lint match expressions present in
+ // macro_rules! block
+ return;
+ }
+ if let PatKind::Or(..) = arms[0].pat.kind {
+ // don't lint for or patterns for now, this makes
+ // the lint noisy in unnecessary situations
+ return;
+ }
+ let els = arms[1].body;
+ let els = if is_unit_expr(remove_blocks(els)) {
+ None
+ } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
+ if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
+ // single statement/expr "else" block, don't lint
+ return;
+ }
+ // block with 2+ statements or 1 expr and 1+ statement
+ Some(els)
+ } else {
+ // not a block, don't lint
+ return;
+ };
+
+ let ty = cx.typeck_results().expr_ty(ex);
+ if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
+ check_single_match_single_pattern(cx, ex, arms, expr, els);
+ check_single_match_opt_like(cx, ex, arms, expr, ty, els);
+ }
+ }
+}
+
+fn check_single_match_single_pattern(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ arms: &[Arm<'_>],
+ expr: &Expr<'_>,
+ els: Option<&Expr<'_>>,
+) {
+ if is_wild(arms[1].pat) {
+ report_single_match_single_pattern(cx, ex, arms, expr, els);
+ }
+}
+
+fn report_single_match_single_pattern(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ arms: &[Arm<'_>],
+ expr: &Expr<'_>,
+ els: Option<&Expr<'_>>,
+) {
+ let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
+ let els_str = els.map_or(String::new(), |els| {
+ format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
+ });
+
+ let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
+ let (msg, sugg) = if_chain! {
+ if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
+ let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
+ if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
+ if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
+ if ty.is_integral() || ty.is_char() || ty.is_str()
+ || (implements_trait(cx, ty, spe_trait_id, &[])
+ && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
+ then {
+ // scrutinee derives PartialEq and the pattern is a constant.
+ let pat_ref_count = match pat.kind {
+ // string literals are already a reference.
+ PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
+ _ => pat_ref_count,
+ };
+ // References are only implicitly added to the pattern, so no overflow here.
+ // e.g. will work: match &Some(_) { Some(_) => () }
+ // will not: match Some(_) { &Some(_) => () }
+ let ref_count_diff = ty_ref_count - pat_ref_count;
+
+ // Try to remove address of expressions first.
+ let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
+ let ref_count_diff = ref_count_diff - removed;
+
+ let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
+ let sugg = format!(
+ "if {} == {}{} {}{}",
+ snippet(cx, ex.span, ".."),
+ // PartialEq for different reference counts may not exist.
+ "&".repeat(ref_count_diff),
+ snippet(cx, arms[0].pat.span, ".."),
+ expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
+ els_str,
+ );
+ (msg, sugg)
+ } else {
+ let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
+ let sugg = format!(
+ "if let {} = {} {}{}",
+ snippet(cx, arms[0].pat.span, ".."),
+ snippet(cx, ex.span, ".."),
+ expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
+ els_str,
+ );
+ (msg, sugg)
+ }
+ };
+
+ span_lint_and_sugg(
+ cx,
+ lint,
+ expr.span,
+ msg,
+ "try this",
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+}
+
+fn check_single_match_opt_like(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ arms: &[Arm<'_>],
+ expr: &Expr<'_>,
+ ty: Ty<'_>,
+ els: Option<&Expr<'_>>,
+) {
+ // list of candidate `Enum`s we know will never get any more members
+ let candidates = &[
+ (&paths::COW, "Borrowed"),
+ (&paths::COW, "Cow::Borrowed"),
+ (&paths::COW, "Cow::Owned"),
+ (&paths::COW, "Owned"),
+ (&paths::OPTION, "None"),
+ (&paths::RESULT, "Err"),
+ (&paths::RESULT, "Ok"),
+ ];
+
+ let path = match arms[1].pat.kind {
+ PatKind::TupleStruct(ref path, inner, _) => {
+ // Contains any non wildcard patterns (e.g., `Err(err)`)?
+ if !inner.iter().all(is_wild) {
+ return;
+ }
+ rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
+ },
+ PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
+ PatKind::Path(ref path) => {
+ rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
+ },
+ _ => return,
+ };
+
+ for &(ty_path, pat_path) in candidates {
+ if path == *pat_path && match_type(cx, ty, ty_path) {
+ report_single_match_single_pattern(cx, ex, arms, expr, els);
+ }
+ }
+}
+
+fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ // Type of expression is `bool`.
+ if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
+ span_lint_and_then(
+ cx,
+ MATCH_BOOL,
+ expr.span,
+ "you seem to be trying to match on a boolean expression",
+ move |diag| {
+ if arms.len() == 2 {
+ // no guards
+ let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
+ if let ExprKind::Lit(ref lit) = arm_bool.kind {
+ match lit.node {
+ LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
+ LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ if let Some((true_expr, false_expr)) = exprs {
+ let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
+ (false, false) => Some(format!(
+ "if {} {} else {}",
+ snippet(cx, ex.span, "b"),
+ expr_block(cx, true_expr, None, "..", Some(expr.span)),
+ expr_block(cx, false_expr, None, "..", Some(expr.span))
+ )),
+ (false, true) => Some(format!(
+ "if {} {}",
+ snippet(cx, ex.span, "b"),
+ expr_block(cx, true_expr, None, "..", Some(expr.span))
+ )),
+ (true, false) => {
+ let test = Sugg::hir(cx, ex, "..");
+ Some(format!(
+ "if {} {}",
+ !test,
+ expr_block(cx, false_expr, None, "..", Some(expr.span))
+ ))
+ },
+ (true, true) => None,
+ };
+
+ if let Some(sugg) = sugg {
+ diag.span_suggestion(
+ expr.span,
+ "consider using an `if`/`else` expression",
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ }
+ },
+ );
+ }
+}
+
+fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
+ if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
+ let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
+ let type_ranges = type_ranges(&ranges);
+ if !type_ranges.is_empty() {
+ if let Some((start, end)) = overlapping(&type_ranges) {
+ span_lint_and_note(
+ cx,
+ MATCH_OVERLAPPING_ARM,
+ start.span,
+ "some ranges overlap",
+ Some(end.span),
+ "overlaps with this",
+ );
+ }
+ }
+ }
+}
+
+fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
+ let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
+ if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
+ for arm in arms {
+ if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
+ let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
+ if path_str == "Err" {
+ let mut matching_wild = inner.iter().any(is_wild);
+ let mut ident_bind_name = String::from("_");
+ if !matching_wild {
+ // Looking for unused bindings (i.e.: `_e`)
+ for pat in inner.iter() {
+ if let PatKind::Binding(_, id, ident, None) = pat.kind {
+ if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
+ ident_bind_name = (&ident.name.as_str()).to_string();
+ matching_wild = true;
+ }
+ }
+ }
+ }
+ if_chain! {
+ if matching_wild;
+ if let ExprKind::Block(block, _) = arm.body.kind;
+ if is_panic_block(block);
+ then {
+ // `Err(_)` or `Err(_e)` arm with `panic!` found
+ span_lint_and_note(cx,
+ MATCH_WILD_ERR_ARM,
+ arm.pat.span,
+ &format!("`Err({})` matches all errors", &ident_bind_name),
+ None,
+ "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+enum CommonPrefixSearcher<'a> {
+ None,
+ Path(&'a [PathSegment<'a>]),
+ Mixed,
+}
+impl CommonPrefixSearcher<'a> {
+ fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
+ match path {
+ [path @ .., _] => self.with_prefix(path),
+ [] => (),
+ }
+ }
+
+ fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
+ match self {
+ Self::None => *self = Self::Path(path),
+ Self::Path(self_path)
+ if path
+ .iter()
+ .map(|p| p.ident.name)
+ .eq(self_path.iter().map(|p| p.ident.name)) => {},
+ Self::Path(_) => *self = Self::Mixed,
+ Self::Mixed => (),
+ }
+ }
+}
+
+fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
+ let attrs = cx.tcx.get_attrs(variant_def.def_id);
+ clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
+ let ty = cx.typeck_results().expr_ty(ex).peel_refs();
+ let adt_def = match ty.kind() {
+ ty::Adt(adt_def, _)
+ if adt_def.is_enum()
++ && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) =>
+ {
+ adt_def
+ },
+ _ => return,
+ };
+
+ // First pass - check for violation, but don't do much book-keeping because this is hopefully
+ // the uncommon case, and the book-keeping is slightly expensive.
+ let mut wildcard_span = None;
+ let mut wildcard_ident = None;
+ let mut has_non_wild = false;
+ for arm in arms {
+ match peel_hir_pat_refs(arm.pat).0.kind {
+ PatKind::Wild => wildcard_span = Some(arm.pat.span),
+ PatKind::Binding(_, _, ident, None) => {
+ wildcard_span = Some(arm.pat.span);
+ wildcard_ident = Some(ident);
+ },
+ _ => has_non_wild = true,
+ }
+ }
+ let wildcard_span = match wildcard_span {
+ Some(x) if has_non_wild => x,
+ _ => return,
+ };
+
+ // Accumulate the variants which should be put in place of the wildcard because they're not
+ // already covered.
+ let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
+ let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
+
+ let mut path_prefix = CommonPrefixSearcher::None;
+ for arm in arms {
+ // Guards mean that this case probably isn't exhaustively covered. Technically
+ // this is incorrect, as we should really check whether each variant is exhaustively
+ // covered by the set of guards that cover it, but that's really hard to do.
+ recurse_or_patterns(arm.pat, |pat| {
+ let path = match &peel_hir_pat_refs(pat).0.kind {
+ PatKind::Path(path) => {
+ #[allow(clippy::match_same_arms)]
+ let id = match cx.qpath_res(path, pat.hir_id) {
+ Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
+ Res::Def(_, id) => id,
+ _ => return,
+ };
+ if arm.guard.is_none() {
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ path
+ },
+ PatKind::TupleStruct(path, patterns, ..) => {
+ if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ }
+ path
+ },
+ PatKind::Struct(path, patterns, ..) => {
+ if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
+ missing_variants.retain(|e| e.def_id != id);
+ }
+ }
+ path
+ },
+ _ => return,
+ };
+ match path {
+ QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
+ QPath::TypeRelative(
+ hir::Ty {
+ kind: TyKind::Path(QPath::Resolved(_, path)),
+ ..
+ },
+ _,
+ ) => path_prefix.with_prefix(path.segments),
+ _ => (),
+ }
+ });
+ }
+
+ let format_suggestion = |variant: &VariantDef| {
+ format!(
+ "{}{}{}{}",
+ if let Some(ident) = wildcard_ident {
+ format!("{} @ ", ident.name)
+ } else {
+ String::new()
+ },
+ if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
+ let mut s = String::new();
+ for seg in path_prefix {
+ s.push_str(&seg.ident.as_str());
+ s.push_str("::");
+ }
+ s
+ } else {
+ let mut s = cx.tcx.def_path_str(adt_def.did);
+ s.push_str("::");
+ s
+ },
+ variant.ident.name,
+ match variant.ctor_kind {
+ CtorKind::Fn if variant.fields.len() == 1 => "(_)",
+ CtorKind::Fn => "(..)",
+ CtorKind::Const => "",
+ CtorKind::Fictive => "{ .. }",
+ }
+ )
+ };
+
+ match missing_variants.as_slice() {
+ [] => (),
+ [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
+ cx,
+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ wildcard_span,
+ "wildcard matches only a single variant and will also match any future added variants",
+ "try this",
+ format_suggestion(x),
+ Applicability::MaybeIncorrect,
+ ),
+ variants => {
+ let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
+ let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
+ suggestions.push("_".into());
+ "wildcard matches known variants and will also match future added variants"
+ } else {
+ "wildcard match will also match any future added variants"
+ };
+
+ span_lint_and_sugg(
+ cx,
+ WILDCARD_ENUM_MATCH_ARM,
+ wildcard_span,
+ message,
+ "try this",
+ suggestions.join(" | "),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ };
+}
+
+// If the block contains only a `panic!` macro (as expression or statement)
+fn is_panic_block(block: &Block<'_>) -> bool {
+ match (&block.expr, block.stmts.len(), block.stmts.first()) {
+ (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(),
+ (&None, 1, Some(stmt)) => {
+ is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
+ },
+ _ => false,
+ }
+}
+
+fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
+where
+ 'b: 'a,
+ I: Clone + Iterator<Item = &'a Pat<'b>>,
+{
+ if !has_only_ref_pats(pats.clone()) {
+ return;
+ }
+
+ let (first_sugg, msg, title);
+ let span = ex.span.source_callsite();
+ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
+ first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
+ msg = "try";
+ title = "you don't need to add `&` to both the expression and the patterns";
+ } else {
+ first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
+ msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
+ title = "you don't need to add `&` to all patterns";
+ }
+
+ let remaining_suggs = pats.filter_map(|pat| {
+ if let PatKind::Ref(refp, _) = pat.kind {
+ Some((pat.span, snippet(cx, refp.span, "..").to_string()))
+ } else {
+ None
+ }
+ });
+
+ span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
+ if !expr.span.from_expansion() {
+ multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
+ }
+ });
+}
+
+fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
+ let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
+ is_ref_some_arm(cx, &arms[1])
+ } else if is_none_arm(cx, &arms[1]) {
+ is_ref_some_arm(cx, &arms[0])
+ } else {
+ None
+ };
+ if let Some(rb) = arm_ref {
+ let suggestion = if rb == BindingAnnotation::Ref {
+ "as_ref"
+ } else {
+ "as_mut"
+ };
+
+ let output_ty = cx.typeck_results().expr_ty(expr);
+ let input_ty = cx.typeck_results().expr_ty(ex);
+
+ let cast = if_chain! {
+ if let ty::Adt(_, substs) = input_ty.kind();
+ let input_ty = substs.type_at(0);
+ if let ty::Adt(_, substs) = output_ty.kind();
+ let output_ty = substs.type_at(0);
+ if let ty::Ref(_, output_ty, _) = *output_ty.kind();
+ if input_ty != output_ty;
+ then {
+ ".map(|x| x as _)"
+ } else {
+ ""
+ }
+ };
+
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ MATCH_AS_REF,
+ expr.span,
+ &format!("use `{}()` instead", suggestion),
+ "try this",
+ format!(
+ "{}.{}(){}",
+ snippet_with_applicability(cx, ex.span, "_", &mut applicability),
+ suggestion,
+ cast,
+ ),
+ applicability,
+ );
+ }
+ }
+}
+
+fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
+ for arm in arms {
+ if let PatKind::Or(fields) = arm.pat.kind {
+ // look for multiple fields in this arm that contains at least one Wild pattern
+ if fields.len() > 1 && fields.iter().any(is_wild) {
+ span_lint_and_help(
+ cx,
+ WILDCARD_IN_OR_PATTERNS,
+ arm.pat.span,
+ "wildcard pattern covers any other pattern as it will match anyway",
+ None,
+ "consider handling `_` separately",
+ );
+ }
+ }
+ }
+}
+
+/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
+fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ if let Some(higher::IfLet {
+ let_pat,
+ let_expr,
+ if_then,
+ if_else: Some(if_else),
+ }) = higher::IfLet::hir(cx, expr)
+ {
+ return find_matches_sugg(
+ cx,
+ let_expr,
+ array::IntoIter::new([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
+ expr,
+ true,
+ );
+ }
+
+ if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
+ return find_matches_sugg(
+ cx,
+ scrut,
+ arms.iter().map(|arm| {
+ (
+ cx.tcx.hir().attrs(arm.hir_id),
+ Some(arm.pat),
+ arm.body,
+ arm.guard.as_ref(),
+ )
+ }),
+ expr,
+ false,
+ );
+ }
+
+ false
+}
+
+/// Lint a `match` or `if let` for replacement by `matches!`
+fn find_matches_sugg<'a, 'b, I>(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ mut iter: I,
+ expr: &Expr<'_>,
+ is_if_let: bool,
+) -> bool
+where
+ 'b: 'a,
+ I: Clone
+ + DoubleEndedIterator
+ + ExactSizeIterator
+ + Iterator<
+ Item = (
+ &'a [Attribute],
+ Option<&'a Pat<'b>>,
+ &'a Expr<'b>,
+ Option<&'a Guard<'b>>,
+ ),
+ >,
+{
+ if_chain! {
+ if iter.len() >= 2;
+ if cx.typeck_results().expr_ty(expr).is_bool();
+ if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
+ let iter_without_last = iter.clone();
+ if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
+ if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
+ if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
+ if b0 != b1;
+ if first_guard.is_none() || iter.len() == 0;
+ if first_attrs.is_empty();
+ if iter
+ .all(|arm| {
+ find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
+ });
+ then {
+ if let Some(last_pat) = last_pat_opt {
+ if !is_wild(last_pat) {
+ return false;
+ }
+ }
+
+ // The suggestion may be incorrect, because some arms can have `cfg` attributes
+ // evaluated into `false` and so such arms will be stripped before.
+ let mut applicability = Applicability::MaybeIncorrect;
+ let pat = {
+ use itertools::Itertools as _;
+ iter_without_last
+ .filter_map(|arm| {
+ let pat_span = arm.1?.span;
+ Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
+ })
+ .join(" | ")
+ };
+ let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
+ format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
+ } else {
+ pat
+ };
+
+ // strip potential borrows (#6503), but only if the type is a reference
+ let mut ex_new = ex;
+ if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
+ if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
+ ex_new = ex_inner;
+ }
+ };
+ span_lint_and_sugg(
+ cx,
+ MATCH_LIKE_MATCHES_MACRO,
+ expr.span,
+ &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
+ "try this",
+ format!(
+ "{}matches!({}, {})",
+ if b0 { "" } else { "!" },
+ snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
+ pat_and_guard,
+ ),
+ applicability,
+ );
+ true
+ } else {
+ false
+ }
+ }
+}
+
+/// Extract a `bool` or `{ bool }`
+fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
+ match ex {
+ ExprKind::Lit(Spanned {
+ node: LitKind::Bool(b), ..
+ }) => Some(*b),
+ ExprKind::Block(
+ rustc_hir::Block {
+ stmts: &[],
+ expr: Some(exp),
+ ..
+ },
+ _,
+ ) if is_if_let => {
+ if let ExprKind::Lit(Spanned {
+ node: LitKind::Bool(b), ..
+ }) = exp.kind
+ {
+ Some(b)
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
+ return;
+ }
+
+ // HACK:
+ // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
+ // to prevent false positives as there is currently no better way to detect if code was excluded by
+ // a macro. See PR #6435
+ if_chain! {
+ if let Some(match_snippet) = snippet_opt(cx, expr.span);
+ if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
+ if let Some(ex_snippet) = snippet_opt(cx, ex.span);
+ let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
+ if rest_snippet.contains("=>");
+ then {
+ // The code it self contains another thick arrow "=>"
+ // -> Either another arm or a comment
+ return;
+ }
+ }
+
+ let matched_vars = ex.span;
+ let bind_names = arms[0].pat.span;
+ let match_body = remove_blocks(arms[0].body);
+ let mut snippet_body = if match_body.span.from_expansion() {
+ Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
+ } else {
+ snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
+ };
+
+ // Do we need to add ';' to suggestion ?
+ match match_body.kind {
+ ExprKind::Block(block, _) => {
+ // macro + expr_ty(body) == ()
+ if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
+ snippet_body.push(';');
+ }
+ },
+ _ => {
+ // expr_ty(body) == ()
+ if cx.typeck_results().expr_ty(match_body).is_unit() {
+ snippet_body.push(';');
+ }
+ },
+ }
+
+ let mut applicability = Applicability::MaybeIncorrect;
+ match arms[0].pat.kind {
+ PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
+ // If this match is in a local (`let`) stmt
+ let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
+ (
+ parent_let_node.span,
+ format!(
+ "let {} = {};\n{}let {} = {};",
+ snippet_with_applicability(cx, bind_names, "..", &mut applicability),
+ snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
+ " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
+ snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
+ snippet_body
+ ),
+ )
+ } else {
+ // If we are in closure, we need curly braces around suggestion
+ let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
+ let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
+ if let Some(parent_expr) = get_parent_expr(cx, expr) {
+ if let ExprKind::Closure(..) = parent_expr.kind {
+ cbrace_end = format!("\n{}}}", indent);
+ // Fix body indent due to the closure
+ indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
+ cbrace_start = format!("{{\n{}", indent);
+ }
+ }
+ // If the parent is already an arm, and the body is another match statement,
+ // we need curly braces around suggestion
+ let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
+ if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
+ if let ExprKind::Match(..) = arm.body.kind {
+ cbrace_end = format!("\n{}}}", indent);
+ // Fix body indent due to the match
+ indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
+ cbrace_start = format!("{{\n{}", indent);
+ }
+ }
+ (
+ expr.span,
+ format!(
+ "{}let {} = {};\n{}{}{}",
+ cbrace_start,
+ snippet_with_applicability(cx, bind_names, "..", &mut applicability),
+ snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
+ indent,
+ snippet_body,
+ cbrace_end
+ ),
+ )
+ };
+ span_lint_and_sugg(
+ cx,
+ MATCH_SINGLE_BINDING,
+ target_span,
+ "this match could be written as a `let` statement",
+ "consider using `let` statement",
+ sugg,
+ applicability,
+ );
+ },
+ PatKind::Wild => {
+ if ex.can_have_side_effects() {
+ let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
+ let sugg = format!(
+ "{};\n{}{}",
+ snippet_with_applicability(cx, ex.span, "..", &mut applicability),
+ indent,
+ snippet_body
+ );
+ span_lint_and_sugg(
+ cx,
+ MATCH_SINGLE_BINDING,
+ expr.span,
+ "this match could be replaced by its scrutinee and body",
+ "consider using the scrutinee and body instead",
+ sugg,
+ applicability,
+ );
+ } else {
+ span_lint_and_sugg(
+ cx,
+ MATCH_SINGLE_BINDING,
+ expr.span,
+ "this match could be replaced by its body itself",
+ "consider using the match body instead",
+ snippet_body,
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ _ => (),
+ }
+}
+
+/// Returns true if the `ex` match expression is in a local (`let`) statement
+fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
+ let map = &cx.tcx.hir();
+ if_chain! {
+ if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
+ if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
+ then {
+ return Some(parent_let_expr);
+ }
+ }
+ None
+}
+
+/// Gets all arms that are unbounded `PatRange`s.
+fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
+ arms.iter()
+ .filter_map(|arm| {
+ if let Arm { pat, guard: None, .. } = *arm {
+ if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
+ let lhs = match lhs {
+ Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
+ None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
+ };
+ let rhs = match rhs {
+ Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
+ None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
+ };
+ let rhs = match range_end {
+ RangeEnd::Included => Bound::Included(rhs),
+ RangeEnd::Excluded => Bound::Excluded(rhs),
+ };
+ return Some(SpannedRange {
+ span: pat.span,
+ node: (lhs, rhs),
+ });
+ }
+
+ if let PatKind::Lit(value) = pat.kind {
+ let value = constant(cx, cx.typeck_results(), value)?.0;
+ return Some(SpannedRange {
+ span: pat.span,
+ node: (value.clone(), Bound::Included(value)),
+ });
+ }
+ }
+ None
+ })
+ .collect()
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct SpannedRange<T> {
+ pub span: Span,
+ pub node: (T, Bound<T>),
+}
+
+type TypedRanges = Vec<SpannedRange<u128>>;
+
+/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
+/// and other types than
+/// `Uint` and `Int` probably don't make sense.
+fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
+ ranges
+ .iter()
+ .filter_map(|range| match range.node {
+ (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
+ span: range.span,
+ node: (start, Bound::Included(end)),
+ }),
+ (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
+ span: range.span,
+ node: (start, Bound::Excluded(end)),
+ }),
+ (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
+ span: range.span,
+ node: (start, Bound::Unbounded),
+ }),
+ _ => None,
+ })
+ .collect()
+}
+
+// Checks if arm has the form `None => None`
+fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
+}
+
+// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
+fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
+ if_chain! {
+ if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
+ if is_lang_ctor(cx, qpath, OptionSome);
+ if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
+ if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
+ if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
+ if let ExprKind::Path(ref some_path) = e.kind;
+ if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
+ if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
+ if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
+ then {
+ return Some(rb)
+ }
+ }
+ None
+}
+
+fn has_only_ref_pats<'a, 'b, I>(pats: I) -> bool
+where
+ 'b: 'a,
+ I: Iterator<Item = &'a Pat<'b>>,
+{
+ let mut at_least_one_is_true = false;
+ for opt in pats.map(|pat| match pat.kind {
+ PatKind::Ref(..) => Some(true), // &-patterns
+ PatKind::Wild => Some(false), // an "anything" wildcard is also fine
+ _ => None, // any other pattern is not fine
+ }) {
+ if let Some(inner) = opt {
+ if inner {
+ at_least_one_is_true = true;
+ }
+ } else {
+ return false;
+ }
+ }
+ at_least_one_is_true
+}
+
+pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
+where
+ T: Copy + Ord,
+{
+ #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+ enum Kind<'a, T> {
+ Start(T, &'a SpannedRange<T>),
+ End(Bound<T>, &'a SpannedRange<T>),
+ }
+
+ impl<'a, T: Copy> Kind<'a, T> {
+ fn range(&self) -> &'a SpannedRange<T> {
+ match *self {
+ Kind::Start(_, r) | Kind::End(_, r) => r,
+ }
+ }
+
+ fn value(self) -> Bound<T> {
+ match self {
+ Kind::Start(t, _) => Bound::Included(t),
+ Kind::End(t, _) => t,
+ }
+ }
+ }
+
+ impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+ }
+
+ impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ match (self.value(), other.value()) {
+ (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
+ // Range patterns cannot be unbounded (yet)
+ (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
+ (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
+ Ordering::Equal => Ordering::Greater,
+ other => other,
+ },
+ (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
+ Ordering::Equal => Ordering::Less,
+ other => other,
+ },
+ }
+ }
+ }
+
+ let mut values = Vec::with_capacity(2 * ranges.len());
+
+ for r in ranges {
+ values.push(Kind::Start(r.node.0, r));
+ values.push(Kind::End(r.node.1, r));
+ }
+
+ values.sort();
+
+ for (a, b) in iter::zip(&values, values.iter().skip(1)) {
+ match (a, b) {
+ (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
+ if ra.node != rb.node {
+ return Some((ra, rb));
+ }
+ },
+ (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
+ _ => {
+ // skip if the range `a` is completely included into the range `b`
+ if let Ordering::Equal | Ordering::Less = a.cmp(b) {
+ let kind_a = Kind::End(a.range().node.1, a.range());
+ let kind_b = Kind::End(b.range().node.1, b.range());
+ if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
+ return None;
+ }
+ }
+ return Some((a.range(), b.range()));
+ },
+ }
+ }
+
+ None
+}
+
+mod redundant_pattern_match {
+ use super::REDUNDANT_PATTERN_MATCHING;
+ use clippy_utils::diagnostics::span_lint_and_then;
+ use clippy_utils::higher;
+ use clippy_utils::source::{snippet, snippet_with_applicability};
+ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+ use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
+ use if_chain::if_chain;
+ use rustc_ast::ast::LitKind;
+ use rustc_data_structures::fx::FxHashSet;
+ use rustc_errors::Applicability;
+ use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+ use rustc_hir::{
+ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+ Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
+ };
+ use rustc_lint::LateContext;
+ use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
+ use rustc_span::sym;
+
+ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if let Some(higher::IfLet {
+ if_else,
+ let_pat,
+ let_expr,
+ ..
+ }) = higher::IfLet::hir(cx, expr)
+ {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
+ }
+ if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
+ find_sugg_for_match(cx, expr, op, arms);
+ }
+ if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
+ }
+ }
+
+ /// Checks if the drop order for a type matters. Some std types implement drop solely to
+ /// deallocate memory. For these types, and composites containing them, changing the drop order
+ /// won't result in any observable side effects.
+ fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+ }
+
+ fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+ if !seen.insert(ty) {
+ return false;
+ }
+ if !ty.needs_drop(cx.tcx, cx.param_env) {
+ false
+ } else if !cx
+ .tcx
+ .lang_items()
+ .drop_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // This type doesn't implement drop, so no side effects here.
+ // Check if any component type has any.
+ match ty.kind() {
+ ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
+ ty::Adt(adt, subs) => adt
+ .all_fields()
+ .map(|f| f.ty(cx.tcx, subs))
+ .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ _ => true,
+ }
+ }
+ // Check for std types which implement drop, but only for memory allocation.
+ else if is_type_diagnostic_item(cx, ty, sym::Vec)
+ || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+ || is_type_diagnostic_item(cx, ty, sym::Rc)
+ || is_type_diagnostic_item(cx, ty, sym::Arc)
+ || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+ || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
+ || is_type_diagnostic_item(cx, ty, sym::LinkedList)
+ || match_type(cx, ty, &paths::WEAK_RC)
+ || match_type(cx, ty, &paths::WEAK_ARC)
+ {
+ // Check all of the generic arguments.
+ if let ty::Adt(_, subs) = ty.kind() {
+ subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
+ } else {
+ true
+ }
+ } else {
+ true
+ }
+ }
+
+ // Extract the generic arguments out of a type
+ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+ if_chain! {
+ if let ty::Adt(_, subs) = ty.kind();
+ if let Some(sub) = subs.get(index);
+ if let GenericArgKind::Type(sub_ty) = sub.unpack();
+ then {
+ Some(sub_ty)
+ } else {
+ None
+ }
+ }
+ }
+
+ // Checks if there are any temporaries created in the given expression for which drop order
+ // matters.
+ fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ struct V<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ res: bool,
+ }
+ impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ match expr.kind {
+ // Taking the reference of a value leaves a temporary
+ // e.g. In `&String::new()` the string is a temporary value.
+ // Remaining fields are temporary values
+ // e.g. In `(String::new(), 0).1` the string is a temporary value.
+ ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+ if !matches!(expr.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+ self.res = true;
+ } else {
+ self.visit_expr(expr);
+ }
+ }
+ },
+ // the base type is alway taken by reference.
+ // e.g. In `(vec![0])[0]` the vector is a temporary value.
+ ExprKind::Index(base, index) => {
+ if !matches!(base.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+ self.res = true;
+ } else {
+ self.visit_expr(base);
+ }
+ }
+ self.visit_expr(index);
+ },
+ // Method calls can take self by reference.
+ // e.g. In `String::new().len()` the string is a temporary value.
+ ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
+ if !matches!(self_arg.kind, ExprKind::Path(_)) {
+ let self_by_ref = self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+ if self_by_ref
+ && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
+ {
+ self.res = true;
+ } else {
+ self.visit_expr(self_arg);
+ }
+ }
+ args.iter().for_each(|arg| self.visit_expr(arg));
+ },
+ // Either explicitly drops values, or changes control flow.
+ ExprKind::DropTemps(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Break(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Block(Block { expr: None, .. }, _)
+ | ExprKind::Loop(..) => (),
+
+ // Only consider the final expression.
+ ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+ _ => walk_expr(self, expr),
+ }
+ }
+ }
+
+ let mut v = V { cx, res: false };
+ v.visit_expr(expr);
+ v.res
+ }
+
+ fn find_sugg_for_if_let<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ let_pat: &Pat<'_>,
+ let_expr: &'tcx Expr<'_>,
+ keyword: &'static str,
+ has_else: bool,
+ ) {
+ // also look inside refs
+ let mut kind = &let_pat.kind;
+ // if we have &None for example, peel it so we can detect "if let None = x"
+ if let PatKind::Ref(inner, _mutability) = kind {
+ kind = &inner.kind;
+ }
+ let op_ty = cx.typeck_results().expr_ty(let_expr);
+ // Determine which function should be used, and the type contained by the corresponding
+ // variant.
+ let (good_method, inner_ty) = match kind {
+ PatKind::TupleStruct(ref path, [sub_pat], _) => {
+ if let PatKind::Wild = sub_pat.kind {
+ if is_lang_ctor(cx, path, ResultOk) {
+ ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+ } else if is_lang_ctor(cx, path, ResultErr) {
+ ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+ } else if is_lang_ctor(cx, path, OptionSome) {
+ ("is_some()", op_ty)
+ } else if is_lang_ctor(cx, path, PollReady) {
+ ("is_ready()", op_ty)
+ } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
+ ("is_ipv4()", op_ty)
+ } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
+ ("is_ipv6()", op_ty)
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ },
+ PatKind::Path(ref path) => {
+ let method = if is_lang_ctor(cx, path, OptionNone) {
+ "is_none()"
+ } else if is_lang_ctor(cx, path, PollPending) {
+ "is_pending()"
+ } else {
+ return;
+ };
+ // `None` and `Pending` don't have an inner type.
+ (method, cx.tcx.types.unit)
+ },
+ _ => return,
+ };
+
+ // If this is the last expression in a block or there is an else clause then the whole
+ // type needs to be considered, not just the inner type of the branch being matched on.
+ // Note the last expression in a block is dropped after all local bindings.
+ let check_ty = if has_else
+ || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+ {
+ op_ty
+ } else {
+ inner_ty
+ };
+
+ // All temporaries created in the scrutinee expression are dropped at the same time as the
+ // scrutinee would be, so they have to be considered as well.
+ // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
+ // for the duration if body.
+ let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
+
+ // check that `while_let_on_iterator` lint does not trigger
+ if_chain! {
+ if keyword == "while";
+ if let ExprKind::MethodCall(method_path, _, _, _) = let_expr.kind;
+ if method_path.ident.name == sym::next;
+ if is_trait_method(cx, let_expr, sym::Iterator);
+ then {
+ return;
+ }
+ }
+
+ let result_expr = match &let_expr.kind {
+ ExprKind::AddrOf(_, _, borrowed) => borrowed,
+ _ => let_expr,
+ };
+ span_lint_and_then(
+ cx,
+ REDUNDANT_PATTERN_MATCHING,
+ let_pat.span,
+ &format!("redundant pattern matching, consider using `{}`", good_method),
+ |diag| {
+ // if/while let ... = ... { ... }
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ let expr_span = expr.span;
+
+ // if/while let ... = ... { ... }
+ // ^^^
+ let op_span = result_expr.span.source_callsite();
+
+ // if/while let ... = ... { ... }
+ // ^^^^^^^^^^^^^^^^^^^
+ let span = expr_span.until(op_span.shrink_to_hi());
+
+ let mut app = if needs_drop {
+ Applicability::MaybeIncorrect
+ } else {
+ Applicability::MachineApplicable
+ };
+ let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+ diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
+
+ if needs_drop {
+ diag.note("this will change drop order of the result, as well as all temporaries");
+ diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
+ }
+ },
+ );
+ }
+
+ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
+ if arms.len() == 2 {
+ let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
+
+ let found_good_method = match node_pair {
+ (
+ PatKind::TupleStruct(ref path_left, patterns_left, _),
+ PatKind::TupleStruct(ref path_right, patterns_right, _),
+ ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
+ if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::RESULT_OK,
+ &paths::RESULT_ERR,
+ "is_ok()",
+ "is_err()",
+ )
+ .or_else(|| {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::IPADDR_V4,
+ &paths::IPADDR_V6,
+ "is_ipv4()",
+ "is_ipv6()",
+ )
+ })
+ } else {
+ None
+ }
+ },
+ (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
+ | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
+ if patterns.len() == 1 =>
+ {
+ if let PatKind::Wild = patterns[0].kind {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::OPTION_SOME,
+ &paths::OPTION_NONE,
+ "is_some()",
+ "is_none()",
+ )
+ .or_else(|| {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::POLL_READY,
+ &paths::POLL_PENDING,
+ "is_ready()",
+ "is_pending()",
+ )
+ })
+ } else {
+ None
+ }
+ },
+ _ => None,
+ };
+
+ if let Some(good_method) = found_good_method {
+ let span = expr.span.to(op.span);
+ let result_expr = match &op.kind {
+ ExprKind::AddrOf(_, _, borrowed) => borrowed,
+ _ => op,
+ };
+ span_lint_and_then(
+ cx,
+ REDUNDANT_PATTERN_MATCHING,
+ expr.span,
+ &format!("redundant pattern matching, consider using `{}`", good_method),
+ |diag| {
+ diag.span_suggestion(
+ span,
+ "try this",
+ format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
+ Applicability::MaybeIncorrect, // snippet
+ );
+ },
+ );
+ }
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ fn find_good_method_for_match<'a>(
+ cx: &LateContext<'_>,
+ arms: &[Arm<'_>],
+ path_left: &QPath<'_>,
+ path_right: &QPath<'_>,
+ expected_left: &[&str],
+ expected_right: &[&str],
+ should_be_left: &'a str,
+ should_be_right: &'a str,
+ ) -> Option<&'a str> {
+ let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
+ && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
+ {
+ (&(*arms[0].body).kind, &(*arms[1].body).kind)
+ } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
+ && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
+ {
+ (&(*arms[1].body).kind, &(*arms[0].body).kind)
+ } else {
+ return None;
+ };
+
+ match body_node_pair {
+ (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
+ (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
+ (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+}
+
+#[test]
+fn test_overlapping() {
+ use rustc_span::source_map::DUMMY_SP;
+
+ let sp = |s, e| SpannedRange {
+ span: DUMMY_SP,
+ node: (s, e),
+ };
+
+ assert_eq!(None, overlapping::<u8>(&[]));
+ assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
+ assert_eq!(
+ None,
+ overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
+ );
+ assert_eq!(
+ None,
+ overlapping(&[
+ sp(1, Bound::Included(4)),
+ sp(5, Bound::Included(6)),
+ sp(10, Bound::Included(11))
+ ],)
+ );
+ assert_eq!(
+ Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
+ overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
+ );
+ assert_eq!(
+ Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
+ overlapping(&[
+ sp(1, Bound::Included(4)),
+ sp(5, Bound::Included(6)),
+ sp(6, Bound::Included(11))
+ ],)
+ );
+}
+
+/// Implementation of `MATCH_SAME_ARMS`.
+fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
+ let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
+ let mut h = SpanlessHash::new(cx);
+ h.hash_expr(arm.body);
+ h.finish()
+ };
+
+ let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
+ let min_index = usize::min(lindex, rindex);
+ let max_index = usize::max(lindex, rindex);
+
+ let mut local_map: HirIdMap<HirId> = HirIdMap::default();
+ let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
+ if_chain! {
+ if let Some(a_id) = path_to_local(a);
+ if let Some(b_id) = path_to_local(b);
+ let entry = match local_map.entry(a_id) {
+ Entry::Vacant(entry) => entry,
+ // check if using the same bindings as before
+ Entry::Occupied(entry) => return *entry.get() == b_id,
+ };
+ // the names technically don't have to match; this makes the lint more conservative
+ if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
+ if TyS::same_type(cx.typeck_results().expr_ty(a), cx.typeck_results().expr_ty(b));
+ if pat_contains_local(lhs.pat, a_id);
+ if pat_contains_local(rhs.pat, b_id);
+ then {
+ entry.insert(b_id);
+ true
+ } else {
+ false
+ }
+ }
+ };
+ // Arms with a guard are ignored, those can’t always be merged together
+ // This is also the case for arms in-between each there is an arm with a guard
+ (min_index..=max_index).all(|index| arms[index].guard.is_none())
+ && SpanlessEq::new(cx)
+ .expr_fallback(eq_fallback)
+ .eq_expr(lhs.body, rhs.body)
+ // these checks could be removed to allow unused bindings
+ && bindings_eq(lhs.pat, local_map.keys().copied().collect())
+ && bindings_eq(rhs.pat, local_map.values().copied().collect())
+ };
+
+ let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
+ for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
+ span_lint_and_then(
+ cx,
+ MATCH_SAME_ARMS,
+ j.body.span,
+ "this `match` has identical arm bodies",
+ |diag| {
+ diag.span_note(i.body.span, "same as this");
+
+ // Note: this does not use `span_suggestion` on purpose:
+ // there is no clean way
+ // to remove the other arm. Building a span and suggest to replace it to ""
+ // makes an even more confusing error message. Also in order not to make up a
+ // span for the whole pattern, the suggestion is only shown when there is only
+ // one pattern. The user should know about `|` if they are already using it…
+
+ let lhs = snippet(cx, i.pat.span, "<pat1>");
+ let rhs = snippet(cx, j.pat.span, "<pat2>");
+
+ if let PatKind::Wild = j.pat.kind {
+ // if the last arm is _, then i could be integrated into _
+ // note that i.pat cannot be _, because that would mean that we're
+ // hiding all the subsequent arms, and rust won't compile
+ diag.span_note(
+ i.body.span,
+ &format!(
+ "`{}` has the same arm body as the `_` wildcard, consider removing it",
+ lhs
+ ),
+ );
+ } else {
+ diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
+ .help("...or consider changing the match arm bodies");
+ }
+ },
+ );
+ }
+ }
+}
+
+fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
+ let mut result = false;
+ pat.walk_short(|p| {
+ result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
+ !result
+ });
+ result
+}
+
+/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
+fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
+ let mut result = true;
+ pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
+ result && ids.is_empty()
+}
--- /dev/null
- /// If the `map` call is intentional, this should be rewritten. Or, if you intend to
- /// drive the iterator to completion, you can just use `for_each` instead.
+mod bind_instead_of_map;
+mod bytes_nth;
+mod chars_cmp;
+mod chars_cmp_with_unwrap;
+mod chars_last_cmp;
+mod chars_last_cmp_with_unwrap;
+mod chars_next_cmp;
+mod chars_next_cmp_with_unwrap;
+mod clone_on_copy;
+mod clone_on_ref_ptr;
+mod cloned_instead_of_copied;
+mod expect_fun_call;
+mod expect_used;
+mod extend_with_drain;
+mod filetype_is_file;
+mod filter_map;
+mod filter_map_identity;
+mod filter_map_next;
+mod filter_next;
+mod flat_map_identity;
+mod flat_map_option;
+mod from_iter_instead_of_collect;
+mod get_unwrap;
+mod implicit_clone;
+mod inefficient_to_string;
+mod inspect_for_each;
+mod into_iter_on_ref;
+mod iter_cloned_collect;
+mod iter_count;
+mod iter_next_slice;
+mod iter_nth;
+mod iter_nth_zero;
+mod iter_skip_next;
+mod iterator_step_by_zero;
+mod manual_saturating_arithmetic;
+mod manual_split_once;
+mod manual_str_repeat;
+mod map_collect_result_unit;
+mod map_flatten;
+mod map_identity;
+mod map_unwrap_or;
+mod ok_expect;
+mod option_as_ref_deref;
+mod option_map_or_none;
+mod option_map_unwrap_or;
+mod or_fun_call;
+mod search_is_some;
+mod single_char_add_str;
+mod single_char_insert_string;
+mod single_char_pattern;
+mod single_char_push_string;
+mod skip_while_next;
+mod string_extend_chars;
+mod suspicious_map;
+mod suspicious_splitn;
+mod uninit_assumed_init;
+mod unnecessary_filter_map;
+mod unnecessary_fold;
+mod unnecessary_lazy_eval;
+mod unwrap_or_else_default;
+mod unwrap_used;
+mod useless_asref;
+mod utils;
+mod wrong_self_convention;
+mod zst_offset;
+
+use bind_instead_of_map::BindInsteadOfMap;
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
+use if_chain::if_chain;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, TraitRef, Ty, TyS};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::SymbolStr;
+use rustc_span::{sym, Span};
+use rustc_typeck::hir_ty_to_ty;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
+ /// `copied()` could be used instead.
+ ///
+ /// ### Why is this bad?
+ /// `copied()` is better because it guarantees that the type being cloned
+ /// implements `Copy`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// [1, 2, 3].iter().cloned();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// [1, 2, 3].iter().copied();
+ /// ```
+ pub CLONED_INSTEAD_OF_COPIED,
+ pedantic,
+ "used `cloned` where `copied` could be used instead"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
+ /// used instead.
+ ///
+ /// ### Why is this bad?
+ /// When applicable, `filter_map()` is more clear since it shows that
+ /// `Option` is used to produce 0 or 1 items.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
+ /// ```
+ pub FLAT_MAP_OPTION,
+ pedantic,
+ "used `flat_map` where `filter_map` could be used instead"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
+ ///
+ /// ### Why is this bad?
+ /// It is better to handle the `None` or `Err` case,
+ /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
+ /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
+ /// `Allow` by default.
+ ///
+ /// `result.unwrap()` will let the thread panic on `Err` values.
+ /// Normally, you want to implement more sophisticated error handling,
+ /// and propagate errors upwards with `?` operator.
+ ///
+ /// Even if you want to panic on errors, not all `Error`s implement good
+ /// messages on display. Therefore, it may be beneficial to look at the places
+ /// where they may get displayed. Activate this lint to do just that.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// # let opt = Some(1);
+ ///
+ /// // Bad
+ /// opt.unwrap();
+ ///
+ /// // Good
+ /// opt.expect("more helpful message");
+ /// ```
+ ///
+ /// // or
+ ///
+ /// ```rust
+ /// # let res: Result<usize, ()> = Ok(1);
+ ///
+ /// // Bad
+ /// res.unwrap();
+ ///
+ /// // Good
+ /// res.expect("more helpful message");
+ /// ```
+ pub UNWRAP_USED,
+ restriction,
+ "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `.expect()` calls on `Option`s and `Result`s.
+ ///
+ /// ### Why is this bad?
+ /// Usually it is better to handle the `None` or `Err` case.
+ /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
+ /// this lint is `Allow` by default.
+ ///
+ /// `result.expect()` will let the thread panic on `Err`
+ /// values. Normally, you want to implement more sophisticated error handling,
+ /// and propagate errors upwards with `?` operator.
+ ///
+ /// ### Examples
+ /// ```rust,ignore
+ /// # let opt = Some(1);
+ ///
+ /// // Bad
+ /// opt.expect("one");
+ ///
+ /// // Good
+ /// let opt = Some(1);
+ /// opt?;
+ /// ```
+ ///
+ /// // or
+ ///
+ /// ```rust
+ /// # let res: Result<usize, ()> = Ok(1);
+ ///
+ /// // Bad
+ /// res.expect("one");
+ ///
+ /// // Good
+ /// res?;
+ /// # Ok::<(), ()>(())
+ /// ```
+ pub EXPECT_USED,
+ restriction,
+ "using `.expect()` on `Result` or `Option`, which might be better handled"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for methods that should live in a trait
+ /// implementation of a `std` trait (see [llogiq's blog
+ /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
+ /// information) instead of an inherent implementation.
+ ///
+ /// ### Why is this bad?
+ /// Implementing the traits improve ergonomics for users of
+ /// the code, often with very little cost. Also people seeing a `mul(...)`
+ /// method
+ /// may expect `*` to work equally, so you should have good reason to disappoint
+ /// them.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct X;
+ /// impl X {
+ /// fn add(&self, other: &X) -> X {
+ /// // ..
+ /// # X
+ /// }
+ /// }
+ /// ```
+ pub SHOULD_IMPLEMENT_TRAIT,
+ style,
+ "defining a method that should be implementing a std trait"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for methods with certain name prefixes and which
+ /// doesn't match how self is taken. The actual rules are:
+ ///
+ /// |Prefix |Postfix |`self` taken | `self` type |
+ /// |-------|------------|-----------------------|--------------|
+ /// |`as_` | none |`&self` or `&mut self` | any |
+ /// |`from_`| none | none | any |
+ /// |`into_`| none |`self` | any |
+ /// |`is_` | none |`&self` or none | any |
+ /// |`to_` | `_mut` |`&mut self` | any |
+ /// |`to_` | not `_mut` |`self` | `Copy` |
+ /// |`to_` | not `_mut` |`&self` | not `Copy` |
+ ///
+ /// Note: Clippy doesn't trigger methods with `to_` prefix in:
+ /// - Traits definition.
+ /// Clippy can not tell if a type that implements a trait is `Copy` or not.
+ /// - Traits implementation, when `&self` is taken.
+ /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
+ /// (see e.g. the `std::string::ToString` trait).
+ ///
+ /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
+ ///
+ /// Please find more info here:
+ /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
+ ///
+ /// ### Why is this bad?
+ /// Consistency breeds readability. If you follow the
+ /// conventions, your users won't be surprised that they, e.g., need to supply a
+ /// mutable reference to a `as_..` function.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # struct X;
+ /// impl X {
+ /// fn as_str(self) -> &'static str {
+ /// // ..
+ /// # ""
+ /// }
+ /// }
+ /// ```
+ pub WRONG_SELF_CONVENTION,
+ style,
+ "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `ok().expect(..)`.
+ ///
+ /// ### Why is this bad?
+ /// Because you usually call `expect()` on the `Result`
+ /// directly to get a better error message.
+ ///
+ /// ### Known problems
+ /// The error type needs to implement `Debug`
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = Ok::<_, ()>(());
+ ///
+ /// // Bad
+ /// x.ok().expect("why did I do this again?");
+ ///
+ /// // Good
+ /// x.expect("why did I do this again?");
+ /// ```
+ pub OK_EXPECT,
+ style,
+ "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
+ /// `Result` values.
+ ///
+ /// ### Why is this bad?
+ /// Readability, these can be written as `_.unwrap_or_default`, which is
+ /// simpler and more concise.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// # let x = Some(1);
+ ///
+ /// // Bad
+ /// x.unwrap_or_else(Default::default);
+ /// x.unwrap_or_else(u32::default);
+ ///
+ /// // Good
+ /// x.unwrap_or_default();
+ /// ```
+ pub UNWRAP_OR_ELSE_DEFAULT,
+ style,
+ "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
+ /// `result.map(_).unwrap_or_else(_)`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, these can be written more concisely (resp.) as
+ /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
+ ///
+ /// ### Known problems
+ /// The order of the arguments is not in execution order
+ ///
+ /// ### Examples
+ /// ```rust
+ /// # let x = Some(1);
+ ///
+ /// // Bad
+ /// x.map(|a| a + 1).unwrap_or(0);
+ ///
+ /// // Good
+ /// x.map_or(0, |a| a + 1);
+ /// ```
+ ///
+ /// // or
+ ///
+ /// ```rust
+ /// # let x: Result<usize, ()> = Ok(1);
+ /// # fn some_function(foo: ()) -> usize { 1 }
+ ///
+ /// // Bad
+ /// x.map(|a| a + 1).unwrap_or_else(some_function);
+ ///
+ /// // Good
+ /// x.map_or_else(some_function, |a| a + 1);
+ /// ```
+ pub MAP_UNWRAP_OR,
+ pedantic,
+ "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.map_or(None, _)`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.and_then(_)`.
+ ///
+ /// ### Known problems
+ /// The order of the arguments is not in execution order.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let opt = Some(1);
+ ///
+ /// // Bad
+ /// opt.map_or(None, |a| Some(a + 1));
+ ///
+ /// // Good
+ /// opt.and_then(|a| Some(a + 1));
+ /// ```
+ pub OPTION_MAP_OR_NONE,
+ style,
+ "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.map_or(None, Some)`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.ok()`.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust
+ /// # let r: Result<u32, &str> = Ok(1);
+ /// assert_eq!(Some(1), r.map_or(None, Some));
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// # let r: Result<u32, &str> = Ok(1);
+ /// assert_eq!(Some(1), r.ok());
+ /// ```
+ pub RESULT_MAP_OR_INTO_OPTION,
+ style,
+ "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
+ /// `_.or_else(|x| Err(y))`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.map(|x| y)` or `_.map_err(|x| y)`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn opt() -> Option<&'static str> { Some("42") }
+ /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
+ /// let _ = opt().and_then(|s| Some(s.len()));
+ /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
+ /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
+ /// ```
+ ///
+ /// The correct use would be:
+ ///
+ /// ```rust
+ /// # fn opt() -> Option<&'static str> { Some("42") }
+ /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
+ /// let _ = opt().map(|s| s.len());
+ /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
+ /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
+ /// ```
+ pub BIND_INSTEAD_OF_MAP,
+ complexity,
+ "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.filter(_).next()`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.find(_)`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().filter(|x| **x == 0).next();
+ /// ```
+ /// Could be written as
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().find(|x| **x == 0);
+ /// ```
+ pub FILTER_NEXT,
+ complexity,
+ "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.skip_while(condition).next()`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.find(!condition)`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().skip_while(|x| **x == 0).next();
+ /// ```
+ /// Could be written as
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().find(|x| **x != 0);
+ /// ```
+ pub SKIP_WHILE_NEXT,
+ complexity,
+ "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.flat_map(_)`
+ ///
+ /// ### Example
+ /// ```rust
+ /// let vec = vec![vec![1]];
+ ///
+ /// // Bad
+ /// vec.iter().map(|x| x.iter()).flatten();
+ ///
+ /// // Good
+ /// vec.iter().flat_map(|x| x.iter());
+ /// ```
+ pub MAP_FLATTEN,
+ pedantic,
+ "using combinations of `flatten` and `map` which can usually be written as a single method call"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
+ /// as `filter_map(_)`.
+ ///
+ /// ### Why is this bad?
+ /// Redundant code in the `filter` and `map` operations is poor style and
+ /// less performant.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust
+ /// (0_i32..10)
+ /// .filter(|n| n.checked_add(1).is_some())
+ /// .map(|n| n.checked_add(1).unwrap());
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// (0_i32..10).filter_map(|n| n.checked_add(1));
+ /// ```
+ pub MANUAL_FILTER_MAP,
+ complexity,
+ "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.find(_).map(_)` that can be written more simply
+ /// as `find_map(_)`.
+ ///
+ /// ### Why is this bad?
+ /// Redundant code in the `find` and `map` operations is poor style and
+ /// less performant.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust
+ /// (0_i32..10)
+ /// .find(|n| n.checked_add(1).is_some())
+ /// .map(|n| n.checked_add(1).unwrap());
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// (0_i32..10).find_map(|n| n.checked_add(1));
+ /// ```
+ pub MANUAL_FIND_MAP,
+ complexity,
+ "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.filter_map(_).next()`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.find_map(_)`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
+ /// ```
+ /// Can be written as
+ ///
+ /// ```rust
+ /// (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
+ /// ```
+ pub FILTER_MAP_NEXT,
+ pedantic,
+ "using combination of `filter_map` and `next` which can usually be written as a single method call"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `flat_map(|x| x)`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely by using `flatten`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let iter = vec![vec![0]].into_iter();
+ /// iter.flat_map(|x| x);
+ /// ```
+ /// Can be written as
+ /// ```rust
+ /// # let iter = vec![vec![0]].into_iter();
+ /// iter.flatten();
+ /// ```
+ pub FLAT_MAP_IDENTITY,
+ complexity,
+ "call to `flat_map` where `flatten` is sufficient"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for an iterator or string search (such as `find()`,
+ /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as:
+ /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
+ /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let vec = vec![1];
+ /// vec.iter().find(|x| **x == 0).is_some();
+ ///
+ /// let _ = "hello world".find("world").is_none();
+ /// ```
+ /// Could be written as
+ /// ```rust
+ /// let vec = vec![1];
+ /// vec.iter().any(|x| *x == 0);
+ ///
+ /// let _ = !"hello world".contains("world");
+ /// ```
+ pub SEARCH_IS_SOME,
+ complexity,
+ "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.chars().next()` on a `str` to check
+ /// if it starts with a given char.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.starts_with(_)`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let name = "foo";
+ /// if name.chars().next() == Some('_') {};
+ /// ```
+ /// Could be written as
+ /// ```rust
+ /// let name = "foo";
+ /// if name.starts_with('_') {};
+ /// ```
+ pub CHARS_NEXT_CMP,
+ style,
+ "using `.chars().next()` to check if a string starts with a char"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
+ /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
+ /// `unwrap_or_default` instead.
+ ///
+ /// ### Why is this bad?
+ /// The function will always be called and potentially
+ /// allocate an object acting as the default.
+ ///
+ /// ### Known problems
+ /// If the function has side-effects, not calling it will
+ /// change the semantic of the program, but you shouldn't rely on that anyway.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// foo.unwrap_or(String::new());
+ /// ```
+ /// this can instead be written:
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// foo.unwrap_or_else(String::new);
+ /// ```
+ /// or
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// foo.unwrap_or_default();
+ /// ```
+ pub OR_FUN_CALL,
+ perf,
+ "using any `*or` method with a function call, which suggests `*or_else`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
+ /// etc., and suggests to use `unwrap_or_else` instead
+ ///
+ /// ### Why is this bad?
+ /// The function will always be called.
+ ///
+ /// ### Known problems
+ /// If the function has side-effects, not calling it will
+ /// change the semantics of the program, but you shouldn't rely on that anyway.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// # let err_code = "418";
+ /// # let err_msg = "I'm a teapot";
+ /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
+ /// ```
+ /// or
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// # let err_code = "418";
+ /// # let err_msg = "I'm a teapot";
+ /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
+ /// ```
+ /// this can instead be written:
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// # let err_code = "418";
+ /// # let err_msg = "I'm a teapot";
+ /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
+ /// ```
+ pub EXPECT_FUN_CALL,
+ perf,
+ "using any `expect` method with a function call"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.clone()` on a `Copy` type.
+ ///
+ /// ### Why is this bad?
+ /// The only reason `Copy` types implement `Clone` is for
+ /// generics, not for using the `clone` method on a concrete type.
+ ///
+ /// ### Example
+ /// ```rust
+ /// 42u64.clone();
+ /// ```
+ pub CLONE_ON_COPY,
+ complexity,
+ "using `clone` on a `Copy` type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.clone()` on a ref-counted pointer,
+ /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
+ /// function syntax instead (e.g., `Rc::clone(foo)`).
+ ///
+ /// ### Why is this bad?
+ /// Calling '.clone()' on an Rc, Arc, or Weak
+ /// can obscure the fact that only the pointer is being cloned, not the underlying
+ /// data.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::rc::Rc;
+ /// let x = Rc::new(1);
+ ///
+ /// // Bad
+ /// x.clone();
+ ///
+ /// // Good
+ /// Rc::clone(&x);
+ /// ```
+ pub CLONE_ON_REF_PTR,
+ restriction,
+ "using 'clone' on a ref-counted pointer"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.clone()` on an `&&T`.
+ ///
+ /// ### Why is this bad?
+ /// Cloning an `&&T` copies the inner `&T`, instead of
+ /// cloning the underlying `T`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn main() {
+ /// let x = vec![1];
+ /// let y = &&x;
+ /// let z = y.clone();
+ /// println!("{:p} {:p}", *y, z); // prints out the same pointer
+ /// }
+ /// ```
+ pub CLONE_DOUBLE_REF,
+ correctness,
+ "using `clone` on `&&T`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.to_string()` on an `&&T` where
+ /// `T` implements `ToString` directly (like `&&str` or `&&String`).
+ ///
+ /// ### Why is this bad?
+ /// This bypasses the specialized implementation of
+ /// `ToString` and instead goes through the more expensive string formatting
+ /// facilities.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Generic implementation for `T: Display` is used (slow)
+ /// ["foo", "bar"].iter().map(|s| s.to_string());
+ ///
+ /// // OK, the specialized impl is used
+ /// ["foo", "bar"].iter().map(|&s| s.to_string());
+ /// ```
+ pub INEFFICIENT_TO_STRING,
+ pedantic,
+ "using `to_string` on `&&T` where `T: ToString`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `new` not returning a type that contains `Self`.
+ ///
+ /// ### Why is this bad?
+ /// As a convention, `new` methods are used to make a new
+ /// instance of a type.
+ ///
+ /// ### Example
+ /// In an impl block:
+ /// ```rust
+ /// # struct Foo;
+ /// # struct NotAFoo;
+ /// impl Foo {
+ /// fn new() -> NotAFoo {
+ /// # NotAFoo
+ /// }
+ /// }
+ /// ```
+ ///
+ /// ```rust
+ /// # struct Foo;
+ /// struct Bar(Foo);
+ /// impl Foo {
+ /// // Bad. The type name must contain `Self`
+ /// fn new() -> Bar {
+ /// # Bar(Foo)
+ /// }
+ /// }
+ /// ```
+ ///
+ /// ```rust
+ /// # struct Foo;
+ /// # struct FooError;
+ /// impl Foo {
+ /// // Good. Return type contains `Self`
+ /// fn new() -> Result<Foo, FooError> {
+ /// # Ok(Foo)
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Or in a trait definition:
+ /// ```rust
+ /// pub trait Trait {
+ /// // Bad. The type name must contain `Self`
+ /// fn new();
+ /// }
+ /// ```
+ ///
+ /// ```rust
+ /// pub trait Trait {
+ /// // Good. Return type contains `Self`
+ /// fn new() -> Self;
+ /// }
+ /// ```
+ pub NEW_RET_NO_SELF,
+ style,
+ "not returning type containing `Self` in a `new` method"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for string methods that receive a single-character
+ /// `str` as an argument, e.g., `_.split("x")`.
+ ///
+ /// ### Why is this bad?
+ /// Performing these methods using a `char` is faster than
+ /// using a `str`.
+ ///
+ /// ### Known problems
+ /// Does not catch multi-byte unicode characters.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// // Bad
+ /// _.split("x");
+ ///
+ /// // Good
+ /// _.split('x');
+ pub SINGLE_CHAR_PATTERN,
+ perf,
+ "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calling `.step_by(0)` on iterators which panics.
+ ///
+ /// ### Why is this bad?
+ /// This very much looks like an oversight. Use `panic!()` instead if you
+ /// actually intend to panic.
+ ///
+ /// ### Example
+ /// ```rust,should_panic
+ /// for x in (0..100).step_by(0) {
+ /// //..
+ /// }
+ /// ```
+ pub ITERATOR_STEP_BY_ZERO,
+ correctness,
+ "using `Iterator::step_by(0)`, which will panic at runtime"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for indirect collection of populated `Option`
+ ///
+ /// ### Why is this bad?
+ /// `Option` is like a collection of 0-1 things, so `flatten`
+ /// automatically does this without suspicious-looking `unwrap` calls.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let _ = std::iter::empty::<Option<i32>>().flatten();
+ /// ```
+ pub OPTION_FILTER_MAP,
+ complexity,
+ "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `iter.nth(0)`.
+ ///
+ /// ### Why is this bad?
+ /// `iter.next()` is equivalent to
+ /// `iter.nth(0)`, as they both consume the next element,
+ /// but is more readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::collections::HashSet;
+ /// // Bad
+ /// # let mut s = HashSet::new();
+ /// # s.insert(1);
+ /// let x = s.iter().nth(0);
+ ///
+ /// // Good
+ /// # let mut s = HashSet::new();
+ /// # s.insert(1);
+ /// let x = s.iter().next();
+ /// ```
+ pub ITER_NTH_ZERO,
+ style,
+ "replace `iter.nth(0)` with `iter.next()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `.iter().nth()` (and the related
+ /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
+ ///
+ /// ### Why is this bad?
+ /// `.get()` and `.get_mut()` are more efficient and more
+ /// readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let bad_vec = some_vec.iter().nth(3);
+ /// let bad_slice = &some_vec[..].iter().nth(3);
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let bad_vec = some_vec.get(3);
+ /// let bad_slice = &some_vec[..].get(3);
+ /// ```
+ pub ITER_NTH,
+ perf,
+ "using `.iter().nth()` on a standard library type with O(1) element access"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `.skip(x).next()` on iterators.
+ ///
+ /// ### Why is this bad?
+ /// `.nth(x)` is cleaner
+ ///
+ /// ### Example
+ /// ```rust
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let bad_vec = some_vec.iter().skip(3).next();
+ /// let bad_slice = &some_vec[..].iter().skip(3).next();
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let bad_vec = some_vec.iter().nth(3);
+ /// let bad_slice = &some_vec[..].iter().nth(3);
+ /// ```
+ pub ITER_SKIP_NEXT,
+ style,
+ "using `.skip(x).next()` on an iterator"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `.get().unwrap()` (or
+ /// `.get_mut().unwrap`) on a standard library type which implements `Index`
+ ///
+ /// ### Why is this bad?
+ /// Using the Index trait (`[]`) is more clear and more
+ /// concise.
+ ///
+ /// ### Known problems
+ /// Not a replacement for error handling: Using either
+ /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
+ /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
+ /// temporary placeholder for dealing with the `Option` type, then this does
+ /// not mitigate the need for error handling. If there is a chance that `.get()`
+ /// will be `None` in your program, then it is advisable that the `None` case
+ /// is handled in a future refactor instead of using `.unwrap()` or the Index
+ /// trait.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut some_vec = vec![0, 1, 2, 3];
+ /// let last = some_vec.get(3).unwrap();
+ /// *some_vec.get_mut(0).unwrap() = 1;
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// let mut some_vec = vec![0, 1, 2, 3];
+ /// let last = some_vec[3];
+ /// some_vec[0] = 1;
+ /// ```
+ pub GET_UNWRAP,
+ restriction,
+ "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for occurrences where one vector gets extended instead of append
+ ///
+ /// ### Why is this bad?
+ /// Using `append` instead of `extend` is more concise and faster
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut a = vec![1, 2, 3];
+ /// let mut b = vec![4, 5, 6];
+ ///
+ /// // Bad
+ /// a.extend(b.drain(..));
+ ///
+ /// // Good
+ /// a.append(&mut b);
+ /// ```
+ pub EXTEND_WITH_DRAIN,
+ perf,
+ "using vec.append(&mut vec) to move the full range of a vecor to another"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `.extend(s.chars())` where s is a
+ /// `&str` or `String`.
+ ///
+ /// ### Why is this bad?
+ /// `.push_str(s)` is clearer
+ ///
+ /// ### Example
+ /// ```rust
+ /// let abc = "abc";
+ /// let def = String::from("def");
+ /// let mut s = String::new();
+ /// s.extend(abc.chars());
+ /// s.extend(def.chars());
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// let abc = "abc";
+ /// let def = String::from("def");
+ /// let mut s = String::new();
+ /// s.push_str(abc);
+ /// s.push_str(&def);
+ /// ```
+ pub STRING_EXTEND_CHARS,
+ style,
+ "using `x.extend(s.chars())` where s is a `&str` or `String`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `.cloned().collect()` on slice to
+ /// create a `Vec`.
+ ///
+ /// ### Why is this bad?
+ /// `.to_vec()` is clearer
+ ///
+ /// ### Example
+ /// ```rust
+ /// let s = [1, 2, 3, 4, 5];
+ /// let s2: Vec<isize> = s[..].iter().cloned().collect();
+ /// ```
+ /// The better use would be:
+ /// ```rust
+ /// let s = [1, 2, 3, 4, 5];
+ /// let s2: Vec<isize> = s.to_vec();
+ /// ```
+ pub ITER_CLONED_COLLECT,
+ style,
+ "using `.cloned().collect()` on slice to create a `Vec`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.chars().last()` or
+ /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.ends_with(_)`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let name = "_";
+ ///
+ /// // Bad
+ /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
+ ///
+ /// // Good
+ /// name.ends_with('_') || name.ends_with('-');
+ /// ```
+ pub CHARS_LAST_CMP,
+ style,
+ "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.as_ref()` or `.as_mut()` where the
+ /// types before and after the call are the same.
+ ///
+ /// ### Why is this bad?
+ /// The call is unnecessary.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn do_stuff(x: &[i32]) {}
+ /// let x: &[i32] = &[1, 2, 3, 4, 5];
+ /// do_stuff(x.as_ref());
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// # fn do_stuff(x: &[i32]) {}
+ /// let x: &[i32] = &[1, 2, 3, 4, 5];
+ /// do_stuff(x);
+ /// ```
+ pub USELESS_ASREF,
+ complexity,
+ "using `as_ref` where the types before and after the call are the same"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for using `fold` when a more succinct alternative exists.
+ /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
+ /// `sum` or `product`.
+ ///
+ /// ### Why is this bad?
+ /// Readability.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+ /// ```
+ /// This could be written as:
+ /// ```rust
+ /// let _ = (0..3).any(|x| x > 2);
+ /// ```
+ pub UNNECESSARY_FOLD,
+ style,
+ "using `fold` when a more succinct alternative exists"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `filter_map` calls which could be replaced by `filter` or `map`.
+ /// More specifically it checks if the closure provided is only performing one of the
+ /// filter or map operations and suggests the appropriate option.
+ ///
+ /// ### Why is this bad?
+ /// Complexity. The intent is also clearer if only a single
+ /// operation is being performed.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
+ ///
+ /// // As there is no transformation of the argument this could be written as:
+ /// let _ = (0..3).filter(|&x| x > 2);
+ /// ```
+ ///
+ /// ```rust
+ /// let _ = (0..4).filter_map(|x| Some(x + 1));
+ ///
+ /// // As there is no conditional check on the argument this could be written as:
+ /// let _ = (0..4).map(|x| x + 1);
+ /// ```
+ pub UNNECESSARY_FILTER_MAP,
+ complexity,
+ "using `filter_map` when a more succinct alternative exists"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `into_iter` calls on references which should be replaced by `iter`
+ /// or `iter_mut`.
+ ///
+ /// ### Why is this bad?
+ /// Readability. Calling `into_iter` on a reference will not move out its
+ /// content into the resulting iterator, which is confusing. It is better just call `iter` or
+ /// `iter_mut` directly.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let _ = (&vec![3, 4, 5]).into_iter();
+ ///
+ /// // Good
+ /// let _ = (&vec![3, 4, 5]).iter();
+ /// ```
+ pub INTO_ITER_ON_REF,
+ style,
+ "using `.into_iter()` on a reference"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `map` followed by a `count`.
+ ///
+ /// ### Why is this bad?
+ /// It looks suspicious. Maybe `map` was confused with `filter`.
++ /// If the `map` call is intentional, this should be rewritten
++ /// using `inspect`. Or, if you intend to drive the iterator to
++ /// completion, you can just use `for_each` instead.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = (0..3).map(|x| x + 2).count();
+ /// ```
+ pub SUSPICIOUS_MAP,
+ suspicious,
+ "suspicious usage of map"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `MaybeUninit::uninit().assume_init()`.
+ ///
+ /// ### Why is this bad?
+ /// For most types, this is undefined behavior.
+ ///
+ /// ### Known problems
+ /// For now, we accept empty tuples and tuples / arrays
+ /// of `MaybeUninit`. There may be other types that allow uninitialized
+ /// data, but those are not yet rigorously defined.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Beware the UB
+ /// use std::mem::MaybeUninit;
+ ///
+ /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+ /// ```
+ ///
+ /// Note that the following is OK:
+ ///
+ /// ```rust
+ /// use std::mem::MaybeUninit;
+ ///
+ /// let _: [MaybeUninit<bool>; 5] = unsafe {
+ /// MaybeUninit::uninit().assume_init()
+ /// };
+ /// ```
+ pub UNINIT_ASSUMED_INIT,
+ correctness,
+ "`MaybeUninit::uninit().assume_init()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
+ ///
+ /// ### Why is this bad?
+ /// These can be written simply with `saturating_add/sub` methods.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let y: u32 = 0;
+ /// # let x: u32 = 100;
+ /// let add = x.checked_add(y).unwrap_or(u32::MAX);
+ /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
+ /// ```
+ ///
+ /// can be written using dedicated methods for saturating addition/subtraction as:
+ ///
+ /// ```rust
+ /// # let y: u32 = 0;
+ /// # let x: u32 = 100;
+ /// let add = x.saturating_add(y);
+ /// let sub = x.saturating_sub(y);
+ /// ```
+ pub MANUAL_SATURATING_ARITHMETIC,
+ style,
+ "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
+ /// zero-sized types
+ ///
+ /// ### Why is this bad?
+ /// This is a no-op, and likely unintended
+ ///
+ /// ### Example
+ /// ```rust
+ /// unsafe { (&() as *const ()).offset(1) };
+ /// ```
+ pub ZST_OFFSET,
+ correctness,
+ "Check for offset calculations on raw pointers to zero-sized types"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `FileType::is_file()`.
+ ///
+ /// ### Why is this bad?
+ /// When people testing a file type with `FileType::is_file`
+ /// they are testing whether a path is something they can get bytes from. But
+ /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
+ /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # || {
+ /// let metadata = std::fs::metadata("foo.txt")?;
+ /// let filetype = metadata.file_type();
+ ///
+ /// if filetype.is_file() {
+ /// // read file
+ /// }
+ /// # Ok::<_, std::io::Error>(())
+ /// # };
+ /// ```
+ ///
+ /// should be written as:
+ ///
+ /// ```rust
+ /// # || {
+ /// let metadata = std::fs::metadata("foo.txt")?;
+ /// let filetype = metadata.file_type();
+ ///
+ /// if !filetype.is_dir() {
+ /// // read file
+ /// }
+ /// # Ok::<_, std::io::Error>(())
+ /// # };
+ /// ```
+ pub FILETYPE_IS_FILE,
+ restriction,
+ "`FileType::is_file` is not recommended to test for readable file type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.as_deref()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let opt = Some("".to_string());
+ /// opt.as_ref().map(String::as_str)
+ /// # ;
+ /// ```
+ /// Can be written as
+ /// ```rust
+ /// # let opt = Some("".to_string());
+ /// opt.as_deref()
+ /// # ;
+ /// ```
+ pub OPTION_AS_REF_DEREF,
+ complexity,
+ "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `iter().next()` on a Slice or an Array
+ ///
+ /// ### Why is this bad?
+ /// These can be shortened into `.get()`
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let a = [1, 2, 3];
+ /// # let b = vec![1, 2, 3];
+ /// a[2..].iter().next();
+ /// b.iter().next();
+ /// ```
+ /// should be written as:
+ /// ```rust
+ /// # let a = [1, 2, 3];
+ /// # let b = vec![1, 2, 3];
+ /// a.get(2);
+ /// b.get(0);
+ /// ```
+ pub ITER_NEXT_SLICE,
+ style,
+ "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns when using `push_str`/`insert_str` with a single-character string literal
+ /// where `push`/`insert` with a `char` would work fine.
+ ///
+ /// ### Why is this bad?
+ /// It's less clear that we are pushing a single character.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut string = String::new();
+ /// string.insert_str(0, "R");
+ /// string.push_str("R");
+ /// ```
+ /// Could be written as
+ /// ```rust
+ /// let mut string = String::new();
+ /// string.insert(0, 'R');
+ /// string.push('R');
+ /// ```
+ pub SINGLE_CHAR_ADD_STR,
+ style,
+ "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
+ /// lazily evaluated closures on `Option` and `Result`.
+ ///
+ /// This lint suggests changing the following functions, when eager evaluation results in
+ /// simpler code:
+ /// - `unwrap_or_else` to `unwrap_or`
+ /// - `and_then` to `and`
+ /// - `or_else` to `or`
+ /// - `get_or_insert_with` to `get_or_insert`
+ /// - `ok_or_else` to `ok_or`
+ ///
+ /// ### Why is this bad?
+ /// Using eager evaluation is shorter and simpler in some cases.
+ ///
+ /// ### Known problems
+ /// It is possible, but not recommended for `Deref` and `Index` to have
+ /// side effects. Eagerly evaluating them can change the semantics of the program.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // example code where clippy issues a warning
+ /// let opt: Option<u32> = None;
+ ///
+ /// opt.unwrap_or_else(|| 42);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let opt: Option<u32> = None;
+ ///
+ /// opt.unwrap_or(42);
+ /// ```
+ pub UNNECESSARY_LAZY_EVALUATIONS,
+ style,
+ "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
+ ///
+ /// ### Why is this bad?
+ /// Using `try_for_each` instead is more readable and idiomatic.
+ ///
+ /// ### Example
+ /// ```rust
+ /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// (0..3).try_for_each(|t| Err(t));
+ /// ```
+ pub MAP_COLLECT_RESULT_UNIT,
+ style,
+ "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
+ /// trait.
+ ///
+ /// ### Why is this bad?
+ /// It is recommended style to use collect. See
+ /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::iter::FromIterator;
+ ///
+ /// let five_fives = std::iter::repeat(5).take(5);
+ ///
+ /// let v = Vec::from_iter(five_fives);
+ ///
+ /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let five_fives = std::iter::repeat(5).take(5);
+ ///
+ /// let v: Vec<i32> = five_fives.collect();
+ ///
+ /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
+ /// ```
+ pub FROM_ITER_INSTEAD_OF_COLLECT,
+ pedantic,
+ "use `.collect()` instead of `::from_iter()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `inspect().for_each()`.
+ ///
+ /// ### Why is this bad?
+ /// It is the same as performing the computation
+ /// inside `inspect` at the beginning of the closure in `for_each`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// [1,2,3,4,5].iter()
+ /// .inspect(|&x| println!("inspect the number: {}", x))
+ /// .for_each(|&x| {
+ /// assert!(x >= 0);
+ /// });
+ /// ```
+ /// Can be written as
+ /// ```rust
+ /// [1,2,3,4,5].iter()
+ /// .for_each(|&x| {
+ /// println!("inspect the number: {}", x);
+ /// assert!(x >= 0);
+ /// });
+ /// ```
+ pub INSPECT_FOR_EACH,
+ complexity,
+ "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `filter_map(|x| x)`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely by using `flatten`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let iter = vec![Some(1)].into_iter();
+ /// iter.filter_map(|x| x);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let iter = vec![Some(1)].into_iter();
+ /// iter.flatten();
+ /// ```
+ pub FILTER_MAP_IDENTITY,
+ complexity,
+ "call to `filter_map` where `flatten` is sufficient"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for instances of `map(f)` where `f` is the identity function.
+ ///
+ /// ### Why is this bad?
+ /// It can be written more concisely without the call to `map`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = [1, 2, 3];
+ /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x = [1, 2, 3];
+ /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
+ /// ```
+ pub MAP_IDENTITY,
+ complexity,
+ "using iterator.map(|x| x)"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `.bytes().nth()`.
+ ///
+ /// ### Why is this bad?
+ /// `.as_bytes().get()` is more efficient and more
+ /// readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let _ = "Hello".bytes().nth(3);
+ ///
+ /// // Good
+ /// let _ = "Hello".as_bytes().get(3);
+ /// ```
+ pub BYTES_NTH,
+ style,
+ "replace `.bytes().nth()` with `.as_bytes().get()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
+ ///
+ /// ### Why is this bad?
+ /// These methods do the same thing as `_.clone()` but may be confusing as
+ /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let a = vec![1, 2, 3];
+ /// let b = a.to_vec();
+ /// let c = a.to_owned();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let a = vec![1, 2, 3];
+ /// let b = a.clone();
+ /// let c = a.clone();
+ /// ```
+ pub IMPLICIT_CLONE,
+ pedantic,
+ "implicitly cloning a value by invoking a function on its dereferenced type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `.iter().count()`.
+ ///
+ /// ### Why is this bad?
+ /// `.len()` is more efficient and more
+ /// readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let _ = some_vec.iter().count();
+ /// let _ = &some_vec[..].iter().count();
+ ///
+ /// // Good
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let _ = some_vec.len();
+ /// let _ = &some_vec[..].len();
+ /// ```
+ pub ITER_COUNT,
+ complexity,
+ "replace `.iter().count()` with `.len()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to [`splitn`]
+ /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
+ /// related functions with either zero or one splits.
+ ///
+ /// ### Why is this bad?
+ /// These calls don't actually split the value and are
+ /// likely to be intended as a different number.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let s = "";
+ /// for x in s.splitn(1, ":") {
+ /// // use x
+ /// }
+ ///
+ /// // Good
+ /// let s = "";
+ /// for x in s.splitn(2, ":") {
+ /// // use x
+ /// }
+ /// ```
+ pub SUSPICIOUS_SPLITN,
+ correctness,
+ "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for manual implementations of `str::repeat`
+ ///
+ /// ### Why is this bad?
+ /// These are both harder to read, as well as less performant.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let x: String = std::iter::repeat('x').take(10).collect();
+ ///
+ /// // Good
+ /// let x: String = "x".repeat(10);
+ /// ```
+ pub MANUAL_STR_REPEAT,
+ perf,
+ "manual implementation of `str::repeat`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for usages of `str::splitn(2, _)`
+ ///
+ /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust,ignore
+ /// // Bad
+ /// let (key, value) = _.splitn(2, '=').next_tuple()?;
+ /// let value = _.splitn(2, '=').nth(1)?;
+ ///
+ /// // Good
+ /// let (key, value) = _.split_once('=')?;
+ /// let value = _.split_once('=')?.1;
+ /// ```
+ pub MANUAL_SPLIT_ONCE,
+ complexity,
+ "replace `.splitn(2, pat)` with `.split_once(pat)`"
+}
+
+pub struct Methods {
+ avoid_breaking_exported_api: bool,
+ msrv: Option<RustcVersion>,
+}
+
+impl Methods {
+ #[must_use]
+ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Self {
+ Self {
+ avoid_breaking_exported_api,
+ msrv,
+ }
+ }
+}
+
+impl_lint_pass!(Methods => [
+ UNWRAP_USED,
+ EXPECT_USED,
+ SHOULD_IMPLEMENT_TRAIT,
+ WRONG_SELF_CONVENTION,
+ OK_EXPECT,
+ UNWRAP_OR_ELSE_DEFAULT,
+ MAP_UNWRAP_OR,
+ RESULT_MAP_OR_INTO_OPTION,
+ OPTION_MAP_OR_NONE,
+ BIND_INSTEAD_OF_MAP,
+ OR_FUN_CALL,
+ EXPECT_FUN_CALL,
+ CHARS_NEXT_CMP,
+ CHARS_LAST_CMP,
+ CLONE_ON_COPY,
+ CLONE_ON_REF_PTR,
+ CLONE_DOUBLE_REF,
+ CLONED_INSTEAD_OF_COPIED,
+ FLAT_MAP_OPTION,
+ INEFFICIENT_TO_STRING,
+ NEW_RET_NO_SELF,
+ SINGLE_CHAR_PATTERN,
+ SINGLE_CHAR_ADD_STR,
+ SEARCH_IS_SOME,
+ FILTER_NEXT,
+ SKIP_WHILE_NEXT,
+ FILTER_MAP_IDENTITY,
+ MAP_IDENTITY,
+ MANUAL_FILTER_MAP,
+ MANUAL_FIND_MAP,
+ OPTION_FILTER_MAP,
+ FILTER_MAP_NEXT,
+ FLAT_MAP_IDENTITY,
+ MAP_FLATTEN,
+ ITERATOR_STEP_BY_ZERO,
+ ITER_NEXT_SLICE,
+ ITER_COUNT,
+ ITER_NTH,
+ ITER_NTH_ZERO,
+ BYTES_NTH,
+ ITER_SKIP_NEXT,
+ GET_UNWRAP,
+ STRING_EXTEND_CHARS,
+ ITER_CLONED_COLLECT,
+ USELESS_ASREF,
+ UNNECESSARY_FOLD,
+ UNNECESSARY_FILTER_MAP,
+ INTO_ITER_ON_REF,
+ SUSPICIOUS_MAP,
+ UNINIT_ASSUMED_INIT,
+ MANUAL_SATURATING_ARITHMETIC,
+ ZST_OFFSET,
+ FILETYPE_IS_FILE,
+ OPTION_AS_REF_DEREF,
+ UNNECESSARY_LAZY_EVALUATIONS,
+ MAP_COLLECT_RESULT_UNIT,
+ FROM_ITER_INSTEAD_OF_COLLECT,
+ INSPECT_FOR_EACH,
+ IMPLICIT_CLONE,
+ SUSPICIOUS_SPLITN,
+ MANUAL_STR_REPEAT,
+ EXTEND_WITH_DRAIN,
+ MANUAL_SPLIT_ONCE
+]);
+
+/// Extracts a method call name, args, and `Span` of the method name.
+fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(SymbolStr, &'tcx [hir::Expr<'tcx>], Span)> {
+ if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
+ if !args.iter().any(|e| e.span.from_expansion()) {
+ return Some((path.ident.name.as_str(), args, span));
+ }
+ }
+ None
+}
+
+/// Same as `method_call` but the `SymbolStr` is dereferenced into a temporary `&str`
+macro_rules! method_call {
+ ($expr:expr) => {
+ method_call($expr)
+ .as_ref()
+ .map(|&(ref name, args, span)| (&**name, args, span))
+ };
+}
+
+impl<'tcx> LateLintPass<'tcx> for Methods {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if in_macro(expr.span) {
+ return;
+ }
+
+ check_methods(cx, expr, self.msrv.as_ref());
+
+ match expr.kind {
+ hir::ExprKind::Call(func, args) => {
+ from_iter_instead_of_collect::check(cx, expr, args, func);
+ },
+ hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
+ or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
+ expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
+ clone_on_copy::check(cx, expr, method_call.ident.name, args);
+ clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
+ inefficient_to_string::check(cx, expr, method_call.ident.name, args);
+ single_char_add_str::check(cx, expr, args);
+ into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
+ single_char_pattern::check(cx, expr, method_call.ident.name, args);
+ },
+ hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
+ let mut info = BinaryExprInfo {
+ expr,
+ chain: lhs,
+ other: rhs,
+ eq: op.node == hir::BinOpKind::Eq,
+ };
+ lint_binary_expr_with_method_call(cx, &mut info);
+ },
+ _ => (),
+ }
+ }
+
+ #[allow(clippy::too_many_lines)]
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
+ if in_external_macro(cx.sess(), impl_item.span) {
+ return;
+ }
+ let name = impl_item.ident.name.as_str();
+ let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
+ let item = cx.tcx.hir().expect_item(parent);
+ let self_ty = cx.tcx.type_of(item.def_id);
+
+ let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
+ if_chain! {
+ if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
+ if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
+
+ let method_sig = cx.tcx.fn_sig(impl_item.def_id);
+ let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
+
+ let first_arg_ty = &method_sig.inputs().iter().next();
+
+ // check conventions w.r.t. conversion method names and predicates
+ if let Some(first_arg_ty) = first_arg_ty;
+
+ then {
+ // if this impl block implements a trait, lint in trait definition instead
+ if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
+ // check missing trait implementations
+ for method_config in &TRAIT_METHODS {
+ if name == method_config.method_name &&
+ sig.decl.inputs.len() == method_config.param_count &&
+ method_config.output_type.matches(&sig.decl.output) &&
+ method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
+ fn_header_equals(method_config.fn_header, sig.header) &&
+ method_config.lifetime_param_cond(impl_item)
+ {
+ span_lint_and_help(
+ cx,
+ SHOULD_IMPLEMENT_TRAIT,
+ impl_item.span,
+ &format!(
+ "method `{}` can be confused for the standard trait method `{}::{}`",
+ method_config.method_name,
+ method_config.trait_name,
+ method_config.method_name
+ ),
+ None,
+ &format!(
+ "consider implementing the trait `{}` or choosing a less ambiguous method name",
+ method_config.trait_name
+ )
+ );
+ }
+ }
+ }
+
+ if sig.decl.implicit_self.has_implicit_self()
+ && !(self.avoid_breaking_exported_api
+ && cx.access_levels.is_exported(impl_item.def_id))
+ {
+ wrong_self_convention::check(
+ cx,
+ &name,
+ self_ty,
+ first_arg_ty,
+ first_arg.pat.span,
+ implements_trait,
+ false
+ );
+ }
+ }
+ }
+
+ // if this impl block implements a trait, lint in trait definition instead
+ if implements_trait {
+ return;
+ }
+
+ if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
+ let ret_ty = return_ty(cx, impl_item.hir_id());
+
+ // walk the return type and check for Self (this does not check associated types)
+ if let Some(self_adt) = self_ty.ty_adt_def() {
+ if contains_adt_constructor(cx.tcx, ret_ty, self_adt) {
+ return;
+ }
+ } else if contains_ty(cx.tcx, ret_ty, self_ty) {
+ return;
+ }
+
+ // if return type is impl trait, check the associated types
+ if let ty::Opaque(def_id, _) = *ret_ty.kind() {
+ // one of the associated types must be Self
+ for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
+ if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
+ // walk the associated type and check for Self
+ if let Some(self_adt) = self_ty.ty_adt_def() {
+ if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) {
+ return;
+ }
+ } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) {
+ return;
+ }
+ }
+ }
+ }
+
+ if name == "new" && !TyS::same_type(ret_ty, self_ty) {
+ span_lint(
+ cx,
+ NEW_RET_NO_SELF,
+ impl_item.span,
+ "methods called `new` usually return `Self`",
+ );
+ }
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if in_external_macro(cx.tcx.sess, item.span) {
+ return;
+ }
+
+ if_chain! {
+ if let TraitItemKind::Fn(ref sig, _) = item.kind;
+ if sig.decl.implicit_self.has_implicit_self();
+ if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
+
+ then {
+ let first_arg_span = first_arg_ty.span;
+ let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
+ let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
+ wrong_self_convention::check(
+ cx,
+ &item.ident.name.as_str(),
+ self_ty,
+ first_arg_ty,
+ first_arg_span,
+ false,
+ true
+ );
+ }
+ }
+
+ if_chain! {
+ if item.ident.name == sym::new;
+ if let TraitItemKind::Fn(_, _) = item.kind;
+ let ret_ty = return_ty(cx, item.hir_id());
+ let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
+ if !contains_ty(cx.tcx, ret_ty, self_ty);
+
+ then {
+ span_lint(
+ cx,
+ NEW_RET_NO_SELF,
+ item.span,
+ "methods called `new` usually return `Self`",
+ );
+ }
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
+ if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
+ match (name, args) {
+ ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
+ zst_offset::check(cx, expr, recv);
+ },
+ ("and_then", [arg]) => {
+ let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
+ let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
+ if !biom_option_linted && !biom_result_linted {
+ unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
+ }
+ },
+ ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
+ ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
+ ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
+ ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
+ ("collect", []) => match method_call!(recv) {
+ Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
+ Some(("map", [m_recv, m_arg], _)) => {
+ map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
+ },
+ Some(("take", [take_self_arg, take_arg], _)) => {
+ if meets_msrv(msrv, &msrvs::STR_REPEAT) {
+ manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
+ }
+ },
+ _ => {},
+ },
+ ("count", []) => match method_call!(recv) {
+ Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
+ iter_count::check(cx, expr, recv2, name);
+ },
+ Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
+ _ => {},
+ },
+ ("expect", [_]) => match method_call!(recv) {
+ Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
+ _ => expect_used::check(cx, expr, recv),
+ },
+ ("extend", [arg]) => {
+ string_extend_chars::check(cx, expr, recv, arg);
+ extend_with_drain::check(cx, expr, recv, arg);
+ },
+ ("filter_map", [arg]) => {
+ unnecessary_filter_map::check(cx, expr, arg);
+ filter_map_identity::check(cx, expr, arg, span);
+ },
+ ("flat_map", [arg]) => {
+ flat_map_identity::check(cx, expr, arg, span);
+ flat_map_option::check(cx, expr, arg, span);
+ },
+ ("flatten", []) => {
+ if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
+ map_flatten::check(cx, expr, recv, map_arg);
+ }
+ },
+ ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
+ ("for_each", [_]) => {
+ if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
+ inspect_for_each::check(cx, expr, span2);
+ }
+ },
+ ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
+ ("is_file", []) => filetype_is_file::check(cx, expr, recv),
+ ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
+ ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+ ("map", [m_arg]) => {
+ if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
+ match (name, args) {
+ ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
+ ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
+ ("filter", [f_arg]) => {
+ filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
+ },
+ ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
+ _ => {},
+ }
+ }
+ map_identity::check(cx, expr, recv, m_arg, span);
+ },
+ ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
+ ("next", []) => {
+ if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
+ match (name, args) {
+ ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
+ ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
+ ("iter", []) => iter_next_slice::check(cx, expr, recv),
+ ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
+ ("skip_while", [_]) => skip_while_next::check(cx, expr),
+ _ => {},
+ }
+ }
+ },
+ ("nth", [n_arg]) => match method_call!(recv) {
+ Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
+ Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
+ Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
+ _ => iter_nth_zero::check(cx, expr, recv, n_arg),
+ },
+ ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
+ ("or_else", [arg]) => {
+ if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
+ unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
+ }
+ },
+ ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
+ if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
+ suspicious_splitn::check(cx, name, expr, recv, count);
+ if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
+ manual_split_once::check(cx, name, expr, recv, pat_arg);
+ }
+ }
+ },
+ ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
+ if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
+ suspicious_splitn::check(cx, name, expr, recv, count);
+ }
+ },
+ ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
+ ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+ implicit_clone::check(cx, name, expr, recv, span);
+ },
+ ("unwrap", []) => match method_call!(recv) {
+ Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
+ Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
+ _ => unwrap_used::check(cx, expr, recv),
+ },
+ ("unwrap_or", [u_arg]) => match method_call!(recv) {
+ Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
+ manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
+ },
+ Some(("map", [m_recv, m_arg], span)) => {
+ option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
+ },
+ _ => {},
+ },
+ ("unwrap_or_else", [u_arg]) => match method_call!(recv) {
+ Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
+ _ => {
+ unwrap_or_else_default::check(cx, expr, recv, u_arg);
+ unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
+ },
+ },
+ _ => {},
+ }
+ }
+}
+
+fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
+ if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
+ search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
+ }
+}
+
+/// Used for `lint_binary_expr_with_method_call`.
+#[derive(Copy, Clone)]
+struct BinaryExprInfo<'a> {
+ expr: &'a hir::Expr<'a>,
+ chain: &'a hir::Expr<'a>,
+ other: &'a hir::Expr<'a>,
+ eq: bool,
+}
+
+/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
+fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
+ macro_rules! lint_with_both_lhs_and_rhs {
+ ($func:expr, $cx:expr, $info:ident) => {
+ if !$func($cx, $info) {
+ ::std::mem::swap(&mut $info.chain, &mut $info.other);
+ if $func($cx, $info) {
+ return;
+ }
+ }
+ };
+ }
+
+ lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
+}
+
+const FN_HEADER: hir::FnHeader = hir::FnHeader {
+ unsafety: hir::Unsafety::Normal,
+ constness: hir::Constness::NotConst,
+ asyncness: hir::IsAsync::NotAsync,
+ abi: rustc_target::spec::abi::Abi::Rust,
+};
+
+struct ShouldImplTraitCase {
+ trait_name: &'static str,
+ method_name: &'static str,
+ param_count: usize,
+ fn_header: hir::FnHeader,
+ // implicit self kind expected (none, self, &self, ...)
+ self_kind: SelfKind,
+ // checks against the output type
+ output_type: OutType,
+ // certain methods with explicit lifetimes can't implement the equivalent trait method
+ lint_explicit_lifetime: bool,
+}
+impl ShouldImplTraitCase {
+ const fn new(
+ trait_name: &'static str,
+ method_name: &'static str,
+ param_count: usize,
+ fn_header: hir::FnHeader,
+ self_kind: SelfKind,
+ output_type: OutType,
+ lint_explicit_lifetime: bool,
+ ) -> ShouldImplTraitCase {
+ ShouldImplTraitCase {
+ trait_name,
+ method_name,
+ param_count,
+ fn_header,
+ self_kind,
+ output_type,
+ lint_explicit_lifetime,
+ }
+ }
+
+ fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
+ self.lint_explicit_lifetime
+ || !impl_item.generics.params.iter().any(|p| {
+ matches!(
+ p.kind,
+ hir::GenericParamKind::Lifetime {
+ kind: hir::LifetimeParamKind::Explicit
+ }
+ )
+ })
+ }
+}
+
+#[rustfmt::skip]
+const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
+ ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true),
+ ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true),
+ // FIXME: default doesn't work
+ ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true),
+ ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true),
+ ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true),
+ ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true),
+ ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true),
+ ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false),
+ ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+];
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+enum SelfKind {
+ Value,
+ Ref,
+ RefMut,
+ No,
+}
+
+impl SelfKind {
+ fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+ fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool {
+ if ty == parent_ty {
+ true
+ } else if ty.is_box() {
+ ty.boxed_ty() == parent_ty
+ } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
+ if let ty::Adt(_, substs) = ty.kind() {
+ substs.types().next().map_or(false, |t| t == parent_ty)
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
+
+ fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+ if let ty::Ref(_, t, m) = *ty.kind() {
+ return m == mutability && t == parent_ty;
+ }
+
+ let trait_path = match mutability {
+ hir::Mutability::Not => &paths::ASREF_TRAIT,
+ hir::Mutability::Mut => &paths::ASMUT_TRAIT,
+ };
+
+ let trait_def_id = match get_trait_def_id(cx, trait_path) {
+ Some(did) => did,
+ None => return false,
+ };
+ implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
+ }
+
+ match self {
+ Self::Value => matches_value(cx, parent_ty, ty),
+ Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
+ Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
+ Self::No => ty != parent_ty,
+ }
+ }
+
+ #[must_use]
+ fn description(self) -> &'static str {
+ match self {
+ Self::Value => "`self` by value",
+ Self::Ref => "`self` by reference",
+ Self::RefMut => "`self` by mutable reference",
+ Self::No => "no `self`",
+ }
+ }
+}
+
+#[derive(Clone, Copy)]
+enum OutType {
+ Unit,
+ Bool,
+ Any,
+ Ref,
+}
+
+impl OutType {
+ fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
+ let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
+ match (self, ty) {
+ (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
+ (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
+ (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
+ (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
+ (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
+ _ => false,
+ }
+ }
+}
+
+fn is_bool(ty: &hir::Ty<'_>) -> bool {
+ if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+ matches!(path.res, Res::PrimTy(PrimTy::Bool))
+ } else {
+ false
+ }
+}
+
+fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
+ expected.constness == actual.constness
+ && expected.unsafety == actual.unsafety
+ && expected.asyncness == actual.asyncness
+}
--- /dev/null
- hir::ExprKind::Block(block, _) => {
- if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
- if let Some(block_expr) = block.expr {
- if let hir::ExprKind::MethodCall(..) = block_expr.kind {
- check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
- }
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eager_or_lazy::is_lazyness_candidate;
+use clippy_utils::is_trait_item;
+use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
+use clippy_utils::ty::implements_trait;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{contains_return, last_path_segment, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::{BlockCheckMode, UnsafeSource};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::source_map::Span;
+use rustc_span::symbol::{kw, sym};
+use std::borrow::Cow;
+
+use super::OR_FUN_CALL;
+
+/// Checks for the `OR_FUN_CALL` lint.
+#[allow(clippy::too_many_lines)]
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &hir::Expr<'_>,
+ method_span: Span,
+ name: &str,
+ args: &'tcx [hir::Expr<'_>],
+) {
+ /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
+ fn check_unwrap_or_default(
+ cx: &LateContext<'_>,
+ name: &str,
+ fun: &hir::Expr<'_>,
+ self_expr: &hir::Expr<'_>,
+ arg: &hir::Expr<'_>,
+ or_has_args: bool,
+ span: Span,
+ ) -> bool {
+ let is_default_default = || is_trait_item(cx, fun, sym::Default);
+
+ let implements_default = |arg, default_trait_id| {
+ let arg_ty = cx.typeck_results().expr_ty(arg);
+ implements_trait(cx, arg_ty, default_trait_id, &[])
+ };
+
+ if_chain! {
+ if !or_has_args;
+ if name == "unwrap_or";
+ if let hir::ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
+ let path = last_path_segment(qpath).ident.name;
+ // needs to target Default::default in particular or be *::new and have a Default impl
+ // available
+ if (matches!(path, kw::Default) && is_default_default())
+ || (matches!(path, sym::new) && implements_default(arg, default_trait_id));
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ OR_FUN_CALL,
+ span,
+ &format!("use of `{}` followed by a call to `{}`", name, path),
+ "try this",
+ format!(
+ "{}.unwrap_or_default()",
+ snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
+ ),
+ applicability,
+ );
+
+ true
+ } else {
+ false
+ }
+ }
+ }
+
+ /// Checks for `*or(foo())`.
+ #[allow(clippy::too_many_arguments)]
+ fn check_general_case<'tcx>(
+ cx: &LateContext<'tcx>,
+ name: &str,
+ method_span: Span,
+ self_expr: &hir::Expr<'_>,
+ arg: &'tcx hir::Expr<'_>,
+ span: Span,
+ // None if lambda is required
+ fun_span: Option<Span>,
+ ) {
+ // (path, fn_has_argument, methods, suffix)
+ static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
+ (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
+ (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
+ (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
+ (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
+ ];
+
+ if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind {
+ if path.ident.name == sym::len {
+ let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+
+ match ty.kind() {
+ ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
+ _ => (),
+ }
+
+ if is_type_diagnostic_item(cx, ty, sym::Vec) {
+ return;
+ }
+ }
+ }
+
+ if_chain! {
+ if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
+
+ if is_lazyness_candidate(cx, arg);
+ if !contains_return(arg);
+
+ let self_ty = cx.typeck_results().expr_ty(self_expr);
+
+ if let Some(&(_, fn_has_arguments, poss, suffix)) =
+ KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
+
+ if poss.contains(&name);
+
+ then {
+ let macro_expanded_snipped;
+ let sugg: Cow<'_, str> = {
+ let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
+ (false, Some(fun_span)) => (fun_span, false),
+ _ => (arg.span, true),
+ };
+ let snippet = {
+ let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
+ if not_macro_argument_snippet == "vec![]" {
+ macro_expanded_snipped = snippet(cx, snippet_span, "..");
+ match macro_expanded_snipped.strip_prefix("$crate::vec::") {
+ Some(stripped) => Cow::from(stripped),
+ None => macro_expanded_snipped
+ }
+ }
+ else {
+ not_macro_argument_snippet
+ }
+ };
+
+ if use_lambda {
+ let l_arg = if fn_has_arguments { "_" } else { "" };
+ format!("|{}| {}", l_arg, snippet).into()
+ } else {
+ snippet
+ }
+ };
+ let span_replace_word = method_span.with_hi(span.hi());
+ span_lint_and_sugg(
+ cx,
+ OR_FUN_CALL,
+ span_replace_word,
+ &format!("use of `{}` followed by a function call", name),
+ "try this",
+ format!("{}_{}({})", name, suffix, sugg),
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ }
+
+ if args.len() == 2 {
+ match args[1].kind {
+ hir::ExprKind::Call(fun, or_args) => {
+ let or_has_args = !or_args.is_empty();
+ if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
+ let fun_span = if or_has_args { None } else { Some(fun.span) };
+ check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
+ }
+ },
+ hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
+ check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
+ },
- },
++ hir::ExprKind::Block(block, _)
++ if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) =>
++ {
++ if let Some(block_expr) = block.expr {
++ if let hir::ExprKind::MethodCall(..) = block_expr.kind {
++ check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
+ }
+ }
++ }
+ _ => (),
+ }
+ }
+}
--- /dev/null
- "make sure you did not confuse `map` with `filter` or `for_each`",
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{expr_or_init, is_trait_method};
+use if_chain::if_chain;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::SUSPICIOUS_MAP;
+
+pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
+ if_chain! {
+ if is_trait_method(cx, count_recv, sym::Iterator);
+ let closure = expr_or_init(cx, map_arg);
+ if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id);
+ let closure_body = cx.tcx.hir().body(body_id);
+ if !cx.typeck_results().expr_ty(&closure_body.value).is_unit();
+ then {
+ if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
+ // A variable is used mutably inside of the closure. Suppress the lint.
+ if !map_mutated_vars.is_empty() {
+ return;
+ }
+ }
+ span_lint_and_help(
+ cx,
+ SUSPICIOUS_MAP,
+ expr.span,
+ "this call to `map()` won't have an effect on the call to `count()`",
+ None,
++ "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`",
+ );
+ }
+ }
+}
--- /dev/null
- if cx.tcx.is_diagnostic_item(sym::Option, adt.did)
- && TyS::same_type(in_ty, subst.type_at(0)) =>
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
+use rustc_hir as hir;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty::{self, TyS};
+use rustc_span::sym;
+
+use super::UNNECESSARY_FILTER_MAP;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
+ if !is_trait_method(cx, expr, sym::Iterator) {
+ return;
+ }
+
+ if let hir::ExprKind::Closure(_, _, body_id, ..) = arg.kind {
+ let body = cx.tcx.hir().body(body_id);
+ let arg_id = body.params[0].pat.hir_id;
+ let mutates_arg =
+ mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
+
+ let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
+
+ let mut return_visitor = ReturnVisitor::new(cx, arg_id);
+ return_visitor.visit_expr(&body.value);
+ found_mapping |= return_visitor.found_mapping;
+ found_filtering |= return_visitor.found_filtering;
+
+ let sugg = if !found_filtering {
+ "map"
+ } else if !found_mapping && !mutates_arg {
+ let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
+ match cx.typeck_results().expr_ty(&body.value).kind() {
+ ty::Adt(adt, subst)
++ if cx.tcx.is_diagnostic_item(sym::Option, adt.did) && TyS::same_type(in_ty, subst.type_at(0)) =>
+ {
+ "filter"
+ },
+ _ => return,
+ }
+ } else {
+ return;
+ };
+ span_lint(
+ cx,
+ UNNECESSARY_FILTER_MAP,
+ expr.span,
+ &format!("this `.filter_map` can be written more simply using `.{}`", sugg),
+ );
+ }
+}
+
+// returns (found_mapping, found_filtering)
+fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
+ match &expr.kind {
+ hir::ExprKind::Call(func, args) => {
+ if let hir::ExprKind::Path(ref path) = func.kind {
+ if is_lang_ctor(cx, path, OptionSome) {
+ if path_to_local_id(&args[0], arg_id) {
+ return (false, false);
+ }
+ return (true, false);
+ }
+ }
+ (true, true)
+ },
+ hir::ExprKind::Block(block, _) => block
+ .expr
+ .as_ref()
+ .map_or((false, false), |expr| check_expression(cx, arg_id, expr)),
+ hir::ExprKind::Match(_, arms, _) => {
+ let mut found_mapping = false;
+ let mut found_filtering = false;
+ for arm in *arms {
+ let (m, f) = check_expression(cx, arg_id, arm.body);
+ found_mapping |= m;
+ found_filtering |= f;
+ }
+ (found_mapping, found_filtering)
+ },
+ // There must be an else_arm or there will be a type error
+ hir::ExprKind::If(_, if_arm, Some(else_arm)) => {
+ let if_check = check_expression(cx, arg_id, if_arm);
+ let else_check = check_expression(cx, arg_id, else_arm);
+ (if_check.0 | else_check.0, if_check.1 | else_check.1)
+ },
+ hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
+ _ => (true, true),
+ }
+}
+
+struct ReturnVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ arg_id: hir::HirId,
+ // Found a non-None return that isn't Some(input)
+ found_mapping: bool,
+ // Found a return that isn't Some
+ found_filtering: bool,
+}
+
+impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> {
+ fn new(cx: &'a LateContext<'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> {
+ ReturnVisitor {
+ cx,
+ arg_id,
+ found_mapping: false,
+ found_filtering: false,
+ }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
+ if let hir::ExprKind::Ret(Some(expr)) = &expr.kind {
+ let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
+ self.found_mapping |= found_mapping;
+ self.found_filtering |= found_filtering;
+ } else {
+ walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
--- /dev/null
- if let BinOpKind::Rem = op.node {
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sext;
+use if_chain::if_chain;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use std::fmt::Display;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for modulo arithmetic.
+ ///
+ /// ### Why is this bad?
+ /// The results of modulo (%) operation might differ
+ /// depending on the language, when negative numbers are involved.
+ /// If you interop with different languages it might be beneficial
+ /// to double check all places that use modulo arithmetic.
+ ///
+ /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = -17 % 3;
+ /// ```
+ pub MODULO_ARITHMETIC,
+ restriction,
+ "any modulo arithmetic statement"
+}
+
+declare_lint_pass!(ModuloArithmetic => [MODULO_ARITHMETIC]);
+
+struct OperandInfo {
+ string_representation: Option<String>,
+ is_negative: bool,
+ is_integral: bool,
+}
+
+fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> {
+ match constant(cx, cx.typeck_results(), operand) {
+ Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() {
+ ty::Int(ity) => {
+ let value = sext(cx.tcx, v, ity);
+ return Some(OperandInfo {
+ string_representation: Some(value.to_string()),
+ is_negative: value < 0,
+ is_integral: true,
+ });
+ },
+ ty::Uint(_) => {
+ return Some(OperandInfo {
+ string_representation: None,
+ is_negative: false,
+ is_integral: true,
+ });
+ },
+ _ => {},
+ },
+ Some((Constant::F32(f), _)) => {
+ return Some(floating_point_operand_info(&f));
+ },
+ Some((Constant::F64(f), _)) => {
+ return Some(floating_point_operand_info(&f));
+ },
+ _ => {},
+ }
+ None
+}
+
+fn floating_point_operand_info<T: Display + PartialOrd + From<f32>>(f: &T) -> OperandInfo {
+ OperandInfo {
+ string_representation: Some(format!("{:.3}", *f)),
+ is_negative: *f < 0.0.into(),
+ is_integral: false,
+ }
+}
+
+fn might_have_negative_value(t: &ty::TyS<'_>) -> bool {
+ t.is_signed() || t.is_floating_point()
+}
+
+fn check_const_operands<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ lhs_operand: &OperandInfo,
+ rhs_operand: &OperandInfo,
+) {
+ if lhs_operand.is_negative ^ rhs_operand.is_negative {
+ span_lint_and_then(
+ cx,
+ MODULO_ARITHMETIC,
+ expr.span,
+ &format!(
+ "you are using modulo operator on constants with different signs: `{} % {}`",
+ lhs_operand.string_representation.as_ref().unwrap(),
+ rhs_operand.string_representation.as_ref().unwrap()
+ ),
+ |diag| {
+ diag.note("double check for expected result especially when interoperating with different languages");
+ if lhs_operand.is_integral {
+ diag.note("or consider using `rem_euclid` or similar function");
+ }
+ },
+ );
+ }
+}
+
+fn check_non_const_operands<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, operand: &Expr<'_>) {
+ let operand_type = cx.typeck_results().expr_ty(operand);
+ if might_have_negative_value(operand_type) {
+ span_lint_and_then(
+ cx,
+ MODULO_ARITHMETIC,
+ expr.span,
+ "you are using modulo operator on types that might have different signs",
+ |diag| {
+ diag.note("double check for expected result especially when interoperating with different languages");
+ if operand_type.is_integral() {
+ diag.note("or consider using `rem_euclid` or similar function");
+ }
+ },
+ );
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for ModuloArithmetic {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ match &expr.kind {
+ ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => {
++ if op.node == BinOpKind::Rem {
+ let lhs_operand = analyze_operand(lhs, cx, expr);
+ let rhs_operand = analyze_operand(rhs, cx, expr);
+ if_chain! {
+ if let Some(lhs_operand) = lhs_operand;
+ if let Some(rhs_operand) = rhs_operand;
+ then {
+ check_const_operands(cx, expr, &lhs_operand, &rhs_operand);
+ }
+ else {
+ check_non_const_operands(cx, expr, lhs);
+ }
+ }
+ };
+ },
+ _ => {},
+ }
+ }
+}
--- /dev/null
- if let BinOpKind::Eq = op.node {
+//! Checks for needless boolean results of if-else expressions
+//!
+//! This lint is **warn** by default
+
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::higher;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{is_else_clause, is_expn_of};
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for expressions of the form `if c { true } else {
+ /// false }` (or vice versa) and suggests using the condition directly.
+ ///
+ /// ### Why is this bad?
+ /// Redundant code.
+ ///
+ /// ### Known problems
+ /// Maybe false positives: Sometimes, the two branches are
+ /// painstakingly documented (which we, of course, do not detect), so they *may*
+ /// have some value. Even then, the documentation can be rewritten to match the
+ /// shorter code.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// if x {
+ /// false
+ /// } else {
+ /// true
+ /// }
+ /// ```
+ /// Could be written as
+ /// ```rust,ignore
+ /// !x
+ /// ```
+ pub NEEDLESS_BOOL,
+ complexity,
+ "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for expressions of the form `x == true`,
+ /// `x != true` and order comparisons such as `x < true` (or vice versa) and
+ /// suggest using the variable directly.
+ ///
+ /// ### Why is this bad?
+ /// Unnecessary code.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// if x == true {}
+ /// if y == false {}
+ /// ```
+ /// use `x` directly:
+ /// ```rust,ignore
+ /// if x {}
+ /// if !y {}
+ /// ```
+ pub BOOL_COMPARISON,
+ complexity,
+ "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
+}
+
+declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
+
+impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ use self::Expression::{Bool, RetBool};
+ if e.span.from_expansion() {
+ return;
+ }
+ if let Some(higher::If {
+ cond,
+ then,
+ r#else: Some(r#else),
+ }) = higher::If::hir(e)
+ {
+ let reduce = |ret, not| {
+ let mut applicability = Applicability::MachineApplicable;
+ let snip = Sugg::hir_with_applicability(cx, cond, "<predicate>", &mut applicability);
+ let mut snip = if not { !snip } else { snip };
+
+ if ret {
+ snip = snip.make_return();
+ }
+
+ if is_else_clause(cx.tcx, e) {
+ snip = snip.blockify();
+ }
+
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_BOOL,
+ e.span,
+ "this if-then-else expression returns a bool literal",
+ "you can reduce it to",
+ snip.to_string(),
+ applicability,
+ );
+ };
+ if let ExprKind::Block(then, _) = then.kind {
+ match (fetch_bool_block(then), fetch_bool_expr(r#else)) {
+ (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
+ span_lint(
+ cx,
+ NEEDLESS_BOOL,
+ e.span,
+ "this if-then-else expression will always return true",
+ );
+ },
+ (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
+ span_lint(
+ cx,
+ NEEDLESS_BOOL,
+ e.span,
+ "this if-then-else expression will always return false",
+ );
+ },
+ (RetBool(true), RetBool(false)) => reduce(true, false),
+ (Bool(true), Bool(false)) => reduce(false, false),
+ (RetBool(false), RetBool(true)) => reduce(true, true),
+ (Bool(false), Bool(true)) => reduce(false, true),
+ _ => (),
+ }
+ } else {
+ panic!("IfExpr `then` node is not an `ExprKind::Block`");
+ }
+ }
+ }
+}
+
+declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
+
+impl<'tcx> LateLintPass<'tcx> for BoolComparison {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if e.span.from_expansion() {
+ return;
+ }
+
+ if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
+ let ignore_case = None::<(fn(_) -> _, &str)>;
+ let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
+ match node {
+ BinOpKind::Eq => {
+ let true_case = Some((|h| h, "equality checks against true are unnecessary"));
+ let false_case = Some((
+ |h: Sugg<'_>| !h,
+ "equality checks against false can be replaced by a negation",
+ ));
+ check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
+ },
+ BinOpKind::Ne => {
+ let true_case = Some((
+ |h: Sugg<'_>| !h,
+ "inequality checks against true can be replaced by a negation",
+ ));
+ let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
+ check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
+ },
+ BinOpKind::Lt => check_comparison(
+ cx,
+ e,
+ ignore_case,
+ Some((|h| h, "greater than checks against false are unnecessary")),
+ Some((
+ |h: Sugg<'_>| !h,
+ "less than comparison against true can be replaced by a negation",
+ )),
+ ignore_case,
+ Some((
+ |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
+ "order comparisons between booleans can be simplified",
+ )),
+ ),
+ BinOpKind::Gt => check_comparison(
+ cx,
+ e,
+ Some((
+ |h: Sugg<'_>| !h,
+ "less than comparison against true can be replaced by a negation",
+ )),
+ ignore_case,
+ ignore_case,
+ Some((|h| h, "greater than checks against false are unnecessary")),
+ Some((
+ |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
+ "order comparisons between booleans can be simplified",
+ )),
+ ),
+ _ => (),
+ }
+ }
+ }
+}
+
+struct ExpressionInfoWithSpan {
+ one_side_is_unary_not: bool,
+ left_span: Span,
+ right_span: Span,
+}
+
+fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
+ if let ExprKind::Unary(UnOp::Not, operand) = e.kind {
+ return (true, operand.span);
+ }
+ (false, e.span)
+}
+
+fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
+ let left = is_unary_not(left_side);
+ let right = is_unary_not(right_side);
+
+ ExpressionInfoWithSpan {
+ one_side_is_unary_not: left.0 != right.0,
+ left_span: left.1,
+ right_span: right.1,
+ }
+}
+
+fn check_comparison<'a, 'tcx>(
+ cx: &LateContext<'tcx>,
+ e: &'tcx Expr<'_>,
+ left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+ left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+ right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+ right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+ no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
+) {
+ use self::Expression::{Bool, Other};
+
+ if let ExprKind::Binary(op, left_side, right_side) = e.kind {
+ let (l_ty, r_ty) = (
+ cx.typeck_results().expr_ty(left_side),
+ cx.typeck_results().expr_ty(right_side),
+ );
+ if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() {
+ return;
+ }
+ if l_ty.is_bool() && r_ty.is_bool() {
+ let mut applicability = Applicability::MachineApplicable;
+
++ if op.node == BinOpKind::Eq {
+ let expression_info = one_side_is_unary_not(left_side, right_side);
+ if expression_info.one_side_is_unary_not {
+ span_lint_and_sugg(
+ cx,
+ BOOL_COMPARISON,
+ e.span,
+ "this comparison might be written more concisely",
+ "try simplifying it as shown",
+ format!(
+ "{} != {}",
+ snippet_with_applicability(cx, expression_info.left_span, "..", &mut applicability),
+ snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability)
+ ),
+ applicability,
+ );
+ }
+ }
+
+ match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
+ (Bool(true), Other) => left_true.map_or((), |(h, m)| {
+ suggest_bool_comparison(cx, e, right_side, applicability, m, h);
+ }),
+ (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
+ suggest_bool_comparison(cx, e, left_side, applicability, m, h);
+ }),
+ (Bool(false), Other) => left_false.map_or((), |(h, m)| {
+ suggest_bool_comparison(cx, e, right_side, applicability, m, h);
+ }),
+ (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
+ suggest_bool_comparison(cx, e, left_side, applicability, m, h);
+ }),
+ (Other, Other) => no_literal.map_or((), |(h, m)| {
+ let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
+ let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ BOOL_COMPARISON,
+ e.span,
+ m,
+ "try simplifying it as shown",
+ h(left_side, right_side).to_string(),
+ applicability,
+ );
+ }),
+ _ => (),
+ }
+ }
+ }
+}
+
+fn suggest_bool_comparison<'a, 'tcx>(
+ cx: &LateContext<'tcx>,
+ e: &'tcx Expr<'_>,
+ expr: &Expr<'_>,
+ mut applicability: Applicability,
+ message: &str,
+ conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
+) {
+ let hint = if expr.span.from_expansion() {
+ if applicability != Applicability::Unspecified {
+ applicability = Applicability::MaybeIncorrect;
+ }
+ Sugg::hir_with_macro_callsite(cx, expr, "..")
+ } else {
+ Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
+ };
+ span_lint_and_sugg(
+ cx,
+ BOOL_COMPARISON,
+ e.span,
+ message,
+ "try simplifying it as shown",
+ conv_hint(hint).to_string(),
+ applicability,
+ );
+}
+
+enum Expression {
+ Bool(bool),
+ RetBool(bool),
+ Other,
+}
+
+fn fetch_bool_block(block: &Block<'_>) -> Expression {
+ match (&*block.stmts, block.expr.as_ref()) {
+ (&[], Some(e)) => fetch_bool_expr(&**e),
+ (&[ref e], None) => {
+ if let StmtKind::Semi(e) = e.kind {
+ if let ExprKind::Ret(_) = e.kind {
+ fetch_bool_expr(e)
+ } else {
+ Expression::Other
+ }
+ } else {
+ Expression::Other
+ }
+ },
+ _ => Expression::Other,
+ }
+}
+
+fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
+ match expr.kind {
+ ExprKind::Block(block, _) => fetch_bool_block(block),
+ ExprKind::Lit(ref lit_ptr) => {
+ if let LitKind::Bool(value) = lit_ptr.node {
+ Expression::Bool(value)
+ } else {
+ Expression::Other
+ }
+ },
+ ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) {
+ Expression::Bool(value) => Expression::RetBool(value),
+ _ => Expression::Other,
+ },
+ _ => Expression::Other,
+ }
+}
--- /dev/null
- if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit));
+use clippy_utils::consts::{self, Constant};
+use clippy_utils::diagnostics::span_lint;
+use if_chain::if_chain;
+use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for multiplication by -1 as a form of negation.
+ ///
+ /// ### Why is this bad?
+ /// It's more readable to just negate.
+ ///
+ /// ### Known problems
+ /// This only catches integers (for now).
+ ///
+ /// ### Example
+ /// ```ignore
+ /// x * -1
+ /// ```
+ pub NEG_MULTIPLY,
+ style,
+ "multiplying integers with `-1`"
+}
+
+declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]);
+
+#[allow(clippy::match_same_arms)]
+impl<'tcx> LateLintPass<'tcx> for NegMultiply {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if let ExprKind::Binary(ref op, left, right) = e.kind {
+ if BinOpKind::Mul == op.node {
+ match (&left.kind, &right.kind) {
+ (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {},
+ (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right),
+ (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left),
+ _ => {},
+ }
+ }
+ }
+ }
+}
+
+fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Lit(ref l) = lit.kind;
++ if consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1);
+ if cx.typeck_results().expr_ty(exp).is_integral();
+ then {
+ span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`");
+ }
+ }
+}
--- /dev/null
- if let hir::AssocItemKind::Fn { has_self: false } = assoc_item.kind {
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::return_ty;
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::DiagnosticBuilderExt;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::HirIdSet;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::TyS;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for types with a `fn new() -> Self` method and no
+ /// implementation of
+ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
+ ///
+ /// ### Why is this bad?
+ /// The user might expect to be able to use
+ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
+ /// type can be constructed without arguments.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// struct Foo(Bar);
+ ///
+ /// impl Foo {
+ /// fn new() -> Self {
+ /// Foo(Bar::new())
+ /// }
+ /// }
+ /// ```
+ ///
+ /// To fix the lint, add a `Default` implementation that delegates to `new`:
+ ///
+ /// ```ignore
+ /// struct Foo(Bar);
+ ///
+ /// impl Default for Foo {
+ /// fn default() -> Self {
+ /// Foo::new()
+ /// }
+ /// }
+ /// ```
+ pub NEW_WITHOUT_DEFAULT,
+ style,
+ "`fn new() -> Self` method without `Default` implementation"
+}
+
+#[derive(Clone, Default)]
+pub struct NewWithoutDefault {
+ impling_types: Option<HirIdSet>,
+}
+
+impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
+
+impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
+ #[allow(clippy::too_many_lines)]
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ if let hir::ItemKind::Impl(hir::Impl {
+ of_trait: None,
+ ref generics,
+ self_ty: impl_self_ty,
+ items,
+ ..
+ }) = item.kind
+ {
+ for assoc_item in items {
++ if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
+ let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
+ if in_external_macro(cx.sess(), impl_item.span) {
+ return;
+ }
+ if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
+ let name = impl_item.ident.name;
+ let id = impl_item.hir_id();
+ if sig.header.constness == hir::Constness::Const {
+ // can't be implemented by default
+ return;
+ }
+ if sig.header.unsafety == hir::Unsafety::Unsafe {
+ // can't be implemented for unsafe new
+ return;
+ }
+ if impl_item
+ .generics
+ .params
+ .iter()
+ .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
+ {
+ // when the result of `new()` depends on a type parameter we should not require
+ // an
+ // impl of `Default`
+ return;
+ }
+ if_chain! {
+ if sig.decl.inputs.is_empty();
+ if name == sym::new;
+ if cx.access_levels.is_reachable(impl_item.def_id);
+ let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
+ let self_ty = cx.tcx.type_of(self_def_id);
+ if TyS::same_type(self_ty, return_ty(cx, id));
+ if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
+ then {
+ if self.impling_types.is_none() {
+ let mut impls = HirIdSet::default();
+ cx.tcx.for_each_impl(default_trait_id, |d| {
+ if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
+ if let Some(local_def_id) = ty_def.did.as_local() {
+ impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
+ }
+ }
+ });
+ self.impling_types = Some(impls);
+ }
+
+ // Check if a Default implementation exists for the Self type, regardless of
+ // generics
+ if_chain! {
+ if let Some(ref impling_types) = self.impling_types;
+ if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
+ if let Some(self_local_did) = self_def.did.as_local();
+ let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
+ if impling_types.contains(&self_id);
+ then {
+ return;
+ }
+ }
+
+ let generics_sugg = snippet(cx, generics.span, "");
+ let self_ty_fmt = self_ty.to_string();
+ let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt);
+ span_lint_hir_and_then(
+ cx,
+ NEW_WITHOUT_DEFAULT,
+ id,
+ impl_item.span,
+ &format!(
+ "you should consider adding a `Default` implementation for `{}`",
+ self_type_snip
+ ),
+ |diag| {
+ diag.suggest_prepend_item(
+ cx,
+ item.span,
+ "try adding this",
+ &create_new_without_default_suggest_msg(&self_type_snip, &generics_sugg),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String {
+ #[rustfmt::skip]
+ format!(
+"impl{} Default for {} {{
+ fn default() -> Self {{
+ Self::new()
+ }}
+}}", generics_sugg, self_type_snip)
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::is_lint_allowed;
++use clippy_utils::source::snippet;
++use clippy_utils::ty::{implements_trait, is_copy};
++use rustc_ast::ImplPolarity;
++use rustc_hir::def_id::DefId;
++use rustc_hir::{FieldDef, Item, ItemKind, Node};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::sym;
++
++declare_clippy_lint! {
++ /// ### What it does
++ /// Warns about fields in struct implementing `Send` that are neither `Send` nor `Copy`.
++ ///
++ /// ### Why is this bad?
++ /// Sending the struct to another thread will transfer the ownership to
++ /// the new thread by dropping in the current thread during the transfer.
++ /// This causes soundness issues for non-`Send` fields, as they are also
++ /// dropped and might not be set up to handle this.
++ ///
++ /// See:
++ /// * [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html)
++ /// * [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html)
++ ///
++ /// ### Known Problems
++ /// Data structures that contain raw pointers may cause false positives.
++ /// They are sometimes safe to be sent across threads but do not implement
++ /// the `Send` trait. This lint has a heuristic to filter out basic cases
++ /// such as `Vec<*const T>`, but it's not perfect. Feel free to create an
++ /// issue if you have a suggestion on how this heuristic can be improved.
++ ///
++ /// ### Example
++ /// ```rust,ignore
++ /// struct ExampleStruct<T> {
++ /// rc_is_not_send: Rc<String>,
++ /// unbounded_generic_field: T,
++ /// }
++ ///
++ /// // This impl is unsound because it allows sending `!Send` types through `ExampleStruct`
++ /// unsafe impl<T> Send for ExampleStruct<T> {}
++ /// ```
++ /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
++ /// or specify correct bounds on generic type parameters (`T: Send`).
++ pub NON_SEND_FIELDS_IN_SEND_TY,
++ nursery,
++ "there is field that does not implement `Send` in a `Send` struct"
++}
++
++#[derive(Copy, Clone)]
++pub struct NonSendFieldInSendTy {
++ enable_raw_pointer_heuristic: bool,
++}
++
++impl NonSendFieldInSendTy {
++ pub fn new(enable_raw_pointer_heuristic: bool) -> Self {
++ Self {
++ enable_raw_pointer_heuristic,
++ }
++ }
++}
++
++impl_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELDS_IN_SEND_TY]);
++
++impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
++ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
++ let ty_allowed_in_send = if self.enable_raw_pointer_heuristic {
++ ty_allowed_with_raw_pointer_heuristic
++ } else {
++ ty_allowed_without_raw_pointer_heuristic
++ };
++
++ // Checks if we are in `Send` impl item.
++ // We start from `Send` impl instead of `check_field_def()` because
++ // single `AdtDef` may have multiple `Send` impls due to generic
++ // parameters, and the lint is much easier to implement in this way.
++ if_chain! {
++ if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send);
++ if let ItemKind::Impl(hir_impl) = &item.kind;
++ if let Some(trait_ref) = &hir_impl.of_trait;
++ if let Some(trait_id) = trait_ref.trait_def_id();
++ if send_trait == trait_id;
++ if hir_impl.polarity == ImplPolarity::Positive;
++ if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.def_id);
++ if let self_ty = ty_trait_ref.self_ty();
++ if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
++ then {
++ let mut non_send_fields = Vec::new();
++
++ let hir_map = cx.tcx.hir();
++ for variant in &adt_def.variants {
++ for field in &variant.fields {
++ if_chain! {
++ if let Some(field_hir_id) = field
++ .did
++ .as_local()
++ .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id));
++ if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id);
++ if let field_ty = field.ty(cx.tcx, impl_trait_substs);
++ if !ty_allowed_in_send(cx, field_ty, send_trait);
++ if let Node::Field(field_def) = hir_map.get(field_hir_id);
++ then {
++ non_send_fields.push(NonSendField {
++ def: field_def,
++ ty: field_ty,
++ generic_params: collect_generic_params(cx, field_ty),
++ })
++ }
++ }
++ }
++ }
++
++ if !non_send_fields.is_empty() {
++ span_lint_and_then(
++ cx,
++ NON_SEND_FIELDS_IN_SEND_TY,
++ item.span,
++ &format!(
++ "this implementation is unsound, as some fields in `{}` are `!Send`",
++ snippet(cx, hir_impl.self_ty.span, "Unknown")
++ ),
++ |diag| {
++ for field in non_send_fields {
++ diag.span_note(
++ field.def.span,
++ &format!("the type of field `{}` is `!Send`", field.def.ident.name),
++ );
++
++ match field.generic_params.len() {
++ 0 => diag.help("use a thread-safe type that implements `Send`"),
++ 1 if is_ty_param(field.ty) => diag.help(&format!("add `{}: Send` bound in `Send` impl", field.ty)),
++ _ => diag.help(&format!(
++ "add bounds on type parameter{} `{}` that satisfy `{}: Send`",
++ if field.generic_params.len() > 1 { "s" } else { "" },
++ field.generic_params_string(),
++ snippet(cx, field.def.ty.span, "Unknown"),
++ )),
++ };
++ }
++ },
++ );
++ }
++ }
++ }
++ }
++}
++
++struct NonSendField<'tcx> {
++ def: &'tcx FieldDef<'tcx>,
++ ty: Ty<'tcx>,
++ generic_params: Vec<Ty<'tcx>>,
++}
++
++impl<'tcx> NonSendField<'tcx> {
++ fn generic_params_string(&self) -> String {
++ self.generic_params
++ .iter()
++ .map(ToString::to_string)
++ .collect::<Vec<_>>()
++ .join(", ")
++ }
++}
++
++/// Given a type, collect all of its generic parameters.
++/// Example: `MyStruct<P, Box<Q, R>>` => `vec![P, Q, R]`
++fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec<Ty<'tcx>> {
++ ty.walk(cx.tcx)
++ .filter_map(|inner| match inner.unpack() {
++ GenericArgKind::Type(inner_ty) => Some(inner_ty),
++ _ => None,
++ })
++ .filter(|&inner_ty| is_ty_param(inner_ty))
++ .collect()
++}
++
++/// Be more strict when the heuristic is disabled
++fn ty_allowed_without_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool {
++ if implements_trait(cx, ty, send_trait, &[]) {
++ return true;
++ }
++
++ if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) {
++ return true;
++ }
++
++ false
++}
++
++/// Heuristic to allow cases like `Vec<*const u8>`
++fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool {
++ if implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) {
++ return true;
++ }
++
++ // The type is known to be `!Send` and `!Copy`
++ match ty.kind() {
++ ty::Tuple(_) => ty
++ .tuple_fields()
++ .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
++ ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
++ ty::Adt(_, substs) => {
++ if contains_raw_pointer(cx, ty) {
++ // descends only if ADT contains any raw pointers
++ substs.iter().all(|generic_arg| match generic_arg.unpack() {
++ GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
++ // Lifetimes and const generics are not solid part of ADT and ignored
++ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true,
++ })
++ } else {
++ false
++ }
++ },
++ // Raw pointers are `!Send` but allowed by the heuristic
++ ty::RawPtr(_) => true,
++ _ => false,
++ }
++}
++
++/// Checks if the type contains any raw pointers in substs (including nested ones).
++fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
++ for ty_node in target_ty.walk(cx.tcx) {
++ if_chain! {
++ if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
++ if let ty::RawPtr(_) = inner_ty.kind();
++ then {
++ return true;
++ }
++ }
++ }
++
++ false
++}
++
++/// Returns `true` if the type is a type parameter such as `T`.
++fn is_ty_param(target_ty: Ty<'_>) -> bool {
++ matches!(target_ty.kind(), ty::Param(_))
++}
--- /dev/null
- if let BinOpKind::Lt = op.node {
- if let BinOpKind::Add = op2.node {
- span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
- "you are trying to use classic C overflow conditions that will fail in Rust");
- }
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::SpanlessEq;
+use if_chain::if_chain;
+use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects classic underflow/overflow checks.
+ ///
+ /// ### Why is this bad?
+ /// Most classic C underflow/overflow checks will fail in
+ /// Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let a = 1;
+ /// # let b = 2;
+ /// a + b < a;
+ /// ```
+ pub OVERFLOW_CHECK_CONDITIONAL,
+ complexity,
+ "overflow checks inspired by C which are likely to panic"
+}
+
+declare_lint_pass!(OverflowCheckConditional => [OVERFLOW_CHECK_CONDITIONAL]);
+
++const OVERFLOW_MSG: &str = "you are trying to use classic C overflow conditions that will fail in Rust";
++const UNDERFLOW_MSG: &str = "you are trying to use classic C underflow conditions that will fail in Rust";
++
+impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional {
+ // a + b < a, a > a + b, a < a - b, a - b > a
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r);
+ if_chain! {
+ if let ExprKind::Binary(ref op, first, second) = expr.kind;
+ if let ExprKind::Binary(ref op2, ident1, ident2) = first.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind;
+ if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]);
+ if cx.typeck_results().expr_ty(ident1).is_integral();
+ if cx.typeck_results().expr_ty(ident2).is_integral();
+ then {
- if let BinOpKind::Gt = op.node {
- if let BinOpKind::Sub = op2.node {
- span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
- "you are trying to use classic C underflow conditions that will fail in Rust");
- }
++ if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG);
+ }
- if let BinOpKind::Gt = op.node {
- if let BinOpKind::Add = op2.node {
- span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
- "you are trying to use classic C overflow conditions that will fail in Rust");
- }
++ if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG);
+ }
+ }
+ }
+
+ if_chain! {
+ if let ExprKind::Binary(ref op, first, second) = expr.kind;
+ if let ExprKind::Binary(ref op2, ident1, ident2) = second.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind;
+ if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]);
+ if cx.typeck_results().expr_ty(ident1).is_integral();
+ if cx.typeck_results().expr_ty(ident2).is_integral();
+ then {
- if let BinOpKind::Lt = op.node {
- if let BinOpKind::Sub = op2.node {
- span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
- "you are trying to use classic C underflow conditions that will fail in Rust");
- }
++ if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG);
+ }
++ if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG);
+ }
+ }
+ }
+ }
+}
--- /dev/null
- if !cx.access_levels.is_exported(item.def_id) {
- if let Some(false) = self.is_exported.last() {
- let span = item.span.with_hi(item.ident.span.hi());
- let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
- span_lint_and_then(
- cx,
- REDUNDANT_PUB_CRATE,
- span,
- &format!("pub(crate) {} inside private module", descr),
- |diag| {
- diag.span_suggestion(
- item.vis.span,
- "consider using",
- "pub".to_string(),
- Applicability::MachineApplicable,
- );
- },
- );
- }
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{Item, ItemKind, VisibilityKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for items declared `pub(crate)` that are not crate visible because they
+ /// are inside a private module.
+ ///
+ /// ### Why is this bad?
+ /// Writing `pub(crate)` is misleading when it's redundant due to the parent
+ /// module's visibility.
+ ///
+ /// ### Example
+ /// ```rust
+ /// mod internal {
+ /// pub(crate) fn internal_fn() { }
+ /// }
+ /// ```
+ /// This function is not visible outside the module and it can be declared with `pub` or
+ /// private visibility
+ /// ```rust
+ /// mod internal {
+ /// pub fn internal_fn() { }
+ /// }
+ /// ```
+ pub REDUNDANT_PUB_CRATE,
+ nursery,
+ "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
+}
+
+#[derive(Default)]
+pub struct RedundantPubCrate {
+ is_exported: Vec<bool>,
+}
+
+impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]);
+
+impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ if let VisibilityKind::Crate { .. } = item.vis.node {
++ if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) {
++ let span = item.span.with_hi(item.ident.span.hi());
++ let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
++ span_lint_and_then(
++ cx,
++ REDUNDANT_PUB_CRATE,
++ span,
++ &format!("pub(crate) {} inside private module", descr),
++ |diag| {
++ diag.span_suggestion(
++ item.vis.span,
++ "consider using",
++ "pub".to_string(),
++ Applicability::MachineApplicable,
++ );
++ },
++ );
+ }
+ }
+
+ if let ItemKind::Mod { .. } = item.kind {
+ self.is_exported.push(cx.access_levels.is_exported(item.def_id));
+ }
+ }
+
+ fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ if let ItemKind::Mod { .. } = item.kind {
+ self.is_exported.pop().expect("unbalanced check_item/check_item_post");
+ }
+ }
+}
--- /dev/null
- if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(count);
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
+ /// - `.to_string()` for `str`
+ /// - `.clone()` for `String`
+ /// - `.to_vec()` for `slice`
+ ///
+ /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
+ /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
+ ///
+ /// ### Why is this bad?
+ /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
+ /// the string is the intention behind this, `clone()` should be used.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn main() {
+ /// let x = String::from("hello world").repeat(1);
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn main() {
+ /// let x = String::from("hello world").clone();
+ /// }
+ /// ```
+ pub REPEAT_ONCE,
+ complexity,
+ "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
+}
+
+declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
+
+impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
+ if_chain! {
+ if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
+ if path.ident.name == sym!(repeat);
++ if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
+ if !in_macro(receiver.span);
+ then {
+ let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
+ if ty.is_str() {
+ span_lint_and_sugg(
+ cx,
+ REPEAT_ONCE,
+ expr.span,
+ "calling `repeat(1)` on str",
+ "consider using `.to_string()` instead",
+ format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ } else if ty.builtin_index().is_some() {
+ span_lint_and_sugg(
+ cx,
+ REPEAT_ONCE,
+ expr.span,
+ "calling `repeat(1)` on slice",
+ "consider using `.to_vec()` instead",
+ format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ } else if is_type_diagnostic_item(cx, ty, sym::String) {
+ span_lint_and_sugg(
+ cx,
+ REPEAT_ONCE,
+ expr.span,
+ "calling `repeat(1)` on a string literal",
+ "consider using `.clone()` instead",
+ format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+}
--- /dev/null
- use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::source::snippet;
- use clippy_utils::{contains_name, higher, iter_input_pats};
- use rustc_hir::intravisit::FnKind;
- use rustc_hir::{
- Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
- UnOp,
- };
- use rustc_lint::{LateContext, LateLintPass, LintContext};
- use rustc_middle::lint::in_external_macro;
- use rustc_middle::ty;
- use rustc_session::{declare_lint_pass, declare_tool_lint};
- use rustc_span::source_map::Span;
- use rustc_span::symbol::Symbol;
++use clippy_utils::visitors::is_local_used;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::def::Res;
++use rustc_hir::def_id::LocalDefId;
++use rustc_hir::hir_id::ItemLocalId;
++use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{Span, Symbol};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for bindings that shadow other bindings already in
+ /// scope, while just changing reference level or mutability.
+ ///
+ /// ### Why is this bad?
+ /// Not much, in fact it's a very common pattern in Rust
+ /// code. Still, some may opt to avoid it in their code base, they can set this
+ /// lint to `Warn`.
+ ///
- /// ### Known problems
- /// This lint, as the other shadowing related lints,
- /// currently only catches very simple patterns.
- ///
+ /// ### Example
+ /// ```rust
+ /// # let x = 1;
+ /// // Bad
+ /// let x = &x;
+ ///
+ /// // Good
+ /// let y = &x; // use different variable name
+ /// ```
+ pub SHADOW_SAME,
+ restriction,
+ "rebinding a name to itself, e.g., `let mut x = &mut x`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for bindings that shadow other bindings already in
+ /// scope, while reusing the original value.
+ ///
+ /// ### Why is this bad?
+ /// Not too much, in fact it's a common pattern in Rust
+ /// code. Still, some argue that name shadowing like this hurts readability,
+ /// because a value may be bound to different things depending on position in
+ /// the code.
+ ///
- /// ### Known problems
- /// This lint, as the other shadowing related lints,
- /// currently only catches very simple patterns.
- ///
+ /// ### Example
+ /// ```rust
+ /// let x = 2;
+ /// let x = x + 1;
+ /// ```
+ /// use different variable name:
+ /// ```rust
+ /// let x = 2;
+ /// let y = x + 1;
+ /// ```
+ pub SHADOW_REUSE,
+ restriction,
+ "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for bindings that shadow other bindings already in
+ /// scope, either without an initialization or with one that does not even use
+ /// the original value.
+ ///
+ /// ### Why is this bad?
+ /// Name shadowing can hurt readability, especially in
+ /// large code bases, because it is easy to lose track of the active binding at
+ /// any place in the code. This can be alleviated by either giving more specific
+ /// names to bindings or introducing more scopes to contain the bindings.
+ ///
- /// ### Known problems
- /// This lint, as the other shadowing related lints,
- /// currently only catches very simple patterns. Note that
- /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level
- /// for this lint.
- ///
+ /// ### Example
+ /// ```rust
+ /// # let y = 1;
+ /// # let z = 2;
+ /// let x = y;
+ ///
+ /// // Bad
+ /// let x = z; // shadows the earlier binding
+ ///
+ /// // Good
+ /// let w = z; // use different variable name
+ /// ```
+ pub SHADOW_UNRELATED,
- pedantic,
++ restriction,
+ "rebinding a name without even using the original value"
+}
+
- declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
++#[derive(Default)]
++pub(crate) struct Shadow {
++ bindings: Vec<FxHashMap<Symbol, Vec<ItemLocalId>>>,
++}
++
++impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
+
+impl<'tcx> LateLintPass<'tcx> for Shadow {
- fn check_fn(
- &mut self,
- cx: &LateContext<'tcx>,
- _: FnKind<'tcx>,
- decl: &'tcx FnDecl<'_>,
- body: &'tcx Body<'_>,
- _: Span,
- _: HirId,
- ) {
- if in_external_macro(cx.sess(), body.value.span) {
++ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
++ let (id, ident) = match pat.kind {
++ PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
++ _ => return,
++ };
++ if ident.span.from_expansion() || ident.span.is_dummy() {
+ return;
+ }
- check_fn(cx, decl, body);
- }
- }
++ let HirId { owner, local_id } = id;
+
- fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) {
- let mut bindings = Vec::with_capacity(decl.inputs.len());
- for arg in iter_input_pats(decl, body) {
- if let PatKind::Binding(.., ident, _) = arg.pat.kind {
- bindings.push((ident.name, ident.span));
++ // get (or insert) the list of items for this owner and symbol
++ let data = self.bindings.last_mut().unwrap();
++ let items_with_name = data.entry(ident.name).or_default();
++
++ // check other bindings with the same name, most recently seen first
++ for &prev in items_with_name.iter().rev() {
++ if prev == local_id {
++ // repeated binding in an `Or` pattern
++ return;
++ }
++
++ if is_shadow(cx, owner, prev, local_id) {
++ let prev_hir_id = HirId { owner, local_id: prev };
++ lint_shadow(cx, pat, prev_hir_id, ident.span);
++ // only lint against the "nearest" shadowed binding
++ break;
++ }
+ }
++ // store the binding
++ items_with_name.push(local_id);
+ }
- check_expr(cx, &body.value, &mut bindings);
- }
+
- fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) {
- let len = bindings.len();
- for stmt in block.stmts {
- match stmt.kind {
- StmtKind::Local(local) => check_local(cx, local, bindings),
- StmtKind::Expr(e) | StmtKind::Semi(e) => check_expr(cx, e, bindings),
- StmtKind::Item(..) => {},
++ fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
++ let hir = cx.tcx.hir();
++ if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) {
++ self.bindings.push(FxHashMap::default());
+ }
+ }
- if let Some(o) = block.expr {
- check_expr(cx, o, bindings);
- }
- bindings.truncate(len);
- }
+
- fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) {
- if in_external_macro(cx.sess(), local.span) {
- return;
- }
- if higher::is_from_for_desugar(local) {
- return;
- }
- let Local {
- pat,
- ref ty,
- ref init,
- span,
- ..
- } = *local;
- if let Some(t) = *ty {
- check_ty(cx, t, bindings);
- }
- if let Some(o) = *init {
- check_expr(cx, o, bindings);
- check_pat(cx, pat, Some(o), span, bindings);
- } else {
- check_pat(cx, pat, None, span, bindings);
++ fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
++ let hir = cx.tcx.hir();
++ if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) {
++ self.bindings.pop();
++ }
+ }
+}
+
- fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool {
- let var_ty = cx.typeck_results().node_type_opt(pat_id);
- var_ty.map_or(false, |var_ty| !matches!(var_ty.kind(), ty::Adt(..)))
++fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool {
++ let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id());
++ let first_scope = scope_tree.var_scope(first);
++ let second_scope = scope_tree.var_scope(second);
++ scope_tree.is_subscope_of(second_scope, first_scope)
+}
+
- fn check_pat<'tcx>(
- cx: &LateContext<'tcx>,
- pat: &'tcx Pat<'_>,
- init: Option<&'tcx Expr<'_>>,
- span: Span,
- bindings: &mut Vec<(Symbol, Span)>,
- ) {
- // TODO: match more stuff / destructuring
- match pat.kind {
- PatKind::Binding(.., ident, ref inner) => {
- let name = ident.name;
- if is_binding(cx, pat.hir_id) {
- let mut new_binding = true;
- for tup in bindings.iter_mut() {
- if tup.0 == name {
- lint_shadow(cx, name, span, pat.span, init, tup.1);
- tup.1 = ident.span;
- new_binding = false;
- break;
- }
- }
- if new_binding {
- bindings.push((name, ident.span));
- }
- }
- if let Some(p) = *inner {
- check_pat(cx, p, init, span, bindings);
- }
- },
- PatKind::Struct(_, pfields, _) => {
- if let Some(init_struct) = init {
- if let ExprKind::Struct(_, efields, _) = init_struct.kind {
- for field in pfields {
- let name = field.ident.name;
- let efield = efields
- .iter()
- .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None });
- check_pat(cx, field.pat, efield, span, bindings);
- }
- } else {
- for field in pfields {
- check_pat(cx, field.pat, init, span, bindings);
- }
- }
- } else {
- for field in pfields {
- check_pat(cx, field.pat, None, span, bindings);
- }
- }
++fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) {
++ let (lint, msg) = match find_init(cx, pat.hir_id) {
++ Some(expr) if is_self_shadow(cx, pat, expr, shadowed) => {
++ let msg = format!(
++ "`{}` is shadowed by itself in `{}`",
++ snippet(cx, pat.span, "_"),
++ snippet(cx, expr.span, "..")
++ );
++ (SHADOW_SAME, msg)
+ },
- PatKind::Tuple(inner, _) => {
- if let Some(init_tup) = init {
- if let ExprKind::Tup(tup) = init_tup.kind {
- for (i, p) in inner.iter().enumerate() {
- check_pat(cx, p, Some(&tup[i]), p.span, bindings);
- }
- } else {
- for p in inner {
- check_pat(cx, p, init, span, bindings);
- }
- }
- } else {
- for p in inner {
- check_pat(cx, p, None, span, bindings);
- }
- }
++ Some(expr) if is_local_used(cx, expr, shadowed) => {
++ let msg = format!(
++ "`{}` is shadowed by `{}` which reuses the original value",
++ snippet(cx, pat.span, "_"),
++ snippet(cx, expr.span, "..")
++ );
++ (SHADOW_REUSE, msg)
+ },
- PatKind::Box(inner) => {
- if let Some(initp) = init {
- if let ExprKind::Box(inner_init) = initp.kind {
- check_pat(cx, inner, Some(inner_init), span, bindings);
- } else {
- check_pat(cx, inner, init, span, bindings);
- }
- } else {
- check_pat(cx, inner, init, span, bindings);
- }
++ _ => {
++ let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
++ (SHADOW_UNRELATED, msg)
+ },
- PatKind::Ref(inner, _) => check_pat(cx, inner, init, span, bindings),
- // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
- _ => (),
- }
++ };
++ span_lint_and_note(
++ cx,
++ lint,
++ span,
++ &msg,
++ Some(cx.tcx.hir().span(shadowed)),
++ "previous binding is here",
++ );
+}
+
- fn lint_shadow<'tcx>(
- cx: &LateContext<'tcx>,
- name: Symbol,
- span: Span,
- pattern_span: Span,
- init: Option<&'tcx Expr<'_>>,
- prev_span: Span,
- ) {
- if let Some(expr) = init {
- if is_self_shadow(name, expr) {
- span_lint_and_then(
- cx,
- SHADOW_SAME,
- span,
- &format!(
- "`{}` is shadowed by itself in `{}`",
- snippet(cx, pattern_span, "_"),
- snippet(cx, expr.span, "..")
- ),
- |diag| {
- diag.span_note(prev_span, "previous binding is here");
- },
- );
- } else if contains_name(name, expr) {
- span_lint_and_then(
- cx,
- SHADOW_REUSE,
- pattern_span,
- &format!(
- "`{}` is shadowed by `{}` which reuses the original value",
- snippet(cx, pattern_span, "_"),
- snippet(cx, expr.span, "..")
- ),
- |diag| {
- diag.span_note(expr.span, "initialization happens here");
- diag.span_note(prev_span, "previous binding is here");
- },
- );
- } else {
- span_lint_and_then(
- cx,
- SHADOW_UNRELATED,
- pattern_span,
- &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")),
- |diag| {
- diag.span_note(expr.span, "initialization happens here");
- diag.span_note(prev_span, "previous binding is here");
++/// Returns true if the expression is a simple transformation of a local binding such as `&x`
++fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_id: HirId) -> bool {
++ let hir = cx.tcx.hir();
++ let is_direct_binding = hir
++ .parent_iter(pat.hir_id)
++ .map_while(|(_id, node)| match node {
++ Node::Pat(pat) => Some(pat),
++ _ => None,
++ })
++ .all(|pat| matches!(pat.kind, PatKind::Ref(..) | PatKind::Or(_)));
++ if !is_direct_binding {
++ return false;
++ }
++ loop {
++ expr = match expr.kind {
++ ExprKind::Box(e)
++ | ExprKind::AddrOf(_, _, e)
++ | ExprKind::Block(
++ &Block {
++ stmts: [],
++ expr: Some(e),
++ ..
+ },
- );
++ _,
++ )
++ | ExprKind::Unary(UnOp::Deref, e) => e,
++ ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id),
++ _ => break false,
+ }
- } else {
- span_lint_and_then(
- cx,
- SHADOW_UNRELATED,
- span,
- &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
- |diag| {
- diag.span_note(prev_span, "previous binding is here");
- },
- );
+ }
+}
+
- fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) {
- if in_external_macro(cx.sess(), expr.span) {
- return;
- }
- match expr.kind {
- ExprKind::Unary(_, e) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Box(e) => {
- check_expr(cx, e, bindings);
- },
- ExprKind::Block(block, _) | ExprKind::Loop(block, ..) => check_block(cx, block, bindings),
- // ExprKind::Call
- // ExprKind::MethodCall
- ExprKind::Array(v) | ExprKind::Tup(v) => {
- for e in v {
- check_expr(cx, e, bindings);
- }
- },
- ExprKind::If(cond, then, ref otherwise) => {
- check_expr(cx, cond, bindings);
- check_expr(cx, then, bindings);
- if let Some(o) = *otherwise {
- check_expr(cx, o, bindings);
- }
- },
- ExprKind::Match(init, arms, _) => {
- check_expr(cx, init, bindings);
- let len = bindings.len();
- for arm in arms {
- check_pat(cx, arm.pat, Some(init), arm.pat.span, bindings);
- // This is ugly, but needed to get the right type
- if let Some(ref guard) = arm.guard {
- match guard {
- Guard::If(if_expr) => check_expr(cx, if_expr, bindings),
- Guard::IfLet(guard_pat, guard_expr) => {
- check_pat(cx, guard_pat, Some(*guard_expr), guard_pat.span, bindings);
- check_expr(cx, guard_expr, bindings);
- },
- }
- }
- check_expr(cx, arm.body, bindings);
- bindings.truncate(len);
- }
- },
- _ => (),
- }
- }
-
- fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) {
- match ty.kind {
- TyKind::Slice(sty) => check_ty(cx, sty, bindings),
- TyKind::Array(fty, ref anon_const) => {
- check_ty(cx, fty, bindings);
- check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings);
- },
- TyKind::Ptr(MutTy { ty: mty, .. }) | TyKind::Rptr(_, MutTy { ty: mty, .. }) => check_ty(cx, mty, bindings),
- TyKind::Tup(tup) => {
- for t in tup {
- check_ty(cx, t, bindings);
- }
- },
- TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings),
- _ => (),
- }
- }
-
- fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
- match expr.kind {
- ExprKind::Box(inner) | ExprKind::AddrOf(_, _, inner) => is_self_shadow(name, inner),
- ExprKind::Block(block, _) => {
- block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e))
- },
- ExprKind::Unary(op, inner) => (UnOp::Deref == op) && is_self_shadow(name, inner),
- ExprKind::Path(QPath::Resolved(_, path)) => path_eq_name(name, path),
- _ => false,
++/// Finds the "init" expression for a pattern: `let <pat> = <init>;` or
++/// `match <init> { .., <pat> => .., .. }`
++fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
++ for (_, node) in cx.tcx.hir().parent_iter(hir_id) {
++ let init = match node {
++ Node::Arm(_) | Node::Pat(_) => continue,
++ Node::Expr(expr) => match expr.kind {
++ ExprKind::Match(e, _, _) => Some(e),
++ _ => None,
++ },
++ Node::Local(local) => local.init,
++ _ => None,
++ };
++ return init;
+ }
- }
-
- fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
- !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name
++ None
+}
--- /dev/null
- let x = const_eval_context.expr(arg);
- if let Some(Constant::RawPtr(0)) = x;
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_expr_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for transmute calls which would receive a null pointer.
+ ///
+ /// ### Why is this bad?
+ /// Transmuting a null pointer is undefined behavior.
+ ///
+ /// ### Known problems
+ /// Not all cases can be detected at the moment of this writing.
+ /// For example, variables which hold a null pointer and are then fed to a `transmute`
+ /// call, aren't detectable yet.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
+ /// ```
+ pub TRANSMUTING_NULL,
+ correctness,
+ "transmutes from a null pointer to a reference, which is undefined behavior"
+}
+
+declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
+
+const LINT_MSG: &str = "transmuting a known null pointer into a reference";
+
+impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+
+ if_chain! {
+ if let ExprKind::Call(func, [arg]) = expr.kind;
+ if is_expr_diagnostic_item(cx, func, sym::transmute);
+
+ then {
+ // Catching transmute over constants that resolve to `null`.
+ let mut const_eval_context = constant_context(cx, cx.typeck_results());
+ if_chain! {
+ if let ExprKind::Path(ref _qpath) = arg.kind;
++ if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
++ if x == 0;
+ then {
+ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
+ }
+ }
+
+ // Catching:
+ // `std::mem::transmute(0 as *const i32)`
+ if_chain! {
+ if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
+ if let ExprKind::Lit(ref lit) = inner_expr.kind;
+ if let LitKind::Int(0, _) = lit.node;
+ then {
+ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
+ }
+ }
+
+ // Catching:
+ // `std::mem::transmute(std::ptr::null::<i32>())`
+ if_chain! {
+ if let ExprKind::Call(func1, []) = arg.kind;
+ if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
+ then {
+ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
+ }
+ }
+
+ // FIXME:
+ // Also catch transmutations of variables which are known nulls.
+ // To do this, MIR const propagation seems to be the better tool.
+ // Whenever MIR const prop routines are more developed, this will
+ // become available. As of this writing (25/03/19) it is not yet.
+ }
+ }
+ }
+}
--- /dev/null
- if cx.tcx.is_diagnostic_item(sym::Option, def_id)
- && is_ty_param_diagnostic_item(cx, qpath, sym::Option).is_some()
- {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_ty_param_diagnostic_item;
+use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::OPTION_OPTION;
+
+pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
++ if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_ty_param_diagnostic_item(cx, qpath, sym::Option).is_some() {
+ span_lint(
+ cx,
+ OPTION_OPTION,
+ hir_ty.span,
+ "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
+ enum if you need to distinguish all 3 cases",
+ );
+ true
+ } else {
+ false
+ }
+}
--- /dev/null
+//! Read configurations files.
+
+#![allow(clippy::module_name_repetitions)]
+
+use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
+use serde::Deserialize;
+use std::error::Error;
+use std::path::{Path, PathBuf};
+use std::{env, fmt, fs, io};
+
+/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Rename {
+ pub path: String,
+ pub rename: String,
+}
+
+/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum DisallowedMethod {
+ Simple(String),
+ WithReason { path: String, reason: Option<String> },
+}
+
+/// Conf with parse errors
+#[derive(Default)]
+pub struct TryConf {
+ pub conf: Conf,
+ pub errors: Vec<String>,
+}
+
+impl TryConf {
+ fn from_error(error: impl Error) -> Self {
+ Self {
+ conf: Conf::default(),
+ errors: vec![error.to_string()],
+ }
+ }
+}
+
+macro_rules! define_Conf {
+ ($(
+ $(#[doc = $doc:literal])+
+ $(#[conf_deprecated($dep:literal)])?
+ ($name:ident: $ty:ty = $default:expr),
+ )*) => {
+ /// Clippy lint configuration
+ pub struct Conf {
+ $($(#[doc = $doc])+ pub $name: $ty,)*
+ }
+
+ mod defaults {
+ $(pub fn $name() -> $ty { $default })*
+ }
+
+ impl Default for Conf {
+ fn default() -> Self {
+ Self { $($name: defaults::$name(),)* }
+ }
+ }
+
+ impl<'de> Deserialize<'de> for TryConf {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+ deserializer.deserialize_map(ConfVisitor)
+ }
+ }
+
+ #[derive(Deserialize)]
+ #[serde(field_identifier, rename_all = "kebab-case")]
+ #[allow(non_camel_case_types)]
+ enum Field { $($name,)* third_party, }
+
+ struct ConfVisitor;
+
+ impl<'de> Visitor<'de> for ConfVisitor {
+ type Value = TryConf;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("Conf")
+ }
+
+ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
+ let mut errors = Vec::new();
+ $(let mut $name = None;)*
+ // could get `Field` here directly, but get `str` first for diagnostics
+ while let Some(name) = map.next_key::<&str>()? {
+ match Field::deserialize(name.into_deserializer())? {
+ $(Field::$name => {
+ $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
+ match map.next_value() {
+ Err(e) => errors.push(e.to_string()),
+ Ok(value) => match $name {
+ Some(_) => errors.push(format!("duplicate field `{}`", name)),
+ None => $name = Some(value),
+ }
+ }
+ })*
+ // white-listed; ignore
+ Field::third_party => drop(map.next_value::<IgnoredAny>())
+ }
+ }
+ let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
+ Ok(TryConf { conf, errors })
+ }
+ }
+
+ #[cfg(feature = "metadata-collector-lint")]
+ pub mod metadata {
+ use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
+
+ macro_rules! wrap_option {
+ () => (None);
+ ($x:literal) => (Some($x));
+ }
+
+ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
+ vec![
+ $(
+ {
+ let deprecation_reason = wrap_option!($($dep)?);
+
+ ClippyConfiguration::new(
+ stringify!($name),
+ stringify!($ty),
+ format!("{:?}", super::defaults::$name()),
+ concat!($($doc, '\n',)*),
+ deprecation_reason,
+ )
+ },
+ )+
+ ]
+ }
+ }
+ };
+}
+
+define_Conf! {
+ /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+ ///
+ /// Suppress lints whenever the suggested change would cause breakage for other crates.
+ (avoid_breaking_exported_api: bool = true),
+ /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT.
+ ///
+ /// The minimum rust version that the project supports
+ (msrv: Option<String> = None),
+ /// Lint: BLACKLISTED_NAME.
+ ///
+ /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
+ (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
+ /// Lint: COGNITIVE_COMPLEXITY.
+ ///
+ /// The maximum cognitive complexity a function can have
+ (cognitive_complexity_threshold: u64 = 25),
+ /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
+ ///
+ /// Use the Cognitive Complexity lint instead.
+ #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
+ (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: Vec<String> = [
+ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
+ "DirectX",
+ "ECMAScript",
+ "GPLv2", "GPLv3",
+ "GitHub", "GitLab",
+ "IPv4", "IPv6",
+ "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
+ "NaN", "NaNs",
+ "OAuth", "GraphQL",
+ "OCaml",
+ "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
+ "WebGL",
+ "TensorFlow",
+ "TrueType",
+ "iOS", "macOS", "FreeBSD",
+ "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: u64 = 7),
+ /// Lint: TYPE_COMPLEXITY.
+ ///
+ /// The maximum complexity a type can have
+ (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: u64 = 4),
+ /// Lint: BOXED_LOCAL, USELESS_VEC.
+ ///
+ /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
+ (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: u64 = 3),
+ /// Lint: LARGE_ENUM_VARIANT.
+ ///
+ /// The maximum size of an enum's variant to avoid box suggestion
+ (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: u64 = 1),
+ /// Lint: DECIMAL_LITERAL_REPRESENTATION.
+ ///
+ /// The lower bound for linting decimal literals
+ (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: Option<u64> = None),
+ /// Lint: LARGE_TYPE_PASS_BY_MOVE.
+ ///
+ /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
+ (pass_by_value_size_limit: u64 = 256),
+ /// Lint: TOO_MANY_LINES.
+ ///
+ /// The maximum number of lines a function or method can have
+ (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: 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: u64 = 4096),
+ /// Lint: TYPE_REPETITION_IN_BOUNDS.
+ ///
+ /// The maximum number of bounds a trait can have to be linted
+ (max_trait_bounds: u64 = 3),
+ /// Lint: STRUCT_EXCESSIVE_BOOLS.
+ ///
+ /// The maximum number of bool fields a struct can have
+ (max_struct_bools: u64 = 3),
+ /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
+ ///
+ /// The maximum number of bool parameters a function can have
+ (max_fn_params_bools: u64 = 3),
+ /// Lint: WILDCARD_IMPORTS.
+ ///
+ /// Whether to allow certain wildcard imports (prelude, super in tests).
+ (warn_on_all_wildcard_imports: bool = false),
+ /// Lint: DISALLOWED_METHOD.
+ ///
+ /// The list of disallowed methods, written as fully qualified paths.
+ (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
+ /// Lint: DISALLOWED_TYPE.
+ ///
+ /// The list of disallowed types, written as fully qualified paths.
+ (disallowed_types: Vec<String> = Vec::new()),
+ /// Lint: UNREADABLE_LITERAL.
+ ///
+ /// Should the fraction of a decimal be linted to include separators.
+ (unreadable_literal_lint_fractions: bool = true),
+ /// Lint: UPPER_CASE_ACRONYMS.
+ ///
+ /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
+ (upper_case_acronyms_aggressive: bool = false),
+ /// Lint: _CARGO_COMMON_METADATA.
+ ///
+ /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
+ (cargo_ignore_publish: bool = false),
+ /// Lint: NONSTANDARD_MACRO_BRACES.
+ ///
+ /// Enforce the named macros always use the braces specified.
+ ///
+ /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
+ /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
+ /// `crate_name::macro_name` and one with just the macro name.
+ (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
+ /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
+ ///
+ /// The list of imports to always rename, a fully qualified path followed by the rename.
+ (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
+ /// Lint: RESTRICTED_SCRIPTS.
+ ///
+ /// The list of unicode scripts allowed to be used in the scope.
+ (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
++ /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
++ ///
++ /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
++ (enable_raw_pointer_heuristic_for_send: bool = true),
+}
+
+/// 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 {
+ if let Ok(config_file) = current.join(config_file_name).canonicalize() {
+ match fs::metadata(&config_file) {
+ Err(e) if e.kind() == io::ErrorKind::NotFound => {},
+ Err(e) => return Err(e),
+ Ok(md) if md.is_dir() => {},
+ Ok(_) => return Ok(Some(config_file)),
+ }
+ }
+ }
+
+ // If the current directory has no parent, we're done searching.
+ if !current.pop() {
+ return Ok(None);
+ }
+ }
+}
+
+/// Read the `toml` configuration file.
+///
+/// In case of error, the function tries to continue as much as possible.
+pub fn read(path: &Path) -> TryConf {
+ let content = match fs::read_to_string(path) {
+ Err(e) => return TryConf::from_error(e),
+ Ok(content) => content,
+ };
+ toml::from_str(&content).unwrap_or_else(TryConf::from_error)
+}
--- /dev/null
- use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
+use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::match_type;
+use clippy_utils::{
+ is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
+ paths, SpanlessEq,
+};
+use if_chain::if_chain;
- BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
- TyKind, UnOp,
++use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
+use rustc_ast::visit::FnKind;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::hir_id::CRATE_HIR_ID;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::{
- fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
++ BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
++ UnOp,
+};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::mir::interpret::ConstValue;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Spanned;
+use rustc_span::symbol::{Symbol, SymbolStr};
+use rustc_span::{BytePos, Span};
+use rustc_typeck::hir_ty_to_ty;
+
+use std::borrow::{Borrow, Cow};
+
+#[cfg(feature = "metadata-collector-lint")]
+pub mod metadata_collector;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for various things we like to keep tidy in clippy.
+ ///
+ /// ### Why is this bad?
+ /// We like to pretend we're an example of tidy code.
+ ///
+ /// ### Example
+ /// Wrong ordering of the util::paths constants.
+ pub CLIPPY_LINTS_INTERNAL,
+ internal,
+ "various things that will negatively affect your clippy experience"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Ensures every lint is associated to a `LintPass`.
+ ///
+ /// ### Why is this bad?
+ /// The compiler only knows lints via a `LintPass`. Without
+ /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
+ /// know the name of the lint.
+ ///
+ /// ### Known problems
+ /// Only checks for lints associated using the
+ /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// declare_lint! { pub LINT_1, ... }
+ /// declare_lint! { pub LINT_2, ... }
+ /// declare_lint! { pub FORGOTTEN_LINT, ... }
+ /// // ...
+ /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
+ /// // missing FORGOTTEN_LINT
+ /// ```
+ pub LINT_WITHOUT_LINT_PASS,
+ internal,
+ "declaring a lint without associating it in a LintPass"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
+ /// variant of the function.
+ ///
+ /// ### Why is this bad?
+ /// The `utils::*` variants also add a link to the Clippy documentation to the
+ /// warning/error messages.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// cx.span_lint(LINT_NAME, "message");
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// utils::span_lint(cx, LINT_NAME, "message");
+ /// ```
+ pub COMPILER_LINT_FUNCTIONS,
+ internal,
+ "usage of the lint functions of the compiler instead of the utils::* variant"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `cx.outer().expn_data()` and suggests to use
+ /// the `cx.outer_expn_data()`
+ ///
+ /// ### Why is this bad?
+ /// `cx.outer_expn_data()` is faster and more concise.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// expr.span.ctxt().outer().expn_data()
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// expr.span.ctxt().outer_expn_data()
+ /// ```
+ pub OUTER_EXPN_EXPN_DATA,
+ internal,
+ "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Not an actual lint. This lint is only meant for testing our customized internal compiler
+ /// error message by calling `panic`.
+ ///
+ /// ### Why is this bad?
+ /// ICE in large quantities can damage your teeth
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// 🍦🍦🍦🍦🍦
+ /// ```
+ pub PRODUCE_ICE,
+ internal,
+ "this message should not appear anywhere as we ICE before and don't emit the lint"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for cases of an auto-generated lint without an updated description,
+ /// i.e. `default lint description`.
+ ///
+ /// ### Why is this bad?
+ /// Indicates that the lint is not finished.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
+ /// ```
+ pub DEFAULT_LINT,
+ internal,
+ "found 'default lint description' in a lint declaration"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Lints `span_lint_and_then` function calls, where the
+ /// closure argument has only one statement and that statement is a method
+ /// call to `span_suggestion`, `span_help`, `span_note` (using the same
+ /// span), `help` or `note`.
+ ///
+ /// These usages of `span_lint_and_then` should be replaced with one of the
+ /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
+ /// `span_lint_and_note`.
+ ///
+ /// ### Why is this bad?
+ /// Using the wrapper `span_lint_and_*` functions, is more
+ /// convenient, readable and less error prone.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
+ /// diag.span_suggestion(
+ /// expr.span,
+ /// help_msg,
+ /// sugg.to_string(),
+ /// Applicability::MachineApplicable,
+ /// );
+ /// });
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
+ /// diag.span_help(expr.span, help_msg);
+ /// });
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
+ /// diag.help(help_msg);
+ /// });
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
+ /// diag.span_note(expr.span, note_msg);
+ /// });
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
+ /// diag.note(note_msg);
+ /// });
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// span_lint_and_sugg(
+ /// cx,
+ /// TEST_LINT,
+ /// expr.span,
+ /// lint_msg,
+ /// help_msg,
+ /// sugg.to_string(),
+ /// Applicability::MachineApplicable,
+ /// );
+ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
+ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
+ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
+ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
+ /// ```
+ pub COLLAPSIBLE_SPAN_LINT_CALLS,
+ internal,
+ "found collapsible `span_lint_and_then` calls"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `utils::match_type()` on a type diagnostic item
+ /// and suggests to use `utils::is_type_diagnostic_item()` instead.
+ ///
+ /// ### Why is this bad?
+ /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// utils::match_type(cx, ty, &paths::VEC)
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
+ /// ```
+ pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
+ internal,
+ "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks the paths module for invalid paths.
+ ///
+ /// ### Why is this bad?
+ /// It indicates a bug in the code.
+ ///
+ /// ### Example
+ /// None.
+ pub INVALID_PATHS,
+ internal,
+ "invalid path"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for interning symbols that have already been pre-interned and defined as constants.
+ ///
+ /// ### Why is this bad?
+ /// It's faster and easier to use the symbol constant.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// let _ = sym!(f32);
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// let _ = sym::f32;
+ /// ```
+ pub INTERNING_DEFINED_SYMBOL,
+ internal,
+ "interning a symbol that is pre-interned and defined as a constant"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for unnecessary conversion from Symbol to a string.
+ ///
+ /// ### Why is this bad?
+ /// It's faster use symbols directly intead of strings.
+ ///
+ /// ### Example
+ /// Bad:
+ /// ```rust,ignore
+ /// symbol.as_str() == "clippy";
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// symbol == sym::clippy;
+ /// ```
+ pub UNNECESSARY_SYMBOL_STR,
+ internal,
+ "unnecessary conversion between Symbol and string"
+}
+
+declare_clippy_lint! {
+ /// Finds unidiomatic usage of `if_chain!`
+ pub IF_CHAIN_STYLE,
+ internal,
+ "non-idiomatic `if_chain!` usage"
+}
+
+declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
+
+impl EarlyLintPass for ClippyLintsInternal {
- fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
++ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
+ if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
+ if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
+ if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
+ if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
+ let mut last_name: Option<SymbolStr> = None;
+ for item in items {
+ let name = item.ident.as_str();
+ if let Some(ref last_name) = last_name {
+ if **last_name > *name {
+ span_lint(
+ cx,
+ CLIPPY_LINTS_INTERNAL,
+ item.span,
+ "this constant should be before the previous constant due to lexical \
+ ordering",
+ );
+ }
+ }
+ last_name = Some(name);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct LintWithoutLintPass {
+ declared_lints: FxHashMap<Symbol, Span>,
+ registered_lints: FxHashSet<Symbol>,
+}
+
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
+
+impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
+ return;
+ }
+
+ if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
+ if is_lint_ref_type(cx, ty) {
+ let expr = &cx.tcx.hir().body(body_id).value;
+ if_chain! {
+ if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
+ if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
+ let field = fields
+ .iter()
+ .find(|f| f.ident.as_str() == "desc")
+ .expect("lints must have a description field");
+ if let ExprKind::Lit(Spanned {
+ node: LitKind::Str(ref sym, _),
+ ..
+ }) = field.expr.kind;
+ if sym.as_str() == "default lint description";
+
+ then {
+ span_lint(
+ cx,
+ DEFAULT_LINT,
+ item.span,
+ &format!("the lint `{}` has the default lint description", item.ident.name),
+ );
+ }
+ }
+ self.declared_lints.insert(item.ident.name, item.span);
+ }
+ } else if is_expn_of(item.span, "impl_lint_pass").is_some()
+ || is_expn_of(item.span, "declare_lint_pass").is_some()
+ {
+ if let hir::ItemKind::Impl(hir::Impl {
+ of_trait: None,
+ items: impl_item_refs,
+ ..
+ }) = item.kind
+ {
+ let mut collector = LintCollector {
+ output: &mut self.registered_lints,
+ cx,
+ };
+ let body_id = cx.tcx.hir().body_owned_by(
+ impl_item_refs
+ .iter()
+ .find(|iiref| iiref.ident.as_str() == "get_lints")
+ .expect("LintPass needs to implement get_lints")
+ .id
+ .hir_id(),
+ );
+ collector.visit_expr(&cx.tcx.hir().body(body_id).value);
+ }
+ }
+ }
+
- fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
++ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+ if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
+ return;
+ }
+
+ for (lint_name, &lint_span) in &self.declared_lints {
+ // When using the `declare_tool_lint!` macro, the original `lint_span`'s
+ // file points to "<rustc macros>".
+ // `compiletest-rs` thinks that's an error in a different file and
+ // just ignores it. This causes the test in compile-fail/lint_pass
+ // not able to capture the error.
+ // Therefore, we need to climb the macro expansion tree and find the
+ // actual span that invoked `declare_tool_lint!`:
+ let lint_span = lint_span.ctxt().outer_expn_data().call_site;
+
+ if !self.registered_lints.contains(lint_name) {
+ span_lint(
+ cx,
+ LINT_WITHOUT_LINT_PASS,
+ lint_span,
+ &format!("the lint `{}` is not added to any `LintPass`", lint_name),
+ );
+ }
+ }
+ }
+}
+
+fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
+ if let TyKind::Rptr(
+ _,
+ MutTy {
+ ty: inner,
+ mutbl: Mutability::Not,
+ },
+ ) = ty.kind
+ {
+ if let TyKind::Path(ref path) = inner.kind {
+ if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
+ return match_def_path(cx, def_id, &paths::LINT);
+ }
+ }
+ }
+
+ false
+}
+
+struct LintCollector<'a, 'tcx> {
+ output: &'a mut FxHashSet<Symbol>,
+ cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
+ if path.segments.len() == 1 {
+ self.output.insert(path.segments[0].ident.name);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::All(self.cx.tcx.hir())
+ }
+}
+
+#[derive(Clone, Default)]
+pub struct CompilerLintFunctions {
+ map: FxHashMap<&'static str, &'static str>,
+}
+
+impl CompilerLintFunctions {
+ #[must_use]
+ pub fn new() -> Self {
+ let mut map = FxHashMap::default();
+ map.insert("span_lint", "utils::span_lint");
+ map.insert("struct_span_lint", "utils::span_lint");
+ map.insert("lint", "utils::span_lint");
+ map.insert("span_lint_note", "utils::span_lint_and_note");
+ map.insert("span_lint_help", "utils::span_lint_and_help");
+ Self { map }
+ }
+}
+
+impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
+
+impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
+ return;
+ }
+
+ if_chain! {
+ if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
+ let fn_name = path.ident;
+ if let Some(sugg) = self.map.get(&*fn_name.as_str());
+ let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+ if match_type(cx, ty, &paths::EARLY_CONTEXT)
+ || match_type(cx, ty, &paths::LATE_CONTEXT);
+ then {
+ span_lint_and_help(
+ cx,
+ COMPILER_LINT_FUNCTIONS,
+ path.ident.span,
+ "usage of a compiler lint function",
+ None,
+ &format!("please use the Clippy variant of this function: `{}`", sugg),
+ );
+ }
+ }
+ }
+}
+
+declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
+
+impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
+ return;
+ }
+
+ let (method_names, arg_lists, spans) = method_calls(expr, 2);
+ let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
+ let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
+ if_chain! {
+ if let ["expn_data", "outer_expn"] = method_names.as_slice();
+ let args = arg_lists[1];
+ if args.len() == 1;
+ let self_arg = &args[0];
+ let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+ if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
+ then {
+ span_lint_and_sugg(
+ cx,
+ OUTER_EXPN_EXPN_DATA,
+ spans[1].with_hi(expr.span.hi()),
+ "usage of `outer_expn().expn_data()`",
+ "try",
+ "outer_expn_data()".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
+
+declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
+
+impl EarlyLintPass for ProduceIce {
+ fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
+ assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
+ }
+}
+
+fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
+ match fn_kind {
+ FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
+ FnKind::Closure(..) => false,
+ }
+}
+
+declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
+
+impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
+ return;
+ }
+
+ if_chain! {
+ if let ExprKind::Call(func, and_then_args) = expr.kind;
+ if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
+ if and_then_args.len() == 5;
+ if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
+ let body = cx.tcx.hir().body(*body_id);
+ if let ExprKind::Block(block, _) = &body.value.kind;
+ let stmts = &block.stmts;
+ if stmts.len() == 1 && block.expr.is_none();
+ if let StmtKind::Semi(only_expr) = &stmts[0].kind;
+ if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind;
+ then {
+ let and_then_snippets = get_and_then_snippets(cx, and_then_args);
+ let mut sle = SpanlessEq::new(cx).deny_side_effects();
+ match &*ps.ident.as_str() {
+ "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
+ suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
+ },
+ "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
+ let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
+ suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
+ },
+ "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
+ let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
+ suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
+ },
+ "help" => {
+ let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
+ suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
+ }
+ "note" => {
+ let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
+ suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
+ }
+ _ => (),
+ }
+ }
+ }
+ }
+}
+
+struct AndThenSnippets<'a> {
+ cx: Cow<'a, str>,
+ lint: Cow<'a, str>,
+ span: Cow<'a, str>,
+ msg: Cow<'a, str>,
+}
+
+fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
+ let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
+ let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
+ let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
+ let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
+
+ AndThenSnippets {
+ cx: cx_snippet,
+ lint: lint_snippet,
+ span: span_snippet,
+ msg: msg_snippet,
+ }
+}
+
+struct SpanSuggestionSnippets<'a> {
+ help: Cow<'a, str>,
+ sugg: Cow<'a, str>,
+ applicability: Cow<'a, str>,
+}
+
+fn span_suggestion_snippets<'a, 'hir>(
+ cx: &LateContext<'_>,
+ span_call_args: &'hir [Expr<'hir>],
+) -> SpanSuggestionSnippets<'a> {
+ let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
+ let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
+ let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
+
+ SpanSuggestionSnippets {
+ help: help_snippet,
+ sugg: sugg_snippet,
+ applicability: applicability_snippet,
+ }
+}
+
+fn suggest_suggestion(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ and_then_snippets: &AndThenSnippets<'_>,
+ span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
+) {
+ span_lint_and_sugg(
+ cx,
+ COLLAPSIBLE_SPAN_LINT_CALLS,
+ expr.span,
+ "this call is collapsible",
+ "collapse into",
+ format!(
+ "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
+ and_then_snippets.cx,
+ and_then_snippets.lint,
+ and_then_snippets.span,
+ and_then_snippets.msg,
+ span_suggestion_snippets.help,
+ span_suggestion_snippets.sugg,
+ span_suggestion_snippets.applicability
+ ),
+ Applicability::MachineApplicable,
+ );
+}
+
+fn suggest_help(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ and_then_snippets: &AndThenSnippets<'_>,
+ help: &str,
+ with_span: bool,
+) {
+ let option_span = if with_span {
+ format!("Some({})", and_then_snippets.span)
+ } else {
+ "None".to_string()
+ };
+
+ span_lint_and_sugg(
+ cx,
+ COLLAPSIBLE_SPAN_LINT_CALLS,
+ expr.span,
+ "this call is collapsible",
+ "collapse into",
+ format!(
+ "span_lint_and_help({}, {}, {}, {}, {}, {})",
+ and_then_snippets.cx,
+ and_then_snippets.lint,
+ and_then_snippets.span,
+ and_then_snippets.msg,
+ &option_span,
+ help
+ ),
+ Applicability::MachineApplicable,
+ );
+}
+
+fn suggest_note(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ and_then_snippets: &AndThenSnippets<'_>,
+ note: &str,
+ with_span: bool,
+) {
+ let note_span = if with_span {
+ format!("Some({})", and_then_snippets.span)
+ } else {
+ "None".to_string()
+ };
+
+ span_lint_and_sugg(
+ cx,
+ COLLAPSIBLE_SPAN_LINT_CALLS,
+ expr.span,
+ "this call is collspible",
+ "collapse into",
+ format!(
+ "span_lint_and_note({}, {}, {}, {}, {}, {})",
+ and_then_snippets.cx,
+ and_then_snippets.lint,
+ and_then_snippets.span,
+ and_then_snippets.msg,
+ note_span,
+ note
+ ),
+ Applicability::MachineApplicable,
+ );
+}
+
+declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
+
+impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
+ return;
+ }
+
+ if_chain! {
+ // Check if this is a call to utils::match_type()
+ if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
+ if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
+ // Extract the path to the matched type
+ if let Some(segments) = path_to_matched_type(cx, ty_path);
+ let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
+ if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id();
+ // Check if the matched type is a diagnostic item
+ let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
+ if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
+ then {
+ // TODO: check paths constants from external crates.
+ let cx_snippet = snippet(cx, context.span, "_");
+ let ty_snippet = snippet(cx, ty.span, "_");
+
+ span_lint_and_sugg(
+ cx,
+ MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
+ expr.span,
+ "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
+ "try",
+ format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+}
+
+fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
+ use rustc_hir::ItemKind;
+
+ match &expr.kind {
+ ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
+ ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
+ Res::Local(hir_id) => {
+ let parent_id = cx.tcx.hir().get_parent_node(hir_id);
+ if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
+ if let Some(init) = local.init {
+ return path_to_matched_type(cx, init);
+ }
+ }
+ },
+ Res::Def(DefKind::Const | DefKind::Static, def_id) => {
+ if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
+ if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
+ let body = cx.tcx.hir().body(body_id);
+ return path_to_matched_type(cx, &body.value);
+ }
+ }
+ },
+ _ => {},
+ },
+ ExprKind::Array(exprs) => {
+ let segments: Vec<SymbolStr> = exprs
+ .iter()
+ .filter_map(|expr| {
+ if let ExprKind::Lit(lit) = &expr.kind {
+ if let LitKind::Str(sym, _) = lit.node {
+ return Some(sym.as_str());
+ }
+ }
+
+ None
+ })
+ .collect();
+
+ if segments.len() == exprs.len() {
+ return Some(segments);
+ }
+ },
+ _ => {},
+ }
+
+ None
+}
+
+// This is not a complete resolver for paths. It works on all the paths currently used in the paths
+// module. That's all it does and all it needs to do.
+pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
+ if path_to_res(cx, path) != Res::Err {
+ return true;
+ }
+
+ // Some implementations can't be found by `path_to_res`, particularly inherent
+ // implementations of native types. Check lang items.
+ let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
+ let lang_items = cx.tcx.lang_items();
+ for item_def_id in lang_items.items().iter().flatten() {
+ let lang_item_path = cx.get_def_path(*item_def_id);
+ if path_syms.starts_with(&lang_item_path) {
+ if let [item] = &path_syms[lang_item_path.len()..] {
+ for child in cx.tcx.item_children(*item_def_id) {
+ if child.ident.name == *item {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ false
+}
+
+declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ let local_def_id = &cx.tcx.parent_module(item.hir_id());
+ let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
+ if_chain! {
+ if mod_name.as_str() == "paths";
+ if let hir::ItemKind::Const(ty, body_id) = item.kind;
+ let ty = hir_ty_to_ty(cx.tcx, ty);
+ if let ty::Array(el_ty, _) = &ty.kind();
+ if let ty::Ref(_, el_ty, _) = &el_ty.kind();
+ if el_ty.is_str();
+ let body = cx.tcx.hir().body(body_id);
+ let typeck_results = cx.tcx.typeck_body(body_id);
+ if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
+ let path: Vec<&str> = path.iter().map(|x| {
+ if let Constant::Str(s) = x {
+ s.as_str()
+ } else {
+ // We checked the type of the constant above
+ unreachable!()
+ }
+ }).collect();
+ if !check_path(cx, &path[..]);
+ then {
+ span_lint(cx, INVALID_PATHS, item.span, "invalid path");
+ }
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct InterningDefinedSymbol {
+ // Maps the symbol value to the constant DefId.
+ symbol_map: FxHashMap<u32, DefId>,
+}
+
+impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
+
+impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
++ fn check_crate(&mut self, cx: &LateContext<'_>) {
+ if !self.symbol_map.is_empty() {
+ return;
+ }
+
+ for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
+ if let Some(def_id) = path_to_res(cx, module).opt_def_id() {
+ for item in cx.tcx.item_children(def_id).iter() {
+ if_chain! {
+ if let Res::Def(DefKind::Const, item_def_id) = item.res;
+ let ty = cx.tcx.type_of(item_def_id);
+ if match_type(cx, ty, &paths::SYMBOL);
+ if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
+ if let Ok(value) = value.to_u32();
+ then {
+ self.symbol_map.insert(value, item_def_id);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Call(func, [arg]) = &expr.kind;
+ if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
+ if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
+ if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
+ let value = Symbol::intern(&arg).as_u32();
+ if let Some(&def_id) = self.symbol_map.get(&value);
+ then {
+ span_lint_and_sugg(
+ cx,
+ INTERNING_DEFINED_SYMBOL,
+ is_expn_of(expr.span, "sym").unwrap_or(expr.span),
+ "interning a defined symbol",
+ "try",
+ cx.tcx.def_path_str(def_id),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ if let ExprKind::Binary(op, left, right) = expr.kind {
+ if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
+ let data = [
+ (left, self.symbol_str_expr(left, cx)),
+ (right, self.symbol_str_expr(right, cx)),
+ ];
+ match data {
+ // both operands are a symbol string
+ [(_, Some(left)), (_, Some(right))] => {
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_SYMBOL_STR,
+ expr.span,
+ "unnecessary `Symbol` to string conversion",
+ "try",
+ format!(
+ "{} {} {}",
+ left.as_symbol_snippet(cx),
+ op.node.as_str(),
+ right.as_symbol_snippet(cx),
+ ),
+ Applicability::MachineApplicable,
+ );
+ },
+ // one of the operands is a symbol string
+ [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
+ // creating an owned string for comparison
+ if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_SYMBOL_STR,
+ expr.span,
+ "unnecessary string allocation",
+ "try",
+ format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ // nothing found
+ [(_, None), (_, None)] => {},
+ }
+ }
+ }
+ }
+}
+
+impl InterningDefinedSymbol {
+ fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
+ static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
+ static SYMBOL_STR_PATHS: &[&[&str]] = &[
+ &paths::SYMBOL_AS_STR,
+ &paths::SYMBOL_TO_IDENT_STRING,
+ &paths::TO_STRING_METHOD,
+ ];
+ // SymbolStr might be de-referenced: `&*symbol.as_str()`
+ let call = if_chain! {
+ if let ExprKind::AddrOf(_, _, e) = expr.kind;
+ if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
+ then { e } else { expr }
+ };
+ if_chain! {
+ // is a method call
+ if let ExprKind::MethodCall(_, _, [item], _) = call.kind;
+ if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
+ let ty = cx.typeck_results().expr_ty(item);
+ // ...on either an Ident or a Symbol
+ if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
+ Some(false)
+ } else if match_type(cx, ty, &paths::IDENT) {
+ Some(true)
+ } else {
+ None
+ };
+ // ...which converts it to a string
+ let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
+ if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
+ then {
+ let is_to_owned = path.last().unwrap().ends_with("string");
+ return Some(SymbolStrExpr::Expr {
+ item,
+ is_ident,
+ is_to_owned,
+ });
+ }
+ }
+ // is a string constant
+ if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
+ let value = Symbol::intern(&s).as_u32();
+ // ...which matches a symbol constant
+ if let Some(&def_id) = self.symbol_map.get(&value) {
+ return Some(SymbolStrExpr::Const(def_id));
+ }
+ }
+ None
+ }
+}
+
+enum SymbolStrExpr<'tcx> {
+ /// a string constant with a corresponding symbol constant
+ Const(DefId),
+ /// a "symbol to string" expression like `symbol.as_str()`
+ Expr {
+ /// part that evaluates to `Symbol` or `Ident`
+ item: &'tcx Expr<'tcx>,
+ is_ident: bool,
+ /// whether an owned `String` is created like `to_ident_string()`
+ is_to_owned: bool,
+ },
+}
+
+impl<'tcx> SymbolStrExpr<'tcx> {
+ /// Returns a snippet that evaluates to a `Symbol` and is const if possible
+ fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
+ match *self {
+ Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
+ Self::Expr { item, is_ident, .. } => {
+ let mut snip = snippet(cx, item.span.source_callsite(), "..");
+ if is_ident {
+ // get `Ident.name`
+ snip.to_mut().push_str(".name");
+ }
+ snip
+ },
+ }
+ }
+}
+
+declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
+
+impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
+ let (local, after, if_chain_span) = if_chain! {
+ if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
+ if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
+ then { (local, after, if_chain_span) } else { return }
+ };
+ if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
+ span_lint(
+ cx,
+ IF_CHAIN_STYLE,
+ if_chain_local_span(cx, local, if_chain_span),
+ "`let` expression should be above the `if_chain!`",
+ );
+ } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
+ span_lint(
+ cx,
+ IF_CHAIN_STYLE,
+ if_chain_local_span(cx, local, if_chain_span),
+ "`let` expression should be inside `then { .. }`",
+ );
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
+ (cond, then, r#else.is_some())
+ } else {
+ return;
+ };
+ let then_block = match then.kind {
+ ExprKind::Block(block, _) => block,
+ _ => return,
+ };
+ let if_chain_span = is_expn_of(expr.span, "if_chain");
+ if !els {
+ check_nested_if_chains(cx, expr, then_block, if_chain_span);
+ }
+ let if_chain_span = match if_chain_span {
+ None => return,
+ Some(span) => span,
+ };
+ // check for `if a && b;`
+ if_chain! {
+ if let ExprKind::Binary(op, _, _) = cond.kind;
+ if op.node == BinOpKind::And;
+ if cx.sess().source_map().is_multiline(cond.span);
+ then {
+ span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
+ }
+ }
+ if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
+ && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
+ {
+ span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
+ }
+ }
+}
+
+fn check_nested_if_chains(
+ cx: &LateContext<'_>,
+ if_expr: &Expr<'_>,
+ then_block: &Block<'_>,
+ if_chain_span: Option<Span>,
+) {
+ #[rustfmt::skip]
+ let (head, tail) = match *then_block {
+ Block { stmts, expr: Some(tail), .. } => (stmts, tail),
+ Block {
+ stmts: &[
+ ref head @ ..,
+ Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
+ ],
+ ..
+ } => (head, tail),
+ _ => return,
+ };
+ if_chain! {
+ if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
+ let sm = cx.sess().source_map();
+ if head
+ .iter()
+ .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
+ if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
+ then {} else { return }
+ }
+ let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
+ (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
+ (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
+ (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
+ _ => return,
+ };
+ span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
+ let (span, msg) = match head {
+ [] => return,
+ [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
+ [a, .., b] => (
+ a.span.to(b.span),
+ "these `let` statements can also be in the `if_chain!`",
+ ),
+ };
+ diag.span_help(span, msg);
+ });
+}
+
+fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
+ cx.tcx
+ .hir()
+ .parent_iter(hir_id)
+ .find(|(_, node)| {
+ #[rustfmt::skip]
+ !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
+ })
+ .map_or(false, |(id, _)| {
+ is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
+ })
+}
+
+/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
+/// of the `then {..}` portion of an `if_chain!`
+fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
+ let span = if let [stmt, ..] = stmts {
+ stmt.span
+ } else if let Some(expr) = expr {
+ expr.span
+ } else {
+ // empty `then {}`
+ return true;
+ };
+ is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
+}
+
+/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
+fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
+ let mut span = local.pat.span;
+ if let Some(init) = local.init {
+ span = span.to(init.span);
+ }
+ span.adjust(if_chain_span.ctxt().outer_expn());
+ let sm = cx.sess().source_map();
+ let span = sm.span_extend_to_prev_str(span, "let", false);
+ let span = sm.span_extend_to_next_char(span, ';', false);
+ Span::new(
+ span.lo() - BytePos(3),
+ span.hi() + BytePos(1),
+ span.ctxt(),
+ span.parent(),
+ )
+}
--- /dev/null
- if let BinOpKind::Div = op.node;
+use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::diagnostics::span_lint_and_help;
+use if_chain::if_chain;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `0.0 / 0.0`.
+ ///
+ /// ### Why is this bad?
+ /// It's less readable than `f32::NAN` or `f64::NAN`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let nan = 0.0f32 / 0.0;
+ ///
+ /// // Good
+ /// let nan = f32::NAN;
+ /// ```
+ pub ZERO_DIVIDED_BY_ZERO,
+ complexity,
+ "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
+}
+
+declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]);
+
+impl<'tcx> LateLintPass<'tcx> for ZeroDiv {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // check for instances of 0.0/0.0
+ if_chain! {
+ if let ExprKind::Binary(ref op, left, right) = expr.kind;
++ if op.node == BinOpKind::Div;
+ // TODO - constant_simple does not fold many operations involving floats.
+ // That's probably fine for this lint - it's pretty unlikely that someone would
+ // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
+ if let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left);
+ if let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right);
+ if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value;
+ if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value;
+ then {
+ // since we're about to suggest a use of f32::NAN or f64::NAN,
+ // match the precision of the literals that are given.
+ let float_type = match (lhs_value, rhs_value) {
+ (Constant::F64(_), _)
+ | (_, Constant::F64(_)) => "f64",
+ _ => "f32"
+ };
+ span_lint_and_help(
+ cx,
+ ZERO_DIVIDED_BY_ZERO,
+ expr.span,
+ "constant division of `0.0` with `0.0` will always result in NaN",
+ None,
+ &format!(
+ "consider using `{}::NAN` if you would like a constant representing NaN",
+ float_type,
+ ),
+ );
+ }
+ }
+ }
+}
--- /dev/null
- #[allow(clippy::shadow_unrelated)] // false positive #6563
+#![feature(box_patterns)]
+#![feature(in_band_lifetimes)]
+#![feature(iter_zip)]
+#![feature(rustc_private)]
+#![feature(control_flow_enum)]
+#![recursion_limit = "512"]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
+// warn on the same lints as `clippy_lints`
+#![warn(trivial_casts, trivial_numeric_casts)]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![warn(rustc::internal)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate rustc_ast;
+extern crate rustc_ast_pretty;
+extern crate rustc_attr;
+extern crate rustc_data_structures;
+extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_infer;
+extern crate rustc_lexer;
+extern crate rustc_lint;
+extern crate rustc_middle;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_target;
+extern crate rustc_trait_selection;
+extern crate rustc_typeck;
+
+#[macro_use]
+pub mod sym_helper;
+
+#[allow(clippy::module_name_repetitions)]
+pub mod ast_utils;
+pub mod attrs;
+pub mod camel_case;
+pub mod comparisons;
+pub mod consts;
+pub mod diagnostics;
+pub mod eager_or_lazy;
+pub mod higher;
+mod hir_utils;
+pub mod msrvs;
+pub mod numeric_literal;
+pub mod paths;
+pub mod ptr;
+pub mod qualify_min_const_fn;
+pub mod source;
+pub mod sugg;
+pub mod ty;
+pub mod usage;
+pub mod visitors;
+
+pub use self::attrs::*;
+pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash};
+
+use std::collections::hash_map::Entry;
+use std::hash::BuildHasherDefault;
+
+use if_chain::if_chain;
+use rustc_ast::ast::{self, Attribute, LitKind};
+use rustc_data_structures::unhash::UnhashMap;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::hir_id::{HirIdMap, HirIdSet};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
+use rustc_hir::{
+ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
+ ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat,
+ PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
+};
+use rustc_lint::{LateContext, Level, Lint, LintContext};
+use rustc_middle::hir::exports::Export;
+use rustc_middle::hir::map::Map;
+use rustc_middle::hir::place::PlaceBase;
+use rustc_middle::ty as rustc_ty;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::binding::BindingMode;
+use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
+use rustc_semver::RustcVersion;
+use rustc_session::Session;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::source_map::original_sp;
+use rustc_span::sym;
+use rustc_span::symbol::{kw, Symbol};
+use rustc_span::{Span, DUMMY_SP};
+use rustc_target::abi::Integer;
+
+use crate::consts::{constant, Constant};
+use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
+
+pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
+ if let Ok(version) = RustcVersion::parse(msrv) {
+ return Some(version);
+ } else if let Some(sess) = sess {
+ if let Some(span) = span {
+ sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
+ }
+ }
+ None
+}
+
+pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
+ msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
+}
+
+#[macro_export]
+macro_rules! extract_msrv_attr {
+ (LateContext) => {
+ extract_msrv_attr!(@LateContext, ());
+ };
+ (EarlyContext) => {
+ extract_msrv_attr!(@EarlyContext);
+ };
+ (@$context:ident$(, $call:tt)?) => {
+ fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
+ use $crate::get_unique_inner_attr;
+ match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
+ Some(msrv_attr) => {
+ if let Some(msrv) = msrv_attr.value_str() {
+ self.msrv = $crate::parse_msrv(
+ &msrv.to_string(),
+ Some(cx.sess$($call)?),
+ Some(msrv_attr.span),
+ );
+ } else {
+ cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute");
+ }
+ },
+ _ => (),
+ }
+ }
+ };
+}
+
+/// 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()
+}
+
+/// If the given expression is a local binding, find the initializer expression.
+/// If that initializer expression is another local binding, find its initializer again.
+/// This process repeats as long as possible (but usually no more than once). Initializer
+/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
+/// instead.
+///
+/// Examples:
+/// ```ignore
+/// let abc = 1;
+/// // ^ output
+/// let def = abc;
+/// dbg!(def)
+/// // ^^^ input
+///
+/// // or...
+/// let abc = 1;
+/// let def = abc + 2;
+/// // ^^^^^^^ output
+/// dbg!(def)
+/// // ^^^ input
+/// ```
+pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
+ while let Some(init) = path_to_local(expr)
+ .and_then(|id| find_binding_init(cx, id))
+ .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
+ {
+ expr = init;
+ }
+ expr
+}
+
+/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
+/// By only considering immutable bindings, we guarantee that the returned expression represents the
+/// value of the binding wherever it is referenced.
+///
+/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
+/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
+/// canonical binding `HirId`.
+pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
+ let hir = cx.tcx.hir();
+ if_chain! {
+ if let Some(Node::Binding(pat)) = hir.find(hir_id);
+ if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
+ let parent = hir.get_parent_node(hir_id);
+ if let Some(Node::Local(local)) = hir.find(parent);
+ then {
+ return local.init;
+ }
+ }
+ None
+}
+
+/// 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(..) | ItemKind::Static(..),
+ ..
+ })
+ | Node::TraitItem(&TraitItem {
+ kind: TraitItemKind::Const(..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Const(..),
+ ..
+ })
+ | Node::AnonConst(_) => true,
+ Node::Item(&Item {
+ kind: ItemKind::Fn(ref sig, ..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(ref sig, _),
+ ..
+ }) => sig.header.constness == Constness::Const,
+ _ => false,
+ }
+}
+
+/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
+/// For example, use this to check whether a function call or a pattern is `Some(..)`.
+pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
+ if let QPath::Resolved(_, path) = qpath {
+ if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
+ if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
+ return cx.tcx.parent(ctor_id) == Some(item_id);
+ }
+ }
+ }
+ false
+}
+
+/// Returns `true` if this `span` was expanded by any macro.
+#[must_use]
+pub fn in_macro(span: Span) -> bool {
+ if span.from_expansion() {
+ !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
+ } else {
+ false
+ }
+}
+
+pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
+ matches!(
+ expr.kind,
+ ExprKind::Block(
+ Block {
+ stmts: [],
+ expr: None,
+ ..
+ },
+ _
+ ) | ExprKind::Tup([])
+ )
+}
+
+/// Checks if given pattern is a wildcard (`_`)
+pub fn is_wild(pat: &Pat<'_>) -> bool {
+ matches!(pat.kind, PatKind::Wild)
+}
+
+/// Checks if the first type parameter is a lang item.
+pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
+ let ty = get_qpath_generic_tys(qpath).next()?;
+
+ if let TyKind::Path(qpath) = &ty.kind {
+ cx.qpath_res(qpath, ty.hir_id)
+ .opt_def_id()
+ .map_or(false, |id| {
+ cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
+ })
+ .then(|| ty)
+ } else {
+ None
+ }
+}
+
+/// Checks if the first type parameter is a diagnostic item.
+pub fn is_ty_param_diagnostic_item(
+ cx: &LateContext<'_>,
+ qpath: &QPath<'tcx>,
+ item: Symbol,
+) -> Option<&'tcx hir::Ty<'tcx>> {
+ let ty = get_qpath_generic_tys(qpath).next()?;
+
+ if let TyKind::Path(qpath) = &ty.kind {
+ cx.qpath_res(qpath, ty.hir_id)
+ .opt_def_id()
+ .map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
+ .then(|| ty)
+ } else {
+ None
+ }
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+/// This is a deprecated function, consider using [`is_trait_method`].
+pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
+ let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
+ let trt_id = cx.tcx.trait_of_item(def_id);
+ trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
+}
+
+/// Checks if a method is defined in an impl of a diagnostic item
+pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+ if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+ return cx.tcx.is_diagnostic_item(diag_item, adt.did);
+ }
+ }
+ false
+}
+
+/// Checks if a method is in a diagnostic item trait
+pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
+ return cx.tcx.is_diagnostic_item(diag_item, trait_did);
+ }
+ false
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ cx.typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
+}
+
+/// Checks if the given expression is a path referring an item on the trait
+/// that is marked with the given diagnostic item.
+///
+/// For checking method call expressions instead of path expressions, use
+/// [`is_trait_method`].
+///
+/// For example, this can be used to find if an expression like `u64::default`
+/// refers to an item of the trait `Default`, which is associated with the
+/// `diag_item` of `sym::Default`.
+pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ if let hir::ExprKind::Path(ref qpath) = expr.kind {
+ cx.qpath_res(qpath, expr.hir_id)
+ .opt_def_id()
+ .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
+ } else {
+ false
+ }
+}
+
+pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
+ match *path {
+ QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
+ QPath::TypeRelative(_, seg) => seg,
+ QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
+ }
+}
+
+pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
+ match path {
+ QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
+ QPath::TypeRelative(_, s) => s.args,
+ QPath::LangItem(..) => None,
+ }
+}
+
+pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
+ get_qpath_generics(path)
+ .map_or([].as_ref(), |a| a.args)
+ .iter()
+ .filter_map(|a| {
+ if let hir::GenericArg::Type(ty) = a {
+ Some(ty)
+ } else {
+ None
+ }
+ })
+}
+
+pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+ match *path {
+ QPath::Resolved(_, path) => path.segments.get(0),
+ QPath::TypeRelative(_, seg) => Some(seg),
+ QPath::LangItem(..) => None,
+ }
+}
+
+/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
+/// `QPath::Resolved.1.res.opt_def_id()`.
+///
+/// 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(_, path) => match_path(path, segments),
+ QPath::TypeRelative(ty, segment) => match ty.kind {
+ TyKind::Path(ref inner_path) => {
+ if let [prefix @ .., end] = segments {
+ if match_qpath(inner_path, prefix) {
+ return segment.ident.name.as_str() == *end;
+ }
+ }
+ false
+ },
+ _ => false,
+ },
+ QPath::LangItem(..) => false,
+ }
+}
+
+/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
+pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
+ if let ExprKind::Path(p) = &expr.kind {
+ cx.qpath_res(p, expr.hir_id)
+ } else {
+ Res::Err
+ }
+}
+
+/// Resolves the path to a `DefId` and checks if it matches the given path.
+pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
+ cx.qpath_res(path, hir_id)
+ .opt_def_id()
+ .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
+///
+/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
+pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
+ expr_path_res(cx, expr)
+ .opt_def_id()
+ .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
+/// diagnostic item.
+pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ expr_path_res(cx, expr)
+ .opt_def_id()
+ .map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+}
+
+/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
+/// `QPath::Resolved.1.res.opt_def_id()`.
+///
+/// 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)
+}
+
+/// If the expression is a path to a local, returns the canonical `HirId` of the local.
+pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
+ if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
+ if let Res::Local(id) = path.res {
+ return Some(id);
+ }
+ }
+ None
+}
+
+/// Returns true if the expression is a path to a local with the specified `HirId`.
+/// Use this function to see if an expression matches a function argument or a match binding.
+pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
+ path_to_local(expr) == Some(id)
+}
+
+/// Gets the definition associated to a path.
- ExprKind::Repeat(x, _) => is_default_equivalent(cx, x),
+pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
+ macro_rules! try_res {
+ ($e:expr) => {
+ match $e {
+ Some(e) => e,
+ None => return Res::Err,
+ }
+ };
+ }
+ fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> {
+ tcx.item_children(def_id)
+ .iter()
+ .find(|item| item.ident.name.as_str() == name)
+ }
+
+ let (krate, first, path) = match *path {
+ [krate, first, ref path @ ..] => (krate, first, path),
+ [primitive] => {
+ return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
+ },
+ _ => return Res::Err,
+ };
+ let tcx = cx.tcx;
+ let crates = tcx.crates(());
+ let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
+ let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
+ let last = path
+ .iter()
+ .copied()
+ // `get_def_path` seems to generate these empty segments for extern blocks.
+ // We can just ignore them.
+ .filter(|segment| !segment.is_empty())
+ // for each segment, find the child item
+ .try_fold(first, |item, segment| {
+ let def_id = item.res.def_id();
+ if let Some(item) = item_child_by_name(tcx, def_id, segment) {
+ Some(item)
+ } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
+ // it is not a child item so check inherent impl items
+ tcx.inherent_impls(def_id)
+ .iter()
+ .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
+ } else {
+ None
+ }
+ });
+ try_res!(last).res.expect_non_local()
+}
+
+/// 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> {
+ match path_to_res(cx, path) {
+ Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
+ _ => None,
+ }
+}
+
+/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
+///
+/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
+///
+/// ```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(impl_) = &item.kind;
+ then { return impl_.of_trait.as_ref(); }
+ }
+ None
+}
+
+/// This method will return tuple of projection stack and root of the expression,
+/// used in `can_mut_borrow_both`.
+///
+/// For example, if `e` represents the `v[0].a.b[x]`
+/// this method will return a tuple, composed of a `Vec`
+/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
+/// and an `Expr` for root of them, `v`
+fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
+ let mut result = vec![];
+ let root = loop {
+ match e.kind {
+ ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
+ result.push(e);
+ e = ep;
+ },
+ _ => break e,
+ };
+ };
+ result.reverse();
+ (result, root)
+}
+
+/// Checks if two expressions can be mutably borrowed simultaneously
+/// and they aren't dependent on borrowing same thing twice
+pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
+ let (s1, r1) = projection_stack(e1);
+ let (s2, r2) = projection_stack(e2);
+ if !eq_expr_value(cx, r1, r2) {
+ return true;
+ }
+ for (x1, x2) in s1.iter().zip(s2.iter()) {
+ match (&x1.kind, &x2.kind) {
+ (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
+ if i1 != i2 {
+ return true;
+ }
+ },
+ (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
+ if !eq_expr_value(cx, i1, i2) {
+ return false;
+ }
+ },
+ _ => return false,
+ }
+ }
+ false
+}
+
+/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
+/// constructor from the std library
+fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
+ let std_types_symbols = &[
+ sym::String,
+ sym::Vec,
+ sym::VecDeque,
+ sym::LinkedList,
+ sym::HashMap,
+ sym::BTreeMap,
+ sym::HashSet,
+ sym::BTreeSet,
+ sym::BinaryHeap,
+ ];
+
+ if let QPath::TypeRelative(_, method) = path {
+ if method.ident.name == sym::new {
+ if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+ if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+ return std_types_symbols
+ .iter()
+ .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
+ }
+ }
+ }
+ }
+ false
+}
+
+/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
+/// It doesn't cover all cases, for example indirect function calls (some of std
+/// functions are supported) but it is the best we have.
+pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ match &e.kind {
+ ExprKind::Lit(lit) => match lit.node {
+ LitKind::Bool(false) | LitKind::Int(0, _) => true,
+ LitKind::Str(s, _) => s.is_empty(),
+ _ => false,
+ },
+ ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
- if let MatchSource::TryDesugar = *source {
++ ExprKind::Repeat(x, y) => if_chain! {
++ if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(y.body).value.kind;
++ if let LitKind::Int(v, _) = const_lit.node;
++ if v <= 32 && is_default_equivalent(cx, x);
++ then {
++ true
++ }
++ else {
++ false
++ }
++ },
+ ExprKind::Call(repl_func, _) => if_chain! {
+ if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
+ if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+ if is_diag_trait_item(cx, repl_def_id, sym::Default)
+ || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
+ then {
+ true
+ }
+ else {
+ false
+ }
+ },
+ ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
+ ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
+ _ => false,
+ }
+}
+
+/// Checks if the top level expression can be moved into a closure as is.
+/// Currently checks for:
+/// * Break/Continue outside the given loop HIR ids.
+/// * Yield/Return statments.
+/// * Inline assembly.
+/// * Usages of a field of a local where the type of the local can be partially moved.
+///
+/// For example, given the following function:
+///
+/// ```
+/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
+/// for item in iter {
+/// let s = item.1;
+/// if item.0 > 10 {
+/// continue;
+/// } else {
+/// s.clear();
+/// }
+/// }
+/// }
+/// ```
+///
+/// When called on the expression `item.0` this will return false unless the local `item` is in the
+/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
+/// isn't always safe to move into a closure when only a single field is needed.
+///
+/// When called on the `continue` expression this will return false unless the outer loop expression
+/// is in the `loop_ids` set.
+///
+/// Note that this check is not recursive, so passing the `if` expression will always return true
+/// even though sub-expressions might return false.
+pub fn can_move_expr_to_closure_no_visit(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ loop_ids: &[HirId],
+ ignore_locals: &HirIdSet,
+) -> bool {
+ match expr.kind {
+ ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
+ | ExprKind::Continue(Destination { target_id: Ok(id), .. })
+ if loop_ids.contains(&id) =>
+ {
+ true
+ },
+ ExprKind::Break(..)
+ | ExprKind::Continue(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Yield(..)
+ | ExprKind::InlineAsm(_)
+ | ExprKind::LlvmInlineAsm(_) => false,
+ // Accessing a field of a local value can only be done if the type isn't
+ // partially moved.
+ ExprKind::Field(
+ &Expr {
+ hir_id,
+ kind:
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ res: Res::Local(local_id),
+ ..
+ },
+ )),
+ ..
+ },
+ _,
+ ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
+ // TODO: check if the local has been partially moved. Assume it has for now.
+ false
+ },
+ _ => true,
+ }
+}
+
+/// How a local is captured by a closure
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureKind {
+ Value,
+ Ref(Mutability),
+}
+impl CaptureKind {
+ pub fn is_imm_ref(self) -> bool {
+ self == Self::Ref(Mutability::Not)
+ }
+}
+impl std::ops::BitOr for CaptureKind {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self::Output {
+ match (self, rhs) {
+ (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
+ (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
+ | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
+ (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
+ }
+ }
+}
+impl std::ops::BitOrAssign for CaptureKind {
+ fn bitor_assign(&mut self, rhs: Self) {
+ *self = *self | rhs;
+ }
+}
+
+/// Given an expression referencing a local, determines how it would be captured in a closure.
+/// Note as this will walk up to parent expressions until the capture can be determined it should
+/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
+/// function argument (other than a receiver).
+pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
+ fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
+ let mut capture = CaptureKind::Ref(Mutability::Not);
+ pat.each_binding_or_first(&mut |_, id, span, _| match cx
+ .typeck_results()
+ .extract_binding_mode(cx.sess(), id, span)
+ .unwrap()
+ {
+ BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
+ capture = CaptureKind::Value;
+ },
+ BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
+ capture = CaptureKind::Ref(Mutability::Mut);
+ },
+ _ => (),
+ });
+ capture
+ }
+
+ debug_assert!(matches!(
+ e.kind,
+ ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
+ ));
+
+ let mut child_id = e.hir_id;
+ let mut capture = CaptureKind::Value;
+ let mut capture_expr_ty = e;
+
+ for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
+ if let [Adjustment {
+ kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
+ target,
+ }, ref adjust @ ..] = *cx
+ .typeck_results()
+ .adjustments()
+ .get(child_id)
+ .map_or(&[][..], |x| &**x)
+ {
+ if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
+ *adjust.last().map_or(target, |a| a.target).kind()
+ {
+ return CaptureKind::Ref(mutability);
+ }
+ }
+
+ match parent {
+ Node::Expr(e) => match e.kind {
+ ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
+ ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
+ ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
+ return CaptureKind::Ref(Mutability::Mut);
+ },
+ ExprKind::Field(..) => {
+ if capture == CaptureKind::Value {
+ capture_expr_ty = e;
+ }
+ },
+ ExprKind::Let(pat, ..) => {
+ let mutability = match pat_capture_kind(cx, pat) {
+ CaptureKind::Value => Mutability::Not,
+ CaptureKind::Ref(m) => m,
+ };
+ return CaptureKind::Ref(mutability);
+ },
+ ExprKind::Match(_, arms, _) => {
+ let mut mutability = Mutability::Not;
+ for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
+ match capture {
+ CaptureKind::Value => break,
+ CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
+ CaptureKind::Ref(Mutability::Not) => (),
+ }
+ }
+ return CaptureKind::Ref(mutability);
+ },
+ _ => break,
+ },
+ Node::Local(l) => match pat_capture_kind(cx, l.pat) {
+ CaptureKind::Value => break,
+ capture @ CaptureKind::Ref(_) => return capture,
+ },
+ _ => break,
+ }
+
+ child_id = parent_id;
+ }
+
+ if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
+ // Copy types are never automatically captured by value.
+ CaptureKind::Ref(Mutability::Not)
+ } else {
+ capture
+ }
+}
+
+/// Checks if the expression can be moved into a closure as is. This will return a list of captures
+/// if so, otherwise, `None`.
+pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ // Stack of potential break targets contained in the expression.
+ loops: Vec<HirId>,
+ /// Local variables created in the expression. These don't need to be captured.
+ locals: HirIdSet,
+ /// Whether this expression can be turned into a closure.
+ allow_closure: bool,
+ /// Locals which need to be captured, and whether they need to be by value, reference, or
+ /// mutable reference.
+ captures: HirIdMap<CaptureKind>,
+ }
+ impl Visitor<'tcx> for V<'_, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if !self.allow_closure {
+ return;
+ }
+
+ match e.kind {
+ ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
+ if !self.locals.contains(&l) {
+ let cap = capture_local_usage(self.cx, e);
+ self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
+ }
+ },
+ ExprKind::Closure(..) => {
+ let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id).to_def_id();
+ for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
+ let local_id = match capture.place.base {
+ PlaceBase::Local(id) => id,
+ PlaceBase::Upvar(var) => var.var_path.hir_id,
+ _ => continue,
+ };
+ if !self.locals.contains(&local_id) {
+ let capture = match capture.info.capture_kind {
+ UpvarCapture::ByValue(_) => CaptureKind::Value,
+ UpvarCapture::ByRef(borrow) => match borrow.kind {
+ BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
+ BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
+ CaptureKind::Ref(Mutability::Mut)
+ },
+ },
+ };
+ self.captures
+ .entry(local_id)
+ .and_modify(|e| *e |= capture)
+ .or_insert(capture);
+ }
+ }
+ },
+ ExprKind::Loop(b, ..) => {
+ self.loops.push(e.hir_id);
+ self.visit_block(b);
+ self.loops.pop();
+ },
+ _ => {
+ self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
+ walk_expr(self, e);
+ },
+ }
+ }
+
+ fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
+ p.each_binding_or_first(&mut |_, id, _, _| {
+ self.locals.insert(id);
+ });
+ }
+ }
+
+ let mut v = V {
+ cx,
+ allow_closure: true,
+ loops: Vec::new(),
+ locals: HirIdSet::default(),
+ captures: HirIdMap::default(),
+ };
+ v.visit_expr(expr);
+ v.allow_closure.then(|| v.captures)
+}
+
+/// 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()`,
+/// `method_chain_args(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(path, _, 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(())
+ .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
+}
+
+/// Returns `true` if the expression is in the program's `#[panic_handler]`.
+pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ let parent = cx.tcx.hir().get_parent_item(e.hir_id);
+ let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
+ Some(def_id) == cx.tcx.lang_items().panic_impl()
+}
+
+/// Gets the name of the item the expression is in, if available.
+pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
+ 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,
+ }
+}
+
+pub struct ContainsName {
+ pub name: Symbol,
+ pub result: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for ContainsName {
+ type Map = Map<'tcx>;
+
+ fn visit_name(&mut self, _: Span, name: Symbol) {
+ 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: Symbol, expr: &Expr<'_>) -> bool {
+ let mut cn = ContainsName { name, result: false };
+ cn.visit_expr(expr);
+ cn.result
+}
+
+/// Returns `true` if `expr` contains a return expression
+pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
+ struct RetCallFinder {
+ found: bool,
+ }
+
+ impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
+ if self.found {
+ return;
+ }
+ if let hir::ExprKind::Ret(..) = &expr.kind {
+ self.found = true;
+ } else {
+ hir::intravisit::walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
+ hir::intravisit::NestedVisitorMap::None
+ }
+ }
+
+ let mut visitor = RetCallFinder { found: false };
+ visitor.visit_expr(expr);
+ visitor.found
+}
+
+struct FindMacroCalls<'a, 'b> {
+ names: &'a [&'b str],
+ result: Vec<Span>,
+}
+
+impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
+ self.result.push(expr.span);
+ }
+ // and check sub-expressions
+ intravisit::walk_expr(self, expr);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Finds calls of the specified macros in a function body.
+pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
+ let mut fmc = FindMacroCalls {
+ names,
+ result: Vec::new(),
+ };
+ fmc.visit_expr(&body.value);
+ fmc.result
+}
+
+/// 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.with_lo(line_start)
+}
+
+/// Gets the parent node, if any.
+pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
+ tcx.hir().parent_iter(id).next().map(|(_, node)| node)
+}
+
+/// Gets the parent expression, if any –- this is useful to constrain a lint.
+pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ get_parent_expr_for_hir(cx, e.hir_id)
+}
+
+/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
+/// constraint lints
+pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
+ match get_parent_node(cx.tcx, hir_id) {
+ Some(Node::Expr(parent)) => Some(parent),
+ _ => None,
+ }
+}
+
+pub fn get_enclosing_block<'tcx>(cx: &LateContext<'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));
+ enclosing_node.and_then(|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(block, _) => Some(block),
+ _ => None,
+ },
+ _ => None,
+ })
+}
+
+/// Gets the loop or closure enclosing the given expression, if any.
+pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
+ match node {
+ Node::Expr(
+ e
+ @
+ Expr {
+ kind: ExprKind::Loop(..) | ExprKind::Closure(..),
+ ..
+ },
+ ) => return Some(e),
+ Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
+ _ => break,
+ }
+ }
+ None
+}
+
+/// Gets the parent node if it's an impl block.
+pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
+ match tcx.hir().parent_iter(id).next() {
+ Some((
+ _,
+ Node::Item(Item {
+ kind: ItemKind::Impl(imp),
+ ..
+ }),
+ )) => Some(imp),
+ _ => None,
+ }
+}
+
+/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
+pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ let mut iter = tcx.hir().parent_iter(expr.hir_id);
+ match iter.next() {
+ Some((
+ _,
+ Node::Expr(Expr {
+ kind: ExprKind::If(_, _, Some(else_expr)),
+ ..
+ }),
+ )) => else_expr.hir_id == expr.hir_id,
+ _ => false,
+ }
+}
+
+/// 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 enclosing_body = cx.tcx.hir().local_def_id(cx.tcx.hir().enclosing_body_owner(e.hir_id));
+ if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), 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.typeck_results().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<'tcx>(cx: &LateContext<'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 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(fun, _) = expr.kind {
+ if let ExprKind::Path(ref qp) = fun.kind {
+ let res = cx.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/thir machinery
+pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
+ fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
+ matches!(
+ cx.qpath_res(qpath, id),
+ def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
+ )
+ }
+
+ fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
+ i.into_iter().any(|pat| is_refutable(cx, pat))
+ }
+
+ match pat.kind {
+ PatKind::Wild => false,
+ PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
+ PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
+ PatKind::Lit(..) | PatKind::Range(..) => true,
+ PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
+ PatKind::Or(pats) => {
+ // TODO: should be the honest check, that pats is exhaustive set
+ are_refutable(cx, pats)
+ },
+ PatKind::Tuple(pats, _) => are_refutable(cx, pats),
+ PatKind::Struct(ref qpath, fields, _) => {
+ is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
+ },
+ PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
+ PatKind::Slice(head, middle, tail) => {
+ match &cx.typeck_results().node_type(pat.hir_id).kind() {
+ rustc_ty::Slice(..) => {
+ // [..] is the only irrefutable slice pattern.
+ !head.is_empty() || middle.is_none() || !tail.is_empty()
+ },
+ rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
+ _ => {
+ // unreachable!()
+ true
+ },
+ }
+ },
+ }
+}
+
+/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
+/// the function once on the given pattern.
+pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
+ if let PatKind::Or(pats) = pat.kind {
+ pats.iter().for_each(f);
+ } else {
+ f(pat);
+ }
+}
+
+/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
+/// implementations have.
+pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
+ attrs.iter().any(|attr| attr.has_name(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(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(QPath::Resolved(None, path)) = slf.kind;
+ 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>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ if_chain! {
+ if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind;
+ if is_lang_ctor(cx, path, ResultOk);
+ if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
+ if path_to_local_id(arm.body, hir_id);
+ then {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
+ is_lang_ctor(cx, path, ResultErr)
+ } else {
+ false
+ }
+ }
+
+ if let ExprKind::Match(_, arms, ref source) = expr.kind {
+ // desugared from a `?` operator
++ if *source == MatchSource::TryDesugar {
+ return Some(expr);
+ }
+
+ if_chain! {
+ if arms.len() == 2;
+ if arms[0].guard.is_none();
+ if arms[1].guard.is_none();
+ if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
+ (is_ok(cx, &arms[1]) && is_err(cx, &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_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
+ cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
+}
+
+pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
+ while let PatKind::Ref(subpat, _) = pat.kind {
+ pat = subpat;
+ }
+ pat
+}
+
+pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
+ Integer::from_int_ty(&tcx, 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: rustc_ty::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: rustc_ty::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: rustc_ty::UintTy) -> u128 {
+ let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
+ let amt = 128 - bits;
+ (u << amt) >> amt
+}
+
+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
+}
+
+/// Matches a function call with the given path and returns the arguments.
+///
+/// Usage:
+///
+/// ```rust,ignore
+/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
+/// ```
+pub fn match_function_call<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ path: &[&str],
+) -> Option<&'tcx [Expr<'tcx>]> {
+ if_chain! {
+ if let ExprKind::Call(fun, args) = expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(fun_def_id) = cx.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 the given `DefId` matches any of the paths. Returns the index of matching path, if
+/// any.
+///
+/// Please use `match_any_diagnostic_items` if the targets are all diagnostic items.
+pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
+ let search_path = cx.get_def_path(did);
+ paths
+ .iter()
+ .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
+}
+
+/// Checks if the given `DefId` matches any of provided diagnostic items. Returns the index of
+/// matching path, if any.
+pub fn match_any_diagnostic_items(cx: &LateContext<'_>, def_id: DefId, diag_items: &[Symbol]) -> Option<usize> {
+ diag_items
+ .iter()
+ .position(|item| cx.tcx.is_diagnostic_item(*item, def_id))
+}
+
+/// Checks if the given `DefId` matches the path.
+pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
+ // We should probably move to Symbols in Clippy as well rather than interning every time.
+ let path = cx.get_def_path(did);
+ syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
+}
+
+pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Call(func, [arg]) = expr.kind {
+ expr_path_res(cx, func)
+ .opt_def_id()
+ .map_or(false, |id| match_panic_def_id(cx, id))
+ .then(|| arg)
+ } else {
+ None
+ }
+}
+
+pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
+ match_any_def_paths(
+ cx,
+ did,
+ &[
+ &paths::BEGIN_PANIC,
+ &paths::BEGIN_PANIC_FMT,
+ &paths::PANIC_ANY,
+ &paths::PANICKING_PANIC,
+ &paths::PANICKING_PANIC_FMT,
+ &paths::PANICKING_PANIC_STR,
+ ],
+ )
+ .is_some()
+}
+
+/// 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>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
+ let mut conds = Vec::new();
+ let mut blocks: Vec<&Block<'_>> = Vec::new();
+
+ while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
+ conds.push(&*cond);
+ if let ExprKind::Block(block, _) = then.kind {
+ blocks.push(block);
+ } else {
+ panic!("ExprKind::If node is not an ExprKind::Block");
+ }
+
+ if let Some(else_expr) = r#else {
+ expr = else_expr;
+ } else {
+ break;
+ }
+ }
+
+ // final `else {..}`
+ if !blocks.is_empty() {
+ if let ExprKind::Block(block, _) = expr.kind {
+ blocks.push(block);
+ }
+ }
+
+ (conds, blocks)
+}
+
+/// Checks if the given function kind is an async function.
+pub fn is_async_fn(kind: FnKind<'_>) -> bool {
+ matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
+}
+
+/// Peels away all the compiler generated code surrounding the body of an async function,
+pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Call(
+ _,
+ &[Expr {
+ kind: ExprKind::Closure(_, _, body, _, _),
+ ..
+ }],
+ ) = body.value.kind
+ {
+ if let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr:
+ Some(Expr {
+ kind: ExprKind::DropTemps(expr),
+ ..
+ }),
+ ..
+ },
+ _,
+ ) = tcx.hir().body(body).value.kind
+ {
+ return Some(expr);
+ }
+ };
+ None
+}
+
+// Finds the `#[must_use]` attribute, if any
+pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
+ attrs.iter().find(|a| a.has_name(sym::must_use))
+}
+
+// check if expr is calling method or function with #[must_use] attribute
+pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let did = match expr.kind {
+ ExprKind::Call(path, _) => if_chain! {
+ if let ExprKind::Path(ref qpath) = path.kind;
+ if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
+ then {
+ Some(did)
+ } else {
+ None
+ }
+ },
+ ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+ _ => None,
+ };
+
+ did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
+}
+
+/// Checks if an expression represents the identity function
+/// Only examines closures and `std::convert::identity`
+pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ /// Checks if a function's body represents the identity function. Looks for bodies of the form:
+ /// * `|x| x`
+ /// * `|x| return x`
+ /// * `|x| { return x }`
+ /// * `|x| { return x; }`
+ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+ let id = if_chain! {
+ if let [param] = func.params;
+ if let PatKind::Binding(_, id, _, _) = param.pat.kind;
+ then {
+ id
+ } else {
+ return false;
+ }
+ };
+
+ let mut expr = &func.value;
+ loop {
+ match expr.kind {
+ #[rustfmt::skip]
+ ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
+ | ExprKind::Ret(Some(e)) => expr = e,
+ #[rustfmt::skip]
+ ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
+ if_chain! {
+ if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
+ if let ExprKind::Ret(Some(ret_val)) = e.kind;
+ then {
+ expr = ret_val;
+ } else {
+ return false;
+ }
+ }
+ },
+ _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
+ }
+ }
+ }
+
+ match expr.kind {
+ ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
+ ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
+ _ => false,
+ }
+}
+
+/// Gets the node where an expression is either used, or it's type is unified with another branch.
+pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
+ let mut child_id = expr.hir_id;
+ let mut iter = tcx.hir().parent_iter(child_id);
+ loop {
+ match iter.next() {
+ None => break None,
+ Some((id, Node::Block(_))) => child_id = id,
+ Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
+ Some((_, Node::Expr(expr))) => match expr.kind {
+ ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
+ ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
+ ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
+ _ => break Some(Node::Expr(expr)),
+ },
+ Some((_, node)) => break Some(node),
+ }
+ }
+}
+
+/// Checks if the result of an expression is used, or it's type is unified with another branch.
+pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ !matches!(
+ get_expr_use_or_unification_node(tcx, expr),
+ None | Some(Node::Stmt(Stmt {
+ kind: StmtKind::Expr(_)
+ | StmtKind::Semi(_)
+ | StmtKind::Local(Local {
+ pat: Pat {
+ kind: PatKind::Wild,
+ ..
+ },
+ ..
+ }),
+ ..
+ }))
+ )
+}
+
+/// Checks if the expression is the final expression returned from a block.
+pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
+}
+
+pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
+ cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
+ if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
+ attr.path == 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(hir::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(cx.tcx) { Some(*p) } else { None });
+ traits::impossible_predicates(
+ cx.tcx,
+ traits::elaborate_predicates(cx.tcx, predicates)
+ .map(|o| o.predicate)
+ .collect::<Vec<_>>(),
+ )
+}
+
+/// Returns the `DefId` of the callee if the given expression is a function or method call.
+pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
+ match &expr.kind {
+ ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(qpath),
+ hir_id: path_hir_id,
+ ..
+ },
+ ..,
+ ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(),
+ _ => None,
+ }
+}
+
+/// Returns Option<String> where String is a textual representation of the type encapsulated in the
+/// slice iff the given expression is a slice of primitives (as defined in the
+/// `is_recursively_primitive_type` function) and None otherwise.
+pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
+ let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
+ let expr_kind = expr_type.kind();
+ let is_primitive = match expr_kind {
+ rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type),
+ rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
+ if let rustc_ty::Slice(element_type) = inner_ty.kind() {
+ is_recursively_primitive_type(element_type)
+ } else {
+ unreachable!()
+ }
+ },
+ _ => false,
+ };
+
+ if is_primitive {
+ // if we have wrappers like Array, Slice or Tuple, print these
+ // and get the type enclosed in the slice ref
+ match expr_type.peel_refs().walk(cx.tcx).nth(1).unwrap().expect_ty().kind() {
+ rustc_ty::Slice(..) => return Some("slice".into()),
+ rustc_ty::Array(..) => return Some("array".into()),
+ rustc_ty::Tuple(..) => return Some("tuple".into()),
+ _ => {
+ // is_recursively_primitive_type() should have taken care
+ // of the rest and we can rely on the type that is found
+ let refs_peeled = expr_type.peel_refs();
+ return Some(refs_peeled.walk(cx.tcx).last().unwrap().to_string());
+ },
+ }
+ }
+ None
+}
+
+/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
+/// `hash` must be comformed with `eq`
+pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
+where
+ Hash: Fn(&T) -> u64,
+ Eq: Fn(&T, &T) -> bool,
+{
+ match exprs {
+ [a, b] if eq(a, b) => return vec![(a, b)],
+ _ if exprs.len() <= 2 => return vec![],
+ _ => {},
+ }
+
+ let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
+
+ let mut map: UnhashMap<u64, Vec<&_>> =
+ UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
+
+ for expr in exprs {
+ match map.entry(hash(expr)) {
+ Entry::Occupied(mut o) => {
+ for o in o.get() {
+ if eq(o, expr) {
+ match_expr_list.push((o, expr));
+ }
+ }
+ o.get_mut().push(expr);
+ },
+ Entry::Vacant(v) => {
+ v.insert(vec![expr]);
+ },
+ }
+ }
+
+ match_expr_list
+}
+
+/// Peels off all references on the pattern. Returns the underlying pattern and the number of
+/// references removed.
+pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
+ fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
+ if let PatKind::Ref(pat, _) = pat.kind {
+ peel(pat, count + 1)
+ } else {
+ (pat, count)
+ }
+ }
+ peel(pat, 0)
+}
+
+/// Peels of expressions while the given closure returns `Some`.
+pub fn peel_hir_expr_while<'tcx>(
+ mut expr: &'tcx Expr<'tcx>,
+ mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
+) -> &'tcx Expr<'tcx> {
+ while let Some(e) = f(expr) {
+ expr = e;
+ }
+ expr
+}
+
+/// Peels off up to the given number of references on the expression. Returns the underlying
+/// expression and the number of references removed.
+pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
+ let mut remaining = count;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
+ remaining -= 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (e, count - remaining)
+}
+
+/// Peels off all references on the expression. Returns the underlying expression and the number of
+/// references removed.
+pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
+ let mut count = 0;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
+ count += 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (e, count)
+}
+
+/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
+/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
+pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
+ loop {
+ match expr.kind {
+ ExprKind::AddrOf(_, _, e) => expr = e,
+ ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
+ _ => break,
+ }
+ }
+ expr
+}
+
+#[macro_export]
+macro_rules! unwrap_cargo_metadata {
+ ($cx: ident, $lint: ident, $deps: expr) => {{
+ let mut command = cargo_metadata::MetadataCommand::new();
+ if !$deps {
+ command.no_deps();
+ }
+
+ match command.exec() {
+ Ok(metadata) => metadata,
+ Err(err) => {
+ span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
+ return;
+ },
+ }
+ }};
+}
+
+pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+ if_chain! {
+ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
+ if let Res::Def(_, def_id) = path.res;
+ then {
+ cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
+ } else {
+ false
+ }
+ }
+}
+
+/// Checks whether item either has `test` attribute applied, or
+/// is a module with `test` in its name.
+pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
+ if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) {
+ if tcx.has_attr(def_id.to_def_id(), sym::test) {
+ return true;
+ }
+ }
+
+ matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
+}
+
+macro_rules! op_utils {
+ ($($name:ident $assign:ident)*) => {
+ /// Binary operation traits like `LangItem::Add`
+ pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
+
+ /// Operator-Assign traits like `LangItem::AddAssign`
+ pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
+
+ /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
+ pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
+ match kind {
+ $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
+ _ => None,
+ }
+ }
+ };
+}
+
+op_utils! {
+ Add AddAssign
+ Sub SubAssign
+ Mul MulAssign
+ Div DivAssign
+ Rem RemAssign
+ BitXor BitXorAssign
+ BitAnd BitAndAssign
+ BitOr BitOrAssign
+ Shl ShlAssign
+ Shr ShrAssign
+}
--- /dev/null
- let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
+use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
+use std::iter;
+
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub enum Radix {
+ Binary,
+ Octal,
+ Decimal,
+ Hexadecimal,
+}
+
+impl Radix {
+ /// Returns a reasonable digit group size for this radix.
+ #[must_use]
+ fn suggest_grouping(self) -> usize {
+ match self {
+ Self::Binary | Self::Hexadecimal => 4,
+ Self::Octal | Self::Decimal => 3,
+ }
+ }
+}
+
+/// A helper method to format numeric literals with digit grouping.
+/// `lit` must be a valid numeric literal without suffix.
+pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
+ NumericLiteral::new(lit, type_suffix, float).format()
+}
+
+#[derive(Debug)]
+pub struct NumericLiteral<'a> {
+ /// Which radix the literal was represented in.
+ pub radix: Radix,
+ /// The radix prefix, if present.
+ pub prefix: Option<&'a str>,
+
+ /// The integer part of the number.
+ pub integer: &'a str,
+ /// The fraction part of the number.
+ pub fraction: Option<&'a str>,
+ /// The exponent separator (b'e' or b'E') including preceding underscore if present
+ /// and the exponent part.
+ pub exponent: Option<(&'a str, &'a str)>,
+
+ /// The type suffix, including preceding underscore if present.
+ pub suffix: Option<&'a str>,
+}
+
+impl<'a> NumericLiteral<'a> {
+ pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
+ NumericLiteral::from_lit_kind(src, &lit.kind)
+ }
+
+ pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
+ if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
+ let (unsuffixed, suffix) = split_suffix(src, lit_kind);
+ let float = matches!(lit_kind, LitKind::Float(..));
+ Some(NumericLiteral::new(unsuffixed, suffix, float))
+ } else {
+ None
+ }
+ }
+
+ #[must_use]
+ pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
+ // Determine delimiter for radix prefix, if present, and radix.
+ let radix = if lit.starts_with("0x") {
+ Radix::Hexadecimal
+ } else if lit.starts_with("0b") {
+ Radix::Binary
+ } else if lit.starts_with("0o") {
+ Radix::Octal
+ } else {
+ Radix::Decimal
+ };
+
+ // Grab part of the literal after prefix, if present.
- output.push_str(separator);
- Self::group_digits(&mut output, exponent, group_size, true, false);
++ let (prefix, mut sans_prefix) = if radix == Radix::Decimal {
+ (None, lit)
+ } else {
+ let (p, s) = lit.split_at(2);
+ (Some(p), s)
+ };
+
+ if suffix.is_some() && sans_prefix.ends_with('_') {
+ // The '_' before the suffix isn't part of the digits
+ sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
+ }
+
+ let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);
+
+ Self {
+ radix,
+ prefix,
+ integer,
+ fraction,
+ exponent,
+ suffix,
+ }
+ }
+
+ pub fn is_decimal(&self) -> bool {
+ self.radix == Radix::Decimal
+ }
+
+ pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
+ let mut integer = digits;
+ let mut fraction = None;
+ let mut exponent = None;
+
+ if float {
+ for (i, c) in digits.char_indices() {
+ match c {
+ '.' => {
+ integer = &digits[..i];
+ fraction = Some(&digits[i + 1..]);
+ },
+ 'e' | 'E' => {
+ let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };
+
+ if integer.len() > exp_start {
+ integer = &digits[..exp_start];
+ } else {
+ fraction = Some(&digits[integer.len() + 1..exp_start]);
+ };
+ exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
+ break;
+ },
+ _ => {},
+ }
+ }
+ }
+
+ (integer, fraction, exponent)
+ }
+
+ /// Returns literal formatted in a sensible way.
+ pub fn format(&self) -> String {
+ let mut output = String::new();
+
+ if let Some(prefix) = self.prefix {
+ output.push_str(prefix);
+ }
+
+ let group_size = self.radix.suggest_grouping();
+
+ Self::group_digits(
+ &mut output,
+ self.integer,
+ group_size,
+ true,
+ self.radix == Radix::Hexadecimal,
+ );
+
+ if let Some(fraction) = self.fraction {
+ output.push('.');
+ Self::group_digits(&mut output, fraction, group_size, false, false);
+ }
+
+ if let Some((separator, exponent)) = self.exponent {
++ if exponent != "0" {
++ output.push_str(separator);
++ Self::group_digits(&mut output, exponent, group_size, true, false);
++ }
+ }
+
+ if let Some(suffix) = self.suffix {
+ if output.ends_with('.') {
+ output.push('0');
+ }
+ output.push('_');
+ output.push_str(suffix);
+ }
+
+ output
+ }
+
+ pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
+ debug_assert!(group_size > 0);
+
+ let mut digits = input.chars().filter(|&c| c != '_');
+
++ // The exponent may have a sign, output it early, otherwise it will be
++ // treated as a digit
++ if digits.clone().next() == Some('-') {
++ let _ = digits.next();
++ output.push('-');
++ }
++
+ let first_group_size;
+
+ if partial_group_first {
+ first_group_size = (digits.clone().count() - 1) % group_size + 1;
+ if pad {
+ for _ in 0..group_size - first_group_size {
+ output.push('0');
+ }
+ }
+ } else {
+ first_group_size = group_size;
+ }
+
+ for _ in 0..first_group_size {
+ if let Some(digit) = digits.next() {
+ output.push(digit);
+ }
+ }
+
+ for (c, i) in iter::zip(digits, (0..group_size).cycle()) {
+ if i == 0 {
+ output.push('_');
+ }
+ output.push(c);
+ }
+ }
+}
+
+fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
+ debug_assert!(lit_kind.is_numeric());
+ lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
+ let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
+ (unsuffixed, Some(suffix))
+ })
+}
+
+fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
+ debug_assert!(lit_kind.is_numeric());
+ let suffix = match lit_kind {
+ LitKind::Int(_, int_lit_kind) => match int_lit_kind {
+ LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
+ LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
+ LitIntType::Unsuffixed => None,
+ },
+ LitKind::Float(_, float_lit_kind) => match float_lit_kind {
+ LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
+ LitFloatType::Unsuffixed => None,
+ },
+ _ => None,
+ };
+
+ suffix.map(str::len)
+}
--- /dev/null
+//! 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] = ["core", "any", "Any"];
+#[cfg(feature = "metadata-collector-lint")]
+pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
+#[cfg(feature = "metadata-collector-lint")]
+pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
+ ["rustc_lint_defs", "Applicability", "Unspecified"],
+ ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
+ ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
+ ["rustc_lint_defs", "Applicability", "MachineApplicable"],
+];
+#[cfg(feature = "metadata-collector-lint")]
+pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
+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(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
+pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
+/// Preferably use the diagnostic item `sym::Borrow` where possible
+pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
+pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
+pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
+pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
+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_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
+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"];
+/// Preferably use the diagnostic item `sym::deref_method` where possible
+pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
+pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
+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 DURATION: [&str; 3] = ["core", "time", "Duration"];
+#[cfg(feature = "internal-lints")]
+pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
+pub const EXIT: [&str; 3] = ["std", "process", "exit"];
+pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
+pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
+pub const FILE: [&str; 3] = ["std", "fs", "File"];
+pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
+pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
+pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
+pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
+pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
+pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
+pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
+pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
+pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
+pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
+#[cfg(feature = "internal-lints")]
+pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
+#[cfg(feature = "internal-lints")]
+pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
+pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
+pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
+pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
+pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
+pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
+pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
+pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
+pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
+#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
+#[cfg(feature = "internal-lints")]
+pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
+#[cfg(feature = "internal-lints")]
+pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
+pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
+#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
+pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
+pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
+pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
+pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"];
+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 MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"];
+pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"];
+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"];
+/// Preferably use the diagnostic item `sym::Option` where possible
+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_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(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
+pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
+pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
+pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
+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_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 PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
+pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
+pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
+pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
+pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
+pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
+pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
+pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
+pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
+pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
+pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
+pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
+pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
+pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
+pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
+pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
+pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
+pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
+pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
+pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
+pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
+pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
+pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
+pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
+pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
++#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
++#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
++#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
++#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
++#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
++#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
+/// Preferably use the diagnostic item `sym::Result` where possible
+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; 3] = ["serde", "de", "Deserialize"];
+pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
+pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
+pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
+pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
+pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
+pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
+pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
+pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
+pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
+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 STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
+pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
+pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
+pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
+#[cfg(feature = "internal-lints")]
+pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
+#[cfg(feature = "internal-lints")]
+pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
+pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
+pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
+pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
+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_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
+pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
+pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
+pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
+pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
--- /dev/null
- if let Some('(') = chars.next() {
+//! Contains utility functions to generate suggestions.
+#![deny(clippy::missing_docs_in_private_items)]
+
+use crate::higher;
+use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite};
+use rustc_ast::util::parser::AssocOp;
+use rustc_ast::{ast, token};
+use rustc_ast_pretty::pprust::token_kind_to_string;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{EarlyContext, LateContext, LintContext};
+use rustc_span::source_map::{CharPos, Span};
+use rustc_span::{BytePos, Pos, SyntaxContext};
+use std::borrow::Cow;
+use std::convert::TryInto;
+use std::fmt::Display;
+use std::ops::{Add, Neg, Not, Sub};
+
+/// A helper type to build suggestion correctly handling parenthesis.
+#[derive(Clone, PartialEq)]
+pub enum Sugg<'a> {
+ /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
+ NonParen(Cow<'a, str>),
+ /// An expression that does not fit in other variants.
+ MaybeParen(Cow<'a, str>),
+ /// A binary operator expression, including `as`-casts and explicit type
+ /// coercion.
+ BinOp(AssocOp, Cow<'a, str>),
+}
+
+/// Literal constant `0`, for convenience.
+pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
+/// Literal constant `1`, for convenience.
+pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
+/// a constant represents an empty string, for convenience.
+pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
+
+impl Display for Sugg<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ match *self {
+ Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
+ }
+ }
+}
+
+#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
+impl<'a> Sugg<'a> {
+ /// Prepare a suggestion from an expression.
+ pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
+ snippet_opt(cx, expr.span).map(|snippet| {
+ let snippet = Cow::Owned(snippet);
+ Self::hir_from_snippet(expr, snippet)
+ })
+ }
+
+ /// Convenience function around `hir_opt` for suggestions with a default
+ /// text.
+ pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
+ Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
+ }
+
+ /// Same as `hir`, 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 hir_with_applicability(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ default: &'a str,
+ applicability: &mut Applicability,
+ ) -> Self {
+ if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
+ *applicability = Applicability::MaybeIncorrect;
+ }
+ Self::hir_opt(cx, expr).unwrap_or_else(|| {
+ if *applicability == Applicability::MachineApplicable {
+ *applicability = Applicability::HasPlaceholders;
+ }
+ Sugg::NonParen(Cow::Borrowed(default))
+ })
+ }
+
+ /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
+ pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
+ let snippet = snippet_with_macro_callsite(cx, expr.span, default);
+
+ Self::hir_from_snippet(expr, snippet)
+ }
+
+ /// Same as `hir`, but first walks the span up to the given context. This will result in the
+ /// macro call, rather then the expansion, if the span is from a child context. If the span is
+ /// not from a child context, it will be used directly instead.
+ ///
+ /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR
+ /// node would result in `box []`. If given the context of the address of expression, this
+ /// function will correctly get a snippet of `vec![]`.
+ pub fn hir_with_context(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ ctxt: SyntaxContext,
+ default: &'a str,
+ applicability: &mut Applicability,
+ ) -> Self {
+ let (snippet, in_macro) = snippet_with_context(cx, expr.span, ctxt, default, applicability);
+
+ if in_macro {
+ Sugg::NonParen(snippet)
+ } else {
+ Self::hir_from_snippet(expr, snippet)
+ }
+ }
+
+ /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
+ /// function variants of `Sugg`, since these use different snippet functions.
+ fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
+ if let Some(range) = higher::Range::hir(expr) {
+ let op = match range.limits {
+ ast::RangeLimits::HalfOpen => AssocOp::DotDot,
+ ast::RangeLimits::Closed => AssocOp::DotDotEq,
+ };
+ return Sugg::BinOp(op, snippet);
+ }
+
+ match expr.kind {
+ hir::ExprKind::AddrOf(..)
+ | hir::ExprKind::Box(..)
+ | hir::ExprKind::If(..)
+ | hir::ExprKind::Let(..)
+ | hir::ExprKind::Closure(..)
+ | hir::ExprKind::Unary(..)
+ | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
+ hir::ExprKind::Continue(..)
+ | hir::ExprKind::Yield(..)
+ | hir::ExprKind::Array(..)
+ | hir::ExprKind::Block(..)
+ | hir::ExprKind::Break(..)
+ | hir::ExprKind::Call(..)
+ | hir::ExprKind::Field(..)
+ | hir::ExprKind::Index(..)
+ | hir::ExprKind::InlineAsm(..)
+ | hir::ExprKind::LlvmInlineAsm(..)
+ | hir::ExprKind::ConstBlock(..)
+ | hir::ExprKind::Lit(..)
+ | hir::ExprKind::Loop(..)
+ | hir::ExprKind::MethodCall(..)
+ | hir::ExprKind::Path(..)
+ | hir::ExprKind::Repeat(..)
+ | hir::ExprKind::Ret(..)
+ | hir::ExprKind::Struct(..)
+ | hir::ExprKind::Tup(..)
+ | hir::ExprKind::DropTemps(_)
+ | hir::ExprKind::Err => Sugg::NonParen(snippet),
+ hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+ hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
+ hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node.into()), snippet),
+ hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
+ hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+ }
+ }
+
+ /// Prepare a suggestion from an expression.
+ pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
+ use rustc_ast::ast::RangeLimits;
+
+ let snippet = if expr.span.from_expansion() {
+ snippet_with_macro_callsite(cx, expr.span, default)
+ } else {
+ snippet(cx, expr.span, default)
+ };
+
+ match expr.kind {
+ ast::ExprKind::AddrOf(..)
+ | ast::ExprKind::Box(..)
+ | ast::ExprKind::Closure(..)
+ | ast::ExprKind::If(..)
+ | ast::ExprKind::Let(..)
+ | ast::ExprKind::Unary(..)
+ | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
+ ast::ExprKind::Async(..)
+ | ast::ExprKind::Block(..)
+ | ast::ExprKind::Break(..)
+ | ast::ExprKind::Call(..)
+ | ast::ExprKind::Continue(..)
+ | ast::ExprKind::Yield(..)
+ | ast::ExprKind::Field(..)
+ | ast::ExprKind::ForLoop(..)
+ | ast::ExprKind::Index(..)
+ | ast::ExprKind::InlineAsm(..)
+ | ast::ExprKind::LlvmInlineAsm(..)
+ | ast::ExprKind::ConstBlock(..)
+ | ast::ExprKind::Lit(..)
+ | ast::ExprKind::Loop(..)
+ | ast::ExprKind::MacCall(..)
+ | ast::ExprKind::MethodCall(..)
+ | ast::ExprKind::Paren(..)
+ | ast::ExprKind::Underscore
+ | ast::ExprKind::Path(..)
+ | ast::ExprKind::Repeat(..)
+ | ast::ExprKind::Ret(..)
+ | ast::ExprKind::Struct(..)
+ | ast::ExprKind::Try(..)
+ | ast::ExprKind::TryBlock(..)
+ | ast::ExprKind::Tup(..)
+ | ast::ExprKind::Array(..)
+ | ast::ExprKind::While(..)
+ | ast::ExprKind::Await(..)
+ | ast::ExprKind::Err => Sugg::NonParen(snippet),
+ ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
+ ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet),
+ ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+ ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
+ ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
+ ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
+ ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+ }
+ }
+
+ /// Convenience method to create the `<lhs> && <rhs>` suggestion.
+ pub fn and(self, rhs: &Self) -> Sugg<'static> {
+ make_binop(ast::BinOpKind::And, &self, rhs)
+ }
+
+ /// Convenience method to create the `<lhs> & <rhs>` suggestion.
+ pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
+ make_binop(ast::BinOpKind::BitAnd, &self, rhs)
+ }
+
+ /// Convenience method to create the `<lhs> as <rhs>` suggestion.
+ pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
+ make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
+ }
+
+ /// Convenience method to create the `&<expr>` suggestion.
+ pub fn addr(self) -> Sugg<'static> {
+ make_unop("&", self)
+ }
+
+ /// Convenience method to create the `&mut <expr>` suggestion.
+ pub fn mut_addr(self) -> Sugg<'static> {
+ make_unop("&mut ", self)
+ }
+
+ /// Convenience method to create the `*<expr>` suggestion.
+ pub fn deref(self) -> Sugg<'static> {
+ make_unop("*", self)
+ }
+
+ /// Convenience method to create the `&*<expr>` suggestion. Currently this
+ /// is needed because `sugg.deref().addr()` produces an unnecessary set of
+ /// parentheses around the deref.
+ pub fn addr_deref(self) -> Sugg<'static> {
+ make_unop("&*", self)
+ }
+
+ /// Convenience method to create the `&mut *<expr>` suggestion. Currently
+ /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
+ /// set of parentheses around the deref.
+ pub fn mut_addr_deref(self) -> Sugg<'static> {
+ make_unop("&mut *", self)
+ }
+
+ /// Convenience method to transform suggestion into a return call
+ pub fn make_return(self) -> Sugg<'static> {
+ Sugg::NonParen(Cow::Owned(format!("return {}", self)))
+ }
+
+ /// Convenience method to transform suggestion into a block
+ /// where the suggestion is a trailing expression
+ pub fn blockify(self) -> Sugg<'static> {
+ Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
+ }
+
+ /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
+ /// suggestion.
+ #[allow(dead_code)]
+ pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
+ match limit {
+ ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
+ ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
+ }
+ }
+
+ /// Adds parenthesis to any expression that might need them. Suitable to the
+ /// `self` argument of a method call
+ /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
+ pub fn maybe_par(self) -> Self {
+ match self {
+ Sugg::NonParen(..) => self,
+ // `(x)` and `(x).y()` both don't need additional parens.
+ Sugg::MaybeParen(sugg) => {
+ if has_enclosing_paren(&sugg) {
+ Sugg::MaybeParen(sugg)
+ } else {
+ Sugg::NonParen(format!("({})", sugg).into())
+ }
+ },
+ Sugg::BinOp(_, sugg) => {
+ if has_enclosing_paren(&sugg) {
+ Sugg::NonParen(sugg)
+ } else {
+ Sugg::NonParen(format!("({})", sugg).into())
+ }
+ },
+ }
+ }
+}
+
+/// Return `true` if `sugg` is enclosed in parenthesis.
+fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
+ let mut chars = sugg.as_ref().chars();
++ if chars.next() == Some('(') {
+ let mut depth = 1;
+ for c in &mut chars {
+ if c == '(' {
+ depth += 1;
+ } else if c == ')' {
+ depth -= 1;
+ }
+ if depth == 0 {
+ break;
+ }
+ }
+ chars.next().is_none()
+ } else {
+ false
+ }
+}
+
+/// Copied from the rust standard library, and then edited
+macro_rules! forward_binop_impls_to_ref {
+ (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
+ impl $imp<$t> for &$t {
+ type Output = $o;
+
+ fn $method(self, other: $t) -> $o {
+ $imp::$method(self, &other)
+ }
+ }
+
+ impl $imp<&$t> for $t {
+ type Output = $o;
+
+ fn $method(self, other: &$t) -> $o {
+ $imp::$method(&self, other)
+ }
+ }
+
+ impl $imp for $t {
+ type Output = $o;
+
+ fn $method(self, other: $t) -> $o {
+ $imp::$method(&self, &other)
+ }
+ }
+ };
+}
+
+impl Add for &Sugg<'_> {
+ type Output = Sugg<'static>;
+ fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
+ make_binop(ast::BinOpKind::Add, self, rhs)
+ }
+}
+
+impl Sub for &Sugg<'_> {
+ type Output = Sugg<'static>;
+ fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
+ make_binop(ast::BinOpKind::Sub, self, rhs)
+ }
+}
+
+forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
+forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
+
+impl Neg for Sugg<'_> {
+ type Output = Sugg<'static>;
+ fn neg(self) -> Sugg<'static> {
+ make_unop("-", self)
+ }
+}
+
+impl Not for Sugg<'_> {
+ type Output = Sugg<'static>;
+ fn not(self) -> Sugg<'static> {
+ make_unop("!", self)
+ }
+}
+
+/// Helper type to display either `foo` or `(foo)`.
+struct ParenHelper<T> {
+ /// `true` if parentheses are needed.
+ paren: bool,
+ /// The main thing to display.
+ wrapped: T,
+}
+
+impl<T> ParenHelper<T> {
+ /// Builds a `ParenHelper`.
+ fn new(paren: bool, wrapped: T) -> Self {
+ Self { paren, wrapped }
+ }
+}
+
+impl<T: Display> Display for ParenHelper<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ if self.paren {
+ write!(f, "({})", self.wrapped)
+ } else {
+ self.wrapped.fmt(f)
+ }
+ }
+}
+
+/// Builds the string for `<op><expr>` adding parenthesis when necessary.
+///
+/// For convenience, the operator is taken as a string because all unary
+/// operators have the same
+/// precedence.
+pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
+ Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
+}
+
+/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
+///
+/// Precedence of shift operator relative to other arithmetic operation is
+/// often confusing so
+/// parenthesis will always be added for a mix of these.
+pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
+ /// Returns `true` if the operator is a shift operator `<<` or `>>`.
+ fn is_shift(op: AssocOp) -> bool {
+ matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
+ }
+
+ /// Returns `true` if the operator is an arithmetic operator
+ /// (i.e., `+`, `-`, `*`, `/`, `%`).
+ fn is_arith(op: AssocOp) -> bool {
+ matches!(
+ op,
+ AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
+ )
+ }
+
+ /// Returns `true` if the operator `op` needs parenthesis with the operator
+ /// `other` in the direction `dir`.
+ fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
+ other.precedence() < op.precedence()
+ || (other.precedence() == op.precedence()
+ && ((op != other && associativity(op) != dir)
+ || (op == other && associativity(op) != Associativity::Both)))
+ || is_shift(op) && is_arith(other)
+ || is_shift(other) && is_arith(op)
+ }
+
+ let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs {
+ needs_paren(op, lop, Associativity::Left)
+ } else {
+ false
+ };
+
+ let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs {
+ needs_paren(op, rop, Associativity::Right)
+ } else {
+ false
+ };
+
+ let lhs = ParenHelper::new(lhs_paren, lhs);
+ let rhs = ParenHelper::new(rhs_paren, rhs);
+ let sugg = match op {
+ AssocOp::Add
+ | AssocOp::BitAnd
+ | AssocOp::BitOr
+ | AssocOp::BitXor
+ | AssocOp::Divide
+ | AssocOp::Equal
+ | AssocOp::Greater
+ | AssocOp::GreaterEqual
+ | AssocOp::LAnd
+ | AssocOp::LOr
+ | AssocOp::Less
+ | AssocOp::LessEqual
+ | AssocOp::Modulus
+ | AssocOp::Multiply
+ | AssocOp::NotEqual
+ | AssocOp::ShiftLeft
+ | AssocOp::ShiftRight
+ | AssocOp::Subtract => format!(
+ "{} {} {}",
+ lhs,
+ op.to_ast_binop().expect("Those are AST ops").to_string(),
+ rhs
+ ),
+ AssocOp::Assign => format!("{} = {}", lhs, rhs),
+ AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs),
+ AssocOp::As => format!("{} as {}", lhs, rhs),
+ AssocOp::DotDot => format!("{}..{}", lhs, rhs),
+ AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
+ AssocOp::Colon => format!("{}: {}", lhs, rhs),
+ };
+
+ Sugg::BinOp(op, sugg.into())
+}
+
+/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
+pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
+ make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
+}
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+/// Operator associativity.
+enum Associativity {
+ /// The operator is both left-associative and right-associative.
+ Both,
+ /// The operator is left-associative.
+ Left,
+ /// The operator is not associative.
+ None,
+ /// The operator is right-associative.
+ Right,
+}
+
+/// Returns the associativity/fixity of an operator. The difference with
+/// `AssocOp::fixity` is that an operator can be both left and right associative
+/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
+///
+/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
+/// they are considered
+/// associative.
+#[must_use]
+fn associativity(op: AssocOp) -> Associativity {
+ use rustc_ast::util::parser::AssocOp::{
+ Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
+ GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
+ };
+
+ match op {
+ Assign | AssignOp(_) => Associativity::Right,
+ Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
+ Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
+ | Subtract => Associativity::Left,
+ DotDot | DotDotEq => Associativity::None,
+ }
+}
+
+/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
+fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
+ use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
+
+ AssocOp::AssignOp(match op.node {
+ hir::BinOpKind::Add => Plus,
+ hir::BinOpKind::BitAnd => And,
+ hir::BinOpKind::BitOr => Or,
+ hir::BinOpKind::BitXor => Caret,
+ hir::BinOpKind::Div => Slash,
+ hir::BinOpKind::Mul => Star,
+ hir::BinOpKind::Rem => Percent,
+ hir::BinOpKind::Shl => Shl,
+ hir::BinOpKind::Shr => Shr,
+ hir::BinOpKind::Sub => Minus,
+
+ hir::BinOpKind::And
+ | hir::BinOpKind::Eq
+ | hir::BinOpKind::Ge
+ | hir::BinOpKind::Gt
+ | hir::BinOpKind::Le
+ | hir::BinOpKind::Lt
+ | hir::BinOpKind::Ne
+ | hir::BinOpKind::Or => panic!("This operator does not exist"),
+ })
+}
+
+/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
+fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
+ use rustc_ast::ast::BinOpKind::{
+ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
+ };
+ use rustc_ast::token::BinOpToken;
+
+ AssocOp::AssignOp(match op.node {
+ Add => BinOpToken::Plus,
+ BitAnd => BinOpToken::And,
+ BitOr => BinOpToken::Or,
+ BitXor => BinOpToken::Caret,
+ Div => BinOpToken::Slash,
+ Mul => BinOpToken::Star,
+ Rem => BinOpToken::Percent,
+ Shl => BinOpToken::Shl,
+ Shr => BinOpToken::Shr,
+ Sub => BinOpToken::Minus,
+ And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
+ })
+}
+
+/// Returns the indentation before `span` if there are nothing but `[ \t]`
+/// before it on its line.
+fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+ let lo = cx.sess().source_map().lookup_char_pos(span.lo());
+ lo.file
+ .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
+ .and_then(|line| {
+ if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
+ // We can mix char and byte positions here because we only consider `[ \t]`.
+ if lo.col == CharPos(pos) {
+ Some(line[..pos].into())
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })
+}
+
+/// Convenience extension trait for `DiagnosticBuilder`.
+pub trait DiagnosticBuilderExt<T: LintContext> {
+ /// Suggests to add an attribute to an item.
+ ///
+ /// Correctly handles indentation of the attribute and item.
+ ///
+ /// # Example
+ ///
+ /// ```rust,ignore
+ /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
+ /// ```
+ fn suggest_item_with_attr<D: Display + ?Sized>(
+ &mut self,
+ cx: &T,
+ item: Span,
+ msg: &str,
+ attr: &D,
+ applicability: Applicability,
+ );
+
+ /// Suggest to add an item before another.
+ ///
+ /// The item should not be indented (except for inner indentation).
+ ///
+ /// # Example
+ ///
+ /// ```rust,ignore
+ /// diag.suggest_prepend_item(cx, item,
+ /// "fn foo() {
+ /// bar();
+ /// }");
+ /// ```
+ fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
+
+ /// Suggest to completely remove an item.
+ ///
+ /// This will remove an item and all following whitespace until the next non-whitespace
+ /// character. This should work correctly if item is on the same indentation level as the
+ /// following item.
+ ///
+ /// # Example
+ ///
+ /// ```rust,ignore
+ /// diag.suggest_remove_item(cx, item, "remove this")
+ /// ```
+ fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
+}
+
+impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'_> {
+ fn suggest_item_with_attr<D: Display + ?Sized>(
+ &mut self,
+ cx: &T,
+ item: Span,
+ msg: &str,
+ attr: &D,
+ applicability: Applicability,
+ ) {
+ if let Some(indent) = indentation(cx, item) {
+ let span = item.with_hi(item.lo());
+
+ self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
+ }
+ }
+
+ fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
+ if let Some(indent) = indentation(cx, item) {
+ let span = item.with_hi(item.lo());
+
+ let mut first = true;
+ let new_item = new_item
+ .lines()
+ .map(|l| {
+ if first {
+ first = false;
+ format!("{}\n", l)
+ } else {
+ format!("{}{}\n", indent, l)
+ }
+ })
+ .collect::<String>();
+
+ self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
+ }
+ }
+
+ fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
+ let mut remove_span = item;
+ let hi = cx.sess().source_map().next_point(remove_span).hi();
+ let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
+
+ if let Some(ref src) = fmpos.sf.src {
+ let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
+
+ if let Some(non_whitespace_offset) = non_whitespace_offset {
+ remove_span = remove_span
+ .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")));
+ }
+ }
+
+ self.span_suggestion(remove_span, msg, String::new(), applicability);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::Sugg;
+
+ use rustc_ast::util::parser::AssocOp;
+ use std::borrow::Cow;
+
+ const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()"));
+
+ #[test]
+ fn make_return_transform_sugg_into_a_return_call() {
+ assert_eq!("return function_call()", SUGGESTION.make_return().to_string());
+ }
+
+ #[test]
+ fn blockify_transforms_sugg_into_a_block() {
+ assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string());
+ }
+
+ #[test]
+ fn binop_maybe_par() {
+ let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into());
+ assert_eq!("(1 + 1)", sugg.maybe_par().to_string());
+
+ let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1) + (1 + 1)".into());
+ assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string());
+ }
+}
--- /dev/null
- /// Returns true iff the given type is a non aggregate primitive (a bool or char, any integer or
- /// floating-point number type). For checking aggregation of primitive types (e.g. tuples and slices
- /// of primitive type) see `is_recursively_primitive_type`
+//! Util methods for [`rustc_middle::ty`]
+
+#![allow(clippy::module_name_repetitions)]
+
+use rustc_ast::ast::Mutability;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{TyKind, Unsafety};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::LateContext;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_span::sym;
+use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::DUMMY_SP;
+use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::query::normalize::AtExt;
+
+use crate::{match_def_path, must_use_attr};
+
+pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
+}
+
+/// Checks whether a type can be partially moved.
+pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ if has_drop(cx, ty) || is_copy(cx, ty) {
+ return false;
+ }
+ match ty.kind() {
+ ty::Param(_) => false,
+ ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
+ _ => true,
+ }
+}
+
+/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
+pub fn contains_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
+ ty.walk(tcx).any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+}
+
+/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
+/// constructor.
+pub fn contains_adt_constructor<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, adt: &'tcx AdtDef) -> bool {
+ ty.walk(tcx).any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+}
+
+/// Resolves `<T as Iterator>::Item` for `T`
+/// Do not invoke without first verifying that the type implements `Iterator`
+pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+ cx.tcx
+ .get_diagnostic_item(sym::Iterator)
+ .and_then(|iter_did| {
+ cx.tcx.associated_items(iter_did).find_by_name_and_kind(
+ cx.tcx,
+ Ident::from_str("Item"),
+ ty::AssocKind::Type,
+ iter_did,
+ )
+ })
+ .map(|assoc| {
+ let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
+ cx.tcx.normalize_erasing_regions(cx.param_env, proj)
+ })
+}
+
+/// Returns true if ty has `iter` or `iter_mut` methods
+pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
+ // 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: &[Symbol] = &[
+ sym::Vec,
+ sym::Option,
+ sym::Result,
+ sym::BTreeMap,
+ sym::BTreeSet,
+ sym::VecDeque,
+ sym::LinkedList,
+ sym::BinaryHeap,
+ sym::HashSet,
+ sym::HashMap,
+ sym::PathBuf,
+ sym::Path,
+ sym::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(sym::array),
+ ty::Slice(..) => return Some(sym::slice),
+ ty::Adt(adt, _) => adt.did,
+ _ => return None,
+ };
+
+ for &name in into_iter_collections {
+ if cx.tcx.is_diagnostic_item(name, def_id) {
+ return Some(cx.tcx.item_name(def_id));
+ }
+ }
+ None
+}
+
+/// Checks whether a type implements a trait.
+/// The function returns false in case the type contains an inference variable.
+/// See also [`get_trait_def_id`](super::get_trait_def_id).
+pub fn implements_trait<'tcx>(
+ cx: &LateContext<'tcx>,
+ ty: Ty<'tcx>,
+ trait_id: DefId,
+ ty_params: &[GenericArg<'tcx>],
+) -> bool {
+ // Clippy shouldn't have infer types
+ assert!(!ty.needs_infer());
+
+ let ty = cx.tcx.erase_regions(ty);
+ if ty.has_escaping_bound_vars() {
+ return false;
+ }
+ let ty_params = cx.tcx.mk_substs(ty_params.iter());
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ infcx
+ .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
+ .must_apply_modulo_regions()
+ })
+}
+
+/// Checks whether this type implements `Drop`.
+pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.ty_adt_def() {
+ Some(def) => def.has_dtor(cx.tcx),
+ None => false,
+ }
+}
+
+// Returns whether the type has #[must_use] attribute
+pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind() {
+ ty::Adt(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(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::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(substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
+ ty::Opaque(ref def_id, _) => {
+ for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
+ if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
+ if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ ty::Dynamic(binder, _) => {
+ for predicate in binder.iter() {
+ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
+ if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ _ => false,
+ }
+}
+
+// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
+// this function can be removed once the `normalize` method does not panic when normalization does
+// not succeed
+/// Checks if `Ty` is normalizable. This function is useful
+/// to avoid crashes on `layout_of`.
+pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+ is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
+}
+
+fn is_normalizable_helper<'tcx>(
+ cx: &LateContext<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ cache: &mut FxHashMap<Ty<'tcx>, bool>,
+) -> bool {
+ if let Some(&cached_result) = cache.get(ty) {
+ return cached_result;
+ }
+ // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
+ cache.insert(ty, false);
+ let result = cx.tcx.infer_ctxt().enter(|infcx| {
+ let cause = rustc_middle::traits::ObligationCause::dummy();
+ if infcx.at(&cause, param_env).normalize(ty).is_ok() {
+ match ty.kind() {
+ ty::Adt(def, substs) => def.variants.iter().all(|variant| {
+ variant
+ .fields
+ .iter()
+ .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
+ }),
+ _ => ty.walk(cx.tcx).all(|generic_arg| match generic_arg.unpack() {
+ GenericArgKind::Type(inner_ty) if inner_ty != ty => {
+ is_normalizable_helper(cx, param_env, inner_ty, cache)
+ },
+ _ => true, // if inner_ty == ty, we've already checked it
+ }),
+ }
+ } else {
+ false
+ }
+ });
+ cache.insert(ty, result);
+ result
+}
+
- /// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
- /// number type, a str, or an array, slice, or tuple of those types).
++/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
++/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
++/// tuples and slices of primitive type) see `is_recursively_primitive_type`
+pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
+ matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
+}
+
++/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
++/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
+pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
+ ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
+ ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+ ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
+ _ => false,
+ }
+}
+
+/// Checks if the type is a reference equals to a diagnostic item
+pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+ match ty.kind() {
+ ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
+ _ => false,
+ },
+ _ => false,
+ }
+}
+
+/// Checks if the type is equal to a diagnostic item
+///
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
+ _ => false,
+ }
+}
+
+/// Checks if the type is equal to a lang item.
+///
+/// Returns `false` if the `LangItem` is not defined.
+pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).map_or(false, |li| li == adt.did),
+ _ => false,
+ }
+}
+
+/// Return `true` if the passed `typ` is `isize` or `usize`.
+pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
+ matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
+}
+
+/// Checks if type is struct, enum or union type with the given def path.
+///
+/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
+ _ => false,
+ }
+}
+
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
+ fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
+ if let ty::Ref(_, ty, _) = ty.kind() {
+ peel(ty, count + 1)
+ } else {
+ (ty, count)
+ }
+ }
+ peel(ty, 0)
+}
+
+/// Peels off all references on the type.Returns the underlying type, the number of references
+/// removed, and whether the pointer is ultimately mutable or not.
+pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
+ fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
+ match ty.kind() {
+ ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
+ ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
+ _ => (ty, count, mutability),
+ }
+ }
+ f(ty, 0, Mutability::Mut)
+}
+
+/// Returns `true` if the given type is an `unsafe` function.
+pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind() {
+ ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
+ _ => false,
+ }
+}
+
+/// 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, 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)
+}
+
+/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
+/// otherwise returns `false`
+pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+ match (&a.kind(), &b.kind()) {
+ (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
+ if did_a != did_b {
+ return false;
+ }
+
+ substs_a
+ .iter()
+ .zip(substs_b.iter())
+ .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
+ (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
+ (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
+ same_type_and_consts(type_a, type_b)
+ },
+ _ => true,
+ })
+ },
+ _ => a == b,
+ }
+}
--- /dev/null
- if let ty::BorrowKind::MutBorrow = bk {
+use crate as utils;
+use rustc_hir as hir;
+use rustc_hir::intravisit;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::HirIdSet;
+use rustc_hir::{Expr, ExprKind, HirId};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_middle::mir::FakeReadCause;
+use rustc_middle::ty;
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+
+/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
+pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<HirIdSet> {
+ let mut delegate = MutVarsDelegate {
+ used_mutably: HirIdSet::default(),
+ skip: false,
+ };
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ ExprUseVisitor::new(
+ &mut delegate,
+ &infcx,
+ expr.hir_id.owner,
+ cx.param_env,
+ cx.typeck_results(),
+ )
+ .walk_expr(expr);
+ });
+
+ if delegate.skip {
+ return None;
+ }
+ Some(delegate.used_mutably)
+}
+
+pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
+ mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
+}
+
+struct MutVarsDelegate {
+ used_mutably: HirIdSet,
+ skip: bool,
+}
+
+impl<'tcx> MutVarsDelegate {
+ #[allow(clippy::similar_names)]
+ fn update(&mut self, cat: &PlaceWithHirId<'tcx>) {
+ match cat.place.base {
+ PlaceBase::Local(id) => {
+ self.used_mutably.insert(id);
+ },
+ PlaceBase::Upvar(_) => {
+ //FIXME: This causes false negatives. We can't get the `NodeId` from
+ //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
+ //`while`-body, not just the ones in the condition.
+ self.skip = true;
+ },
+ _ => {},
+ }
+ }
+}
+
+impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
+ fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+ fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
++ if bk == ty::BorrowKind::MutBorrow {
+ self.update(cmt);
+ }
+ }
+
+ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
+ self.update(cmt);
+ }
+
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
+pub struct ParamBindingIdCollector {
+ binding_hir_ids: Vec<hir::HirId>,
+}
+impl<'tcx> ParamBindingIdCollector {
+ fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
+ let mut hir_ids: Vec<hir::HirId> = Vec::new();
+ for param in body.params.iter() {
+ let mut finder = ParamBindingIdCollector {
+ binding_hir_ids: Vec::new(),
+ };
+ finder.visit_param(param);
+ for hir_id in &finder.binding_hir_ids {
+ hir_ids.push(*hir_id);
+ }
+ }
+ hir_ids
+ }
+}
+impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
+ type Map = Map<'tcx>;
+
+ fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
+ if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
+ self.binding_hir_ids.push(hir_id);
+ }
+ intravisit::walk_pat(self, pat);
+ }
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+ intravisit::NestedVisitorMap::None
+ }
+}
+
+pub struct BindingUsageFinder<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ binding_ids: Vec<hir::HirId>,
+ usage_found: bool,
+}
+impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
+ pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
+ let mut finder = BindingUsageFinder {
+ cx,
+ binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
+ usage_found: false,
+ };
+ finder.visit_body(body);
+ finder.usage_found
+ }
+}
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+ if !self.usage_found {
+ intravisit::walk_expr(self, expr);
+ }
+ }
+
+ fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+ if let hir::def::Res::Local(id) = path.res {
+ if self.binding_ids.contains(&id) {
+ self.usage_found = true;
+ }
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+ intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+}
+
+struct ReturnBreakContinueMacroVisitor {
+ seen_return_break_continue: bool,
+}
+
+impl ReturnBreakContinueMacroVisitor {
+ fn new() -> ReturnBreakContinueMacroVisitor {
+ ReturnBreakContinueMacroVisitor {
+ seen_return_break_continue: false,
+ }
+ }
+}
+
+impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
+ type Map = Map<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+ if self.seen_return_break_continue {
+ // No need to look farther if we've already seen one of them
+ return;
+ }
+ match &ex.kind {
+ ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
+ self.seen_return_break_continue = true;
+ },
+ // Something special could be done here to handle while or for loop
+ // desugaring, as this will detect a break if there's a while loop
+ // or a for loop inside the expression.
+ _ => {
+ if utils::in_macro(ex.span) {
+ self.seen_return_break_continue = true;
+ } else {
+ rustc_hir::intravisit::walk_expr(self, ex);
+ }
+ },
+ }
+ }
+}
+
+pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
+ let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
+ recursive_visitor.visit_expr(expression);
+ recursive_visitor.seen_return_break_continue
+}
+
+pub struct UsedAfterExprVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ definition: HirId,
+ past_expr: bool,
+ used_after_expr: bool,
+}
+impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
+ pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ utils::path_to_local(expr).map_or(false, |definition| {
+ let mut visitor = UsedAfterExprVisitor {
+ cx,
+ expr,
+ definition,
+ past_expr: false,
+ used_after_expr: false,
+ };
+ utils::get_enclosing_block(cx, definition).map_or(false, |block| {
+ visitor.visit_block(block);
+ visitor.used_after_expr
+ })
+ })
+ }
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if self.used_after_expr {
+ return;
+ }
+
+ if expr.hir_id == self.expr.hir_id {
+ self.past_expr = true;
+ } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
+ self.used_after_expr = true;
+ } else {
+ intravisit::walk_expr(self, expr);
+ }
+ }
+}
--- /dev/null
- channel = "nightly-2021-09-28"
+[toolchain]
++channel = "nightly-2021-10-07"
+components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
--- /dev/null
- "dependencies not found in depinfo: {:?}",
+#![feature(test)] // compiletest_rs requires this attribute
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
+use compiletest_rs as compiletest;
+use compiletest_rs::common::Mode as TestMode;
+
+use std::collections::HashMap;
+use std::env::{self, remove_var, set_var, var_os};
+use std::ffi::{OsStr, OsString};
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+
+mod cargo;
+
+// whether to run internal tests or not
+const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
+
+/// All crates used in UI tests are listed here
+static TEST_DEPENDENCIES: &[&str] = &[
+ "clippy_utils",
+ "derive_new",
+ "if_chain",
+ "itertools",
+ "quote",
+ "regex",
+ "serde",
+ "serde_derive",
+ "syn",
+];
+
+// Test dependencies may need an `extern crate` here to ensure that they show up
+// in the depinfo file (otherwise cargo thinks they are unused)
+#[allow(unused_extern_crates)]
+extern crate clippy_utils;
+#[allow(unused_extern_crates)]
+extern crate derive_new;
+#[allow(unused_extern_crates)]
+extern crate if_chain;
+#[allow(unused_extern_crates)]
+extern crate itertools;
+#[allow(unused_extern_crates)]
+extern crate quote;
+#[allow(unused_extern_crates)]
+extern crate syn;
+
+/// Produces a string with an `--extern` flag for all UI test crate
+/// dependencies.
+///
+/// The dependency files are located by parsing the depinfo file for this test
+/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
+/// dependencies must be added to Cargo.toml at the project root. Test
+/// dependencies that are not *directly* used by this test module require an
+/// `extern crate` declaration.
+fn extern_flags() -> String {
+ let current_exe_depinfo = {
+ let mut path = env::current_exe().unwrap();
+ path.set_extension("d");
+ std::fs::read_to_string(path).unwrap()
+ };
+ let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
+ for line in current_exe_depinfo.lines() {
+ // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
+ let parse_name_path = || {
+ if line.starts_with(char::is_whitespace) {
+ return None;
+ }
+ let path_str = line.strip_suffix(':')?;
+ let path = Path::new(path_str);
+ if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
+ return None;
+ }
+ let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
+ // the "lib" prefix is not present for dll files
+ let name = name.strip_prefix("lib").unwrap_or(name);
+ Some((name, path_str))
+ };
+ if let Some((name, path)) = parse_name_path() {
+ if TEST_DEPENDENCIES.contains(&name) {
+ // A dependency may be listed twice if it is available in sysroot,
+ // and the sysroot dependencies are listed first. As of the writing,
+ // this only seems to apply to if_chain.
+ crates.insert(name, path);
+ }
+ }
+ }
+ let not_found: Vec<&str> = TEST_DEPENDENCIES
+ .iter()
+ .copied()
+ .filter(|n| !crates.contains_key(n))
+ .collect();
+ assert!(
+ not_found.is_empty(),
++ "dependencies not found in depinfo: {:?}\n\
++ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
++ help: Try adding to dev-dependencies in Cargo.toml",
+ not_found
+ );
+ crates
+ .into_iter()
+ .map(|(name, path)| format!(" --extern {}={}", name, path))
+ .collect()
+}
+
+fn default_config() -> compiletest::Config {
+ let mut config = compiletest::Config::default();
+
+ if let Ok(filters) = env::var("TESTNAME") {
+ config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
+ }
+
+ if let Some(path) = option_env!("RUSTC_LIB_PATH") {
+ let path = PathBuf::from(path);
+ config.run_lib_path = path.clone();
+ config.compile_lib_path = path;
+ }
+ let current_exe_path = std::env::current_exe().unwrap();
+ let deps_path = current_exe_path.parent().unwrap();
+ let profile_path = deps_path.parent().unwrap();
+
+ // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
+ // This is valuable because a) it allows us to monitor what external dependencies are used
+ // and b) it ensures that conflicting rlibs are resolved properly.
+ let host_libs = option_env!("HOST_LIBS")
+ .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
+ .unwrap_or_default();
+ config.target_rustcflags = Some(format!(
+ "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
+ deps_path.display(),
+ host_libs,
+ extern_flags(),
+ ));
+
+ config.build_base = profile_path.join("test");
+ config.rustc_path = profile_path.join(if cfg!(windows) {
+ "clippy-driver.exe"
+ } else {
+ "clippy-driver"
+ });
+ config
+}
+
+fn run_ui(cfg: &mut compiletest::Config) {
+ cfg.mode = TestMode::Ui;
+ cfg.src_base = Path::new("tests").join("ui");
+ // use tests/clippy.toml
+ let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
+ compiletest::run_tests(cfg);
+}
+
+fn run_internal_tests(cfg: &mut compiletest::Config) {
+ // only run internal tests with the internal-tests feature
+ if !RUN_INTERNAL_TESTS {
+ return;
+ }
+ cfg.mode = TestMode::Ui;
+ cfg.src_base = Path::new("tests").join("ui-internal");
+ compiletest::run_tests(cfg);
+}
+
+fn run_ui_toml(config: &mut compiletest::Config) {
+ fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
+ let mut result = true;
+ let opts = compiletest::test_opts(config);
+ for dir in fs::read_dir(&config.src_base)? {
+ let dir = dir?;
+ if !dir.file_type()?.is_dir() {
+ continue;
+ }
+ let dir_path = dir.path();
+ let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
+ for file in fs::read_dir(&dir_path)? {
+ let file = file?;
+ let file_path = file.path();
+ if file.file_type()?.is_dir() {
+ continue;
+ }
+ if file_path.extension() != Some(OsStr::new("rs")) {
+ continue;
+ }
+ let paths = compiletest::common::TestPaths {
+ file: file_path,
+ base: config.src_base.clone(),
+ relative_dir: dir_path.file_name().unwrap().into(),
+ };
+ let test_name = compiletest::make_test_name(config, &paths);
+ let index = tests
+ .iter()
+ .position(|test| test.desc.name == test_name)
+ .expect("The test should be in there");
+ result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
+ }
+ }
+ Ok(result)
+ }
+
+ config.mode = TestMode::Ui;
+ config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();
+
+ let tests = compiletest::make_tests(config);
+
+ let res = run_tests(config, tests);
+ match res {
+ Ok(true) => {},
+ Ok(false) => panic!("Some tests failed"),
+ Err(e) => {
+ panic!("I/O failure during tests: {:?}", e);
+ },
+ }
+}
+
+fn run_ui_cargo(config: &mut compiletest::Config) {
+ fn run_tests(
+ config: &compiletest::Config,
+ filters: &[String],
+ mut tests: Vec<tester::TestDescAndFn>,
+ ) -> Result<bool, io::Error> {
+ let mut result = true;
+ let opts = compiletest::test_opts(config);
+
+ for dir in fs::read_dir(&config.src_base)? {
+ let dir = dir?;
+ if !dir.file_type()?.is_dir() {
+ continue;
+ }
+
+ // Use the filter if provided
+ let dir_path = dir.path();
+ for filter in filters {
+ if !dir_path.ends_with(filter) {
+ continue;
+ }
+ }
+
+ for case in fs::read_dir(&dir_path)? {
+ let case = case?;
+ if !case.file_type()?.is_dir() {
+ continue;
+ }
+
+ let src_path = case.path().join("src");
+
+ // When switching between branches, if the previous branch had a test
+ // that the current branch does not have, the directory is not removed
+ // because an ignored Cargo.lock file exists.
+ if !src_path.exists() {
+ continue;
+ }
+
+ env::set_current_dir(&src_path)?;
+ for file in fs::read_dir(&src_path)? {
+ let file = file?;
+ if file.file_type()?.is_dir() {
+ continue;
+ }
+
+ // Search for the main file to avoid running a test for each file in the project
+ let file_path = file.path();
+ match file_path.file_name().and_then(OsStr::to_str) {
+ Some("main.rs") => {},
+ _ => continue,
+ }
+ let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
+ let paths = compiletest::common::TestPaths {
+ file: file_path,
+ base: config.src_base.clone(),
+ relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
+ };
+ let test_name = compiletest::make_test_name(config, &paths);
+ let index = tests
+ .iter()
+ .position(|test| test.desc.name == test_name)
+ .expect("The test should be in there");
+ result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
+ }
+ }
+ }
+ Ok(result)
+ }
+
+ if cargo::is_rustc_test_suite() {
+ return;
+ }
+
+ config.mode = TestMode::Ui;
+ config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
+
+ let tests = compiletest::make_tests(config);
+
+ let current_dir = env::current_dir().unwrap();
+ let res = run_tests(config, &config.filters, tests);
+ env::set_current_dir(current_dir).unwrap();
+
+ match res {
+ Ok(true) => {},
+ Ok(false) => panic!("Some tests failed"),
+ Err(e) => {
+ panic!("I/O failure during tests: {:?}", e);
+ },
+ }
+}
+
+fn prepare_env() {
+ set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
+ set_var("__CLIPPY_INTERNAL_TESTS", "true");
+ //set_var("RUST_BACKTRACE", "0");
+}
+
+#[test]
+fn compile_test() {
+ prepare_env();
+ let mut config = default_config();
+ run_ui(&mut config);
+ run_ui_toml(&mut config);
+ run_ui_cargo(&mut config);
+ run_internal_tests(&mut config);
+}
+
+/// Restores an env var on drop
+#[must_use]
+struct VarGuard {
+ key: &'static str,
+ value: Option<OsString>,
+}
+
+impl VarGuard {
+ fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
+ let value = var_os(key);
+ set_var(key, val);
+ Self { key, value }
+ }
+}
+
+impl Drop for VarGuard {
+ fn drop(&mut self) {
+ match self.value.as_deref() {
+ None => remove_var(self.key),
+ Some(value) => set_var(self.key, value),
+ }
+ }
+}
--- /dev/null
--- /dev/null
++enable-raw-pointer-heuristic-for-send = false
--- /dev/null
--- /dev/null
++#![warn(clippy::non_send_fields_in_send_ty)]
++#![feature(extern_types)]
++
++use std::rc::Rc;
++
++// Basic tests should not be affected
++pub struct NoGeneric {
++ rc_is_not_send: Rc<String>,
++}
++
++unsafe impl Send for NoGeneric {}
++
++pub struct MultiField<T> {
++ field1: T,
++ field2: T,
++ field3: T,
++}
++
++unsafe impl<T> Send for MultiField<T> {}
++
++pub enum MyOption<T> {
++ MySome(T),
++ MyNone,
++}
++
++unsafe impl<T> Send for MyOption<T> {}
++
++// All fields are disallowed when raw pointer heuristic is off
++extern "C" {
++ type NonSend;
++}
++
++pub struct HeuristicTest {
++ field1: Vec<*const NonSend>,
++ field2: [*const NonSend; 3],
++ field3: (*const NonSend, *const NonSend, *const NonSend),
++ field4: (*const NonSend, Rc<u8>),
++ field5: Vec<Vec<*const NonSend>>,
++}
++
++unsafe impl Send for HeuristicTest {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this implementation is unsound, as some fields in `NoGeneric` are `!Send`
++ --> $DIR/test.rs:11:1
++ |
++LL | unsafe impl Send for NoGeneric {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings`
++note: the type of field `rc_is_not_send` is `!Send`
++ --> $DIR/test.rs:8:5
++ |
++LL | rc_is_not_send: Rc<String>,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++
++error: this implementation is unsound, as some fields in `MultiField<T>` are `!Send`
++ --> $DIR/test.rs:19:1
++ |
++LL | unsafe impl<T> Send for MultiField<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `field1` is `!Send`
++ --> $DIR/test.rs:14:5
++ |
++LL | field1: T,
++ | ^^^^^^^^^
++ = help: add `T: Send` bound in `Send` impl
++note: the type of field `field2` is `!Send`
++ --> $DIR/test.rs:15:5
++ |
++LL | field2: T,
++ | ^^^^^^^^^
++ = help: add `T: Send` bound in `Send` impl
++note: the type of field `field3` is `!Send`
++ --> $DIR/test.rs:16:5
++ |
++LL | field3: T,
++ | ^^^^^^^^^
++ = help: add `T: Send` bound in `Send` impl
++
++error: this implementation is unsound, as some fields in `MyOption<T>` are `!Send`
++ --> $DIR/test.rs:26:1
++ |
++LL | unsafe impl<T> Send for MyOption<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `0` is `!Send`
++ --> $DIR/test.rs:22:12
++ |
++LL | MySome(T),
++ | ^
++ = help: add `T: Send` bound in `Send` impl
++
++error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send`
++ --> $DIR/test.rs:41:1
++ |
++LL | unsafe impl Send for HeuristicTest {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `field1` is `!Send`
++ --> $DIR/test.rs:34:5
++ |
++LL | field1: Vec<*const NonSend>,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++note: the type of field `field2` is `!Send`
++ --> $DIR/test.rs:35:5
++ |
++LL | field2: [*const NonSend; 3],
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++note: the type of field `field3` is `!Send`
++ --> $DIR/test.rs:36:5
++ |
++LL | field3: (*const NonSend, *const NonSend, *const NonSend),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++note: the type of field `field4` is `!Send`
++ --> $DIR/test.rs:37:5
++ |
++LL | field4: (*const NonSend, Rc<u8>),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++note: the type of field `field5` is `!Send`
++ --> $DIR/test.rs:38:5
++ |
++LL | field5: Vec<Vec<*const NonSend>>,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
- error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `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`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `third-party` at line 5 column 1
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `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`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `third-party` at line 5 column 1
+
+error: aborting due to previous error
+
--- /dev/null
- #[allow(unused, clippy::shadow_unrelated, clippy::similar_names)]
+#[warn(clippy::approx_constant)]
++#[allow(clippy::similar_names)]
+fn main() {
+ let my_e = 2.7182;
+ let almost_e = 2.718;
+ let no_e = 2.71;
+
+ let my_1_frac_pi = 0.3183;
+ let no_1_frac_pi = 0.31;
+
+ let my_frac_1_sqrt_2 = 0.70710678;
+ let almost_frac_1_sqrt_2 = 0.70711;
+ let my_frac_1_sqrt_2 = 0.707;
+
+ let my_frac_2_pi = 0.63661977;
+ let no_frac_2_pi = 0.636;
+
+ let my_frac_2_sq_pi = 1.128379;
+ let no_frac_2_sq_pi = 1.128;
+
+ let my_frac_pi_2 = 1.57079632679;
+ let no_frac_pi_2 = 1.5705;
+
+ let my_frac_pi_3 = 1.04719755119;
+ let no_frac_pi_3 = 1.047;
+
+ let my_frac_pi_4 = 0.785398163397;
+ let no_frac_pi_4 = 0.785;
+
+ let my_frac_pi_6 = 0.523598775598;
+ let no_frac_pi_6 = 0.523;
+
+ let my_frac_pi_8 = 0.3926990816987;
+ let no_frac_pi_8 = 0.392;
+
+ let my_ln_10 = 2.302585092994046;
+ let no_ln_10 = 2.303;
+
+ let my_ln_2 = 0.6931471805599453;
+ let no_ln_2 = 0.693;
+
+ let my_log10_e = 0.4342944819032518;
+ let no_log10_e = 0.434;
+
+ let my_log2_e = 1.4426950408889634;
+ let no_log2_e = 1.442;
+
+ let log2_10 = 3.321928094887362;
+ let no_log2_10 = 3.321;
+
+ let log10_2 = 0.301029995663981;
+ let no_log10_2 = 0.301;
+
+ let my_pi = 3.1415;
+ let almost_pi = 3.14;
+ let no_pi = 3.15;
+
+ let my_sq2 = 1.4142;
+ let no_sq2 = 1.414;
+
+ let my_tau = 6.2832;
+ let almost_tau = 6.28;
+ let no_tau = 6.3;
+}
--- /dev/null
- #![allow(dead_code)]
++#![allow(dead_code, clippy::equatable_if_let)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// This tests the branches_sharing_code lint at the end of blocks
+
+fn simple_examples() {
+ let x = 1;
+
+ let _ = if x == 7 {
+ println!("Branch I");
+ let start_value = 0;
+ println!("=^.^=");
+
+ // Same but not moveable due to `start_value`
+ let _ = start_value;
+
+ // The rest is self contained and moveable => Only lint the rest
+ let result = false;
+ println!("Block end!");
+ result
+ } else {
+ println!("Branch II");
+ let start_value = 8;
+ println!("xD");
+
+ // Same but not moveable due to `start_value`
+ let _ = start_value;
+
+ // The rest is self contained and moveable => Only lint the rest
+ let result = false;
+ println!("Block end!");
+ result
+ };
+
+ // Else if block
+ if x == 9 {
+ println!("The index is: 6");
+
+ println!("Same end of block");
+ } else if x == 8 {
+ println!("The index is: 4");
+
+ // We should only get a lint trigger for the last statement
+ println!("This is also eq with the else block");
+ println!("Same end of block");
+ } else {
+ println!("This is also eq with the else block");
+ println!("Same end of block");
+ }
+
+ // Use of outer scope value
+ let outer_scope_value = "I'm outside the if block";
+ if x < 99 {
+ let z = "How are you";
+ println!("I'm a local because I use the value `z`: `{}`", z);
+
+ println!(
+ "I'm moveable because I know: `outer_scope_value`: '{}'",
+ outer_scope_value
+ );
+ } else {
+ let z = 45678000;
+ println!("I'm a local because I use the value `z`: `{}`", z);
+
+ println!(
+ "I'm moveable because I know: `outer_scope_value`: '{}'",
+ outer_scope_value
+ );
+ }
+
+ if x == 9 {
+ if x == 8 {
+ // No parent!!
+ println!("---");
+ println!("Hello World");
+ } else {
+ println!("Hello World");
+ }
+ }
+}
+
+/// Simple examples where the move can cause some problems due to moved values
+fn simple_but_suggestion_is_invalid() {
+ let x = 16;
+
+ // Local value
+ let later_used_value = 17;
+ if x == 9 {
+ let _ = 9;
+ let later_used_value = "A string value";
+ println!("{}", later_used_value);
+ } else {
+ let later_used_value = "A string value";
+ println!("{}", later_used_value);
+ // I'm expecting a note about this
+ }
+ println!("{}", later_used_value);
+
+ // outer function
+ if x == 78 {
+ let simple_examples = "I now identify as a &str :)";
+ println!("This is the new simple_example: {}", simple_examples);
+ } else {
+ println!("Separator print statement");
+
+ let simple_examples = "I now identify as a &str :)";
+ println!("This is the new simple_example: {}", simple_examples);
+ }
+ simple_examples();
+}
+
+/// Tests where the blocks are not linted due to the used value scope
+fn not_moveable_due_to_value_scope() {
+ let x = 18;
+
+ // Using a local value in the moved code
+ if x == 9 {
+ let y = 18;
+ println!("y is: `{}`", y);
+ } else {
+ let y = "A string";
+ println!("y is: `{}`", y);
+ }
+
+ // Using a local value in the expression
+ let _ = if x == 0 {
+ let mut result = x + 1;
+
+ println!("1. Doing some calculations");
+ println!("2. Some more calculations");
+ println!("3. Setting result");
+
+ result
+ } else {
+ let mut result = x - 1;
+
+ println!("1. Doing some calculations");
+ println!("2. Some more calculations");
+ println!("3. Setting result");
+
+ result
+ };
+
+ let _ = if x == 7 {
+ let z1 = 100;
+ println!("z1: {}", z1);
+
+ let z2 = z1;
+ println!("z2: {}", z2);
+
+ z2
+ } else {
+ let z1 = 300;
+ println!("z1: {}", z1);
+
+ let z2 = z1;
+ println!("z2: {}", z2);
+
+ z2
+ };
+}
+
+/// This should add a note to the lint msg since the moved expression is not `()`
+fn added_note_for_expression_use() -> u32 {
+ let x = 9;
+
+ let _ = if x == 7 {
+ x << 2
+ } else {
+ let _ = 6;
+ x << 2
+ };
+
+ if x == 9 {
+ x * 4
+ } else {
+ let _ = 17;
+ x * 4
+ }
+}
+
+#[rustfmt::skip]
+fn test_suggestion_with_weird_formatting() {
+ let x = 9;
+ let mut a = 0;
+ let mut b = 0;
+
+ // The error message still looks weird tbh but this is the best I can do
+ // for weird formatting
+ if x == 17 { b = 1; a = 0x99; } else { a = 0x99; }
+}
+
+fn fp_test() {
+ let x = 17;
+
+ if x == 18 {
+ let y = 19;
+ if y < x {
+ println!("Trigger")
+ }
+ } else {
+ let z = 166;
+ if z < x {
+ println!("Trigger")
+ }
+ }
+}
+
+fn fp_if_let_issue7054() {
+ // This shouldn't trigger the lint
+ let string;
+ let _x = if let true = true {
+ ""
+ } else if true {
+ string = "x".to_owned();
+ &string
+ } else {
+ string = "y".to_owned();
+ &string
+ };
+}
+
+fn main() {}
--- /dev/null
- #![allow(clippy::assertions_on_constants)]
+// run-rustfix
++#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+#[warn(clippy::collapsible_else_if)]
+
+fn main() {
+ let x = "hello";
+ let y = "world";
+ // Collapse `else { if .. }` to `else if ..`
+ if x == "hello" {
+ print!("Hello ");
+ } else if y == "world" {
+ println!("world!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else if y == "world" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else if x == "hello" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ #[cfg(not(roflol))]
+ if y == "world" {
+ println!("world!")
+ }
+ }
+}
--- /dev/null
- #![allow(clippy::assertions_on_constants)]
+// run-rustfix
++#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+#[warn(clippy::collapsible_else_if)]
+
+fn main() {
+ let x = "hello";
+ let y = "world";
+ // Collapse `else { if .. }` to `else if ..`
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if y == "world" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else {
+ if x == "hello" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ #[cfg(not(roflol))]
+ if y == "world" {
+ println!("world!")
+ }
+ }
+}
--- /dev/null
- #![allow(clippy::assertions_on_constants)]
+// run-rustfix
++#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+fn main() {
+ let x = "hello";
+ let y = "world";
+ if x == "hello" && y == "world" {
+ println!("Hello world!");
+ }
+
+ if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
+ println!("Hello world!");
+ }
+
+ if x == "hello" && x == "world" && (y == "world" || y == "hello") {
+ println!("Hello world!");
+ }
+
+ if (x == "hello" || x == "world") && y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+
+ if x == "hello" && x == "world" && y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+
+ if 42 == 1337 && 'a' != 'A' {
+ println!("world!")
+ }
+
+ // Works because any if with an else statement cannot be collapsed.
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ }
+ } else {
+ println!("Not Hello world");
+ }
+
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ } else {
+ println!("Hello something else");
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if true {
+ } else {
+ assert!(true); // assert! is just an `if`
+ }
+
+
+ // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
+ if x == "hello" {// Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" && y == "world" { // Collapsible
+ println!("Hello world!");
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if let Some(42) = Some(42) {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ // Test behavior wrt. `let_chains`.
+ // None of the cases below should be collapsed.
+ fn truth() -> bool { true }
+
+ // Prefix:
+ if let 0 = 1 {
+ if truth() {}
+ }
+
+ // Suffix:
+ if truth() {
+ if let 0 = 1 {}
+ }
+
+ // Midfix:
+ if truth() {
+ if let 0 = 1 {
+ if truth() {}
+ }
+ }
+
+ // Fix #5962
+ if matches!(true, true) && matches!(true, true) {}
+
+ if true {
+ #[cfg(not(teehee))]
+ if true {
+ println!("Hello world!");
+ }
+ }
+}
--- /dev/null
- #![allow(clippy::assertions_on_constants)]
+// run-rustfix
++#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+fn main() {
+ let x = "hello";
+ let y = "world";
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" || x == "world" {
+ if y == "world" || y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" && x == "world" {
+ if y == "world" || y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" || x == "world" {
+ if y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" && x == "world" {
+ if y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if 42 == 1337 {
+ if 'a' != 'A' {
+ println!("world!")
+ }
+ }
+
+ // Works because any if with an else statement cannot be collapsed.
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ }
+ } else {
+ println!("Not Hello world");
+ }
+
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ } else {
+ println!("Hello something else");
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if true {
+ } else {
+ assert!(true); // assert! is just an `if`
+ }
+
+
+ // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
+ if x == "hello" {// Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ if y == "world" { // Collapsible
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if let Some(42) = Some(42) {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ // Test behavior wrt. `let_chains`.
+ // None of the cases below should be collapsed.
+ fn truth() -> bool { true }
+
+ // Prefix:
+ if let 0 = 1 {
+ if truth() {}
+ }
+
+ // Suffix:
+ if truth() {
+ if let 0 = 1 {}
+ }
+
+ // Midfix:
+ if truth() {
+ if let 0 = 1 {
+ if truth() {}
+ }
+ }
+
+ // Fix #5962
+ if matches!(true, true) {
+ if matches!(true, true) {}
+ }
+
+ if true {
+ #[cfg(not(teehee))]
+ if true {
+ println!("Hello world!");
+ }
+ }
+}
--- /dev/null
- #![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)]
+#![warn(clippy::collapsible_match)]
++#![allow(
++ clippy::needless_return,
++ clippy::no_effect,
++ clippy::single_match,
++ clippy::equatable_if_let
++)]
+
+fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
+ // match without block
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // match with block
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // if let, if let
+ if let Ok(val) = res_opt {
+ if let Some(n) = val {
+ take(n);
+ }
+ }
+
+ // if let else, if let else
+ if let Ok(val) = res_opt {
+ if let Some(n) = val {
+ take(n);
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ // if let, match
+ if let Ok(val) = res_opt {
+ match val {
+ Some(n) => foo(n),
+ _ => (),
+ }
+ }
+
+ // match, if let
+ match res_opt {
+ Ok(val) => {
+ if let Some(n) = val {
+ take(n);
+ }
+ },
+ _ => {},
+ }
+
+ // if let else, match
+ if let Ok(val) = res_opt {
+ match val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ } else {
+ return;
+ }
+
+ // match, if let else
+ match res_opt {
+ Ok(val) => {
+ if let Some(n) = val {
+ take(n);
+ } else {
+ return;
+ }
+ },
+ _ => return,
+ }
+
+ // None in inner match same as outer wild branch
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ None => return,
+ },
+ _ => return,
+ }
+
+ // None in outer match same as inner wild branch
+ match opt_opt {
+ Some(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ None => return,
+ }
+}
+
+fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u32, String>, String>) {
+ while let Some(x) = make() {
+ if let Some(1) = x {
+ todo!();
+ }
+ }
+ // no wild pattern in outer match
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ Err(_) => return,
+ }
+
+ // inner branch is not wild or None
+ match res_res {
+ Ok(val) => match val {
+ Ok(n) => foo(n),
+ Err(_) => return,
+ },
+ _ => return,
+ }
+
+ // statement before inner match
+ match res_opt {
+ Ok(val) => {
+ "hi buddy";
+ match val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ },
+ _ => return,
+ }
+
+ // statement after inner match
+ match res_opt {
+ Ok(val) => {
+ match val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ "hi buddy";
+ },
+ _ => return,
+ }
+
+ // wild branches do not match
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => {
+ "sup";
+ return;
+ },
+ },
+ _ => return,
+ }
+
+ // binding used in if guard
+ match res_opt {
+ Ok(val) if val.is_some() => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // binding used in inner match body
+ match res_opt {
+ Ok(val) => match val {
+ Some(_) => take(val),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // if guard on inner match
+ {
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) if make() => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+ match res_opt {
+ Ok(val) => match val {
+ _ => make(),
+ _ if make() => return,
+ },
+ _ => return,
+ }
+ }
+
+ // differing macro contexts
+ {
+ macro_rules! mac {
+ ($val:ident) => {
+ match $val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ };
+ }
+ match res_opt {
+ Ok(val) => mac!(val),
+ _ => return,
+ }
+ }
+
+ // OR pattern
+ enum E<T> {
+ A(T),
+ B(T),
+ C(T),
+ };
+ match make::<E<Option<u32>>>() {
+ E::A(val) | E::B(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+ match make::<Option<E<u32>>>() {
+ Some(val) => match val {
+ E::A(val) | E::B(val) => foo(val),
+ _ => return,
+ },
+ _ => return,
+ }
+ if let Ok(val) = res_opt {
+ if let Some(n) = val {
+ let _ = || {
+ // usage in closure
+ println!("{:?}", val);
+ };
+ }
+ }
+ let _: &dyn std::any::Any = match &Some(Some(1)) {
+ Some(e) => match e {
+ Some(e) => e,
+ e => e,
+ },
+ // else branch looks the same but the binding is different
+ e => e,
+ };
+}
+
+fn make<T>() -> T {
+ unimplemented!()
+}
+
+fn foo<T, U>(t: T) -> U {
+ unimplemented!()
+}
+
+fn take<T>(t: T) {}
+
+fn main() {}
--- /dev/null
- --> $DIR/collapsible_match.rs:7:20
+error: this `match` can be collapsed into the outer `match`
- --> $DIR/collapsible_match.rs:7:12
++ --> $DIR/collapsible_match.rs:12:20
+ |
+LL | Ok(val) => match val {
+ | ____________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_________^
+ |
+ = note: `-D clippy::collapsible-match` implied by `-D warnings`
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:16:20
++ --> $DIR/collapsible_match.rs:12:12
+ |
+LL | Ok(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
- --> $DIR/collapsible_match.rs:16:12
++ --> $DIR/collapsible_match.rs:21:20
+ |
+LL | Ok(val) => match val {
+ | ____________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:25:9
++ --> $DIR/collapsible_match.rs:21:12
+ |
+LL | Ok(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `if let`
- --> $DIR/collapsible_match.rs:24:15
++ --> $DIR/collapsible_match.rs:30:9
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:32:9
++ --> $DIR/collapsible_match.rs:29:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `if let`
- --> $DIR/collapsible_match.rs:31:15
++ --> $DIR/collapsible_match.rs:37:9
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | } else {
+LL | | return;
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:43:9
++ --> $DIR/collapsible_match.rs:36:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `if let`
- --> $DIR/collapsible_match.rs:42:15
++ --> $DIR/collapsible_match.rs:48:9
+ |
+LL | / match val {
+LL | | Some(n) => foo(n),
+LL | | _ => (),
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:52:13
++ --> $DIR/collapsible_match.rs:47:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | match val {
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `match`
- --> $DIR/collapsible_match.rs:51:12
++ --> $DIR/collapsible_match.rs:57:13
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | }
+ | |_____________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:61:9
++ --> $DIR/collapsible_match.rs:56:12
+ |
+LL | Ok(val) => {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `if let`
- --> $DIR/collapsible_match.rs:60:15
++ --> $DIR/collapsible_match.rs:66:9
+ |
+LL | / match val {
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:72:13
++ --> $DIR/collapsible_match.rs:65:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | match val {
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `match`
- --> $DIR/collapsible_match.rs:71:12
++ --> $DIR/collapsible_match.rs:77:13
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | } else {
+LL | | return;
+LL | | }
+ | |_____________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:83:20
++ --> $DIR/collapsible_match.rs:76:12
+ |
+LL | Ok(val) => {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
- --> $DIR/collapsible_match.rs:83:12
++ --> $DIR/collapsible_match.rs:88:20
+ |
+LL | Ok(val) => match val {
+ | ____________________^
+LL | | Some(n) => foo(n),
+LL | | None => return,
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
- --> $DIR/collapsible_match.rs:92:22
++ --> $DIR/collapsible_match.rs:88:12
+ |
+LL | Ok(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
- --> $DIR/collapsible_match.rs:92:14
++ --> $DIR/collapsible_match.rs:97:22
+ |
+LL | Some(val) => match val {
+ | ______________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
++ --> $DIR/collapsible_match.rs:97:14
+ |
+LL | Some(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: aborting due to 10 previous errors
+
--- /dev/null
- #![allow(clippy::blacklisted_name)]
+#![warn(clippy::all)]
++#![allow(clippy::blacklisted_name, clippy::equatable_if_let)]
+#![allow(unused)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
+
+enum Foo {
+ Bar,
+ Baz,
+}
+
+fn bar(foo: Foo) {
+ macro_rules! baz {
+ () => {
+ if let Foo::Bar = foo {}
+ };
+ }
+
+ baz!();
+ baz!();
+}
+
+fn main() {}
--- /dev/null
+// ignore-windows
+// ignore-macos
+
+#![feature(no_core, lang_items, start)]
+#![no_core]
++#![allow(clippy::missing_safety_doc)]
+
+#[link(name = "c")]
+extern "C" {}
+
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+#[lang = "freeze"]
+pub unsafe trait Freeze {}
+
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ 0
+}
+
+fn main() {}
+
+struct A;
+
+impl A {
+ pub fn as_ref(self) -> &'static str {
+ "A"
+ }
+}
--- /dev/null
- --> $DIR/def_id_nocore.rs:27:19
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
++ --> $DIR/def_id_nocore.rs:28:19
+ |
+LL | pub fn as_ref(self) -> &'static str {
+ | ^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to previous error
+
--- /dev/null
+use std::collections::HashMap;
+
+struct FooDefault<'a> {
+ a: bool,
+ b: i32,
+ c: u64,
+ d: Vec<i32>,
+ e: FooND1,
+ f: FooND2,
+ g: HashMap<i32, i32>,
+ h: (i32, Vec<i32>),
+ i: [Vec<i32>; 3],
+ j: [i32; 5],
+ k: Option<i32>,
+ l: &'a [i32],
+}
+
+impl std::default::Default for FooDefault<'_> {
+ fn default() -> Self {
+ Self {
+ a: false,
+ b: 0,
+ c: 0u64,
+ d: vec![],
+ e: Default::default(),
+ f: FooND2::default(),
+ g: HashMap::new(),
+ h: (0, vec![]),
+ i: [vec![], vec![], vec![]],
+ j: [0; 5],
+ k: None,
+ l: &[],
+ }
+ }
+}
+
+struct TupleDefault(bool, i32, u64);
+
+impl std::default::Default for TupleDefault {
+ fn default() -> Self {
+ Self(false, 0, 0u64)
+ }
+}
+
+struct FooND1 {
+ a: bool,
+}
+
+impl std::default::Default for FooND1 {
+ fn default() -> Self {
+ Self { a: true }
+ }
+}
+
+struct FooND2 {
+ a: i32,
+}
+
+impl std::default::Default for FooND2 {
+ fn default() -> Self {
+ Self { a: 5 }
+ }
+}
+
+struct FooNDNew {
+ a: bool,
+}
+
+impl FooNDNew {
+ fn new() -> Self {
+ Self { a: true }
+ }
+}
+
+impl Default for FooNDNew {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+struct FooNDVec(Vec<i32>);
+
+impl Default for FooNDVec {
+ fn default() -> Self {
+ Self(vec![5, 12])
+ }
+}
+
+struct StrDefault<'a>(&'a str);
+
+impl Default for StrDefault<'_> {
+ fn default() -> Self {
+ Self("")
+ }
+}
+
+#[derive(Default)]
+struct AlreadyDerived(i32, bool);
+
+macro_rules! mac {
+ () => {
+ 0
+ };
+ ($e:expr) => {
+ struct X(u32);
+ impl Default for X {
+ fn default() -> Self {
+ Self($e)
+ }
+ }
+ };
+}
+
+mac!(0);
+
+struct Y(u32);
+impl Default for Y {
+ fn default() -> Self {
+ Self(mac!())
+ }
+}
+
+struct RustIssue26925<T> {
+ a: Option<T>,
+}
+
+// We should watch out for cases where a manual impl is needed because a
+// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925).
+// For example, a struct with Option<T> does not require T: Default, but a derive adds
+// that type bound anyways. So until #26925 get fixed we should disable lint
+// for the following case
+impl<T> Default for RustIssue26925<T> {
+ fn default() -> Self {
+ Self { a: None }
+ }
+}
+
+struct SpecializedImpl<A, B> {
+ a: A,
+ b: B,
+}
+
+impl<T: Default> Default for SpecializedImpl<T, T> {
+ fn default() -> Self {
+ Self {
+ a: T::default(),
+ b: T::default(),
+ }
+ }
+}
+
+struct WithoutSelfCurly {
+ a: bool,
+}
+
+impl Default for WithoutSelfCurly {
+ fn default() -> Self {
+ WithoutSelfCurly { a: false }
+ }
+}
+
+struct WithoutSelfParan(bool);
+
+impl Default for WithoutSelfParan {
+ fn default() -> Self {
+ WithoutSelfParan(false)
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7655
+
+pub struct SpecializedImpl2<T> {
+ v: Vec<T>,
+}
+
+impl Default for SpecializedImpl2<String> {
+ fn default() -> Self {
+ Self { v: Vec::new() }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7654
+
+pub struct Color {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+/// `#000000`
+impl Default for Color {
+ fn default() -> Self {
+ Color { r: 0, g: 0, b: 0 }
+ }
+}
+
+pub struct Color2 {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+impl Default for Color2 {
+ /// `#000000`
+ fn default() -> Self {
+ Self { r: 0, g: 0, b: 0 }
+ }
+}
+
++pub struct RepeatDefault1 {
++ a: [i8; 32],
++}
++
++impl Default for RepeatDefault1 {
++ fn default() -> Self {
++ RepeatDefault1 { a: [0; 32] }
++ }
++}
++
++pub struct RepeatDefault2 {
++ a: [i8; 33],
++}
++
++impl Default for RepeatDefault2 {
++ fn default() -> Self {
++ RepeatDefault2 { a: [0; 33] }
++ }
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/7753
++
++pub enum IntOrString {
++ Int(i32),
++ String(String),
++}
++
++impl Default for IntOrString {
++ fn default() -> Self {
++ IntOrString::Int(0)
++ }
++}
++
+fn main() {}
--- /dev/null
- error: aborting due to 6 previous errors
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:18:1
+ |
+LL | / impl std::default::Default for FooDefault<'_> {
+LL | | fn default() -> Self {
+LL | | Self {
+LL | | a: false,
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::derivable-impls` implied by `-D warnings`
+ = help: try annotating `FooDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:39:1
+ |
+LL | / impl std::default::Default for TupleDefault {
+LL | | fn default() -> Self {
+LL | | Self(false, 0, 0u64)
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `TupleDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:91:1
+ |
+LL | / impl Default for StrDefault<'_> {
+LL | | fn default() -> Self {
+LL | | Self("")
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `StrDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:117:1
+ |
+LL | / impl Default for Y {
+LL | | fn default() -> Self {
+LL | | Self(mac!())
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `Y` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:156:1
+ |
+LL | / impl Default for WithoutSelfCurly {
+LL | | fn default() -> Self {
+LL | | WithoutSelfCurly { a: false }
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `WithoutSelfCurly` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:164:1
+ |
+LL | / impl Default for WithoutSelfParan {
+LL | | fn default() -> Self {
+LL | | WithoutSelfParan(false)
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `WithoutSelfParan` with `#[derive(Default)]`
+
++error: this `impl` can be derived
++ --> $DIR/derivable_impls.rs:214:1
++ |
++LL | / impl Default for RepeatDefault1 {
++LL | | fn default() -> Self {
++LL | | RepeatDefault1 { a: [0; 32] }
++LL | | }
++LL | | }
++ | |_^
++ |
++ = help: try annotating `RepeatDefault1` with `#[derive(Default)]`
++
++error: aborting due to 7 previous errors
+
--- /dev/null
+//! This file tests for the `DOC_MARKDOWN` lint.
+
+#![allow(dead_code, incomplete_features)]
+#![warn(clippy::doc_markdown)]
+#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
+#![rustfmt::skip]
+
+/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
+/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun
+/// which should be reported only once despite being __doubly bad__.
+/// Here be ::a::global:path.
+/// That's not code ~NotInCodeBlock~.
+/// be_sure_we_got_to_the_end_of_it
+fn foo_bar() {
+}
+
+/// That one tests multiline ticks.
+/// ```rust
+/// foo_bar FOO_BAR
+/// _foo bar_
+/// ```
+///
+/// ~~~rust
+/// foo_bar FOO_BAR
+/// _foo bar_
+/// ~~~
+/// be_sure_we_got_to_the_end_of_it
+fn multiline_codeblock() {
+}
+
+/// This _is a test for
+/// multiline
+/// emphasis_.
+/// be_sure_we_got_to_the_end_of_it
+fn test_emphasis() {
+}
+
+/// This tests units. See also #835.
+/// kiB MiB GiB TiB PiB EiB
+/// kib Mib Gib Tib Pib Eib
+/// kB MB GB TB PB EB
+/// kb Mb Gb Tb Pb Eb
+/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
+/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
+/// 32kB 32MB 32GB 32TB 32PB 32EB
+/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
+/// NaN
+/// be_sure_we_got_to_the_end_of_it
+fn test_units() {
+}
+
+/// This tests allowed identifiers.
+/// KiB MiB GiB TiB PiB EiB
+/// DirectX
+/// ECMAScript
+/// GPLv2 GPLv3
+/// GitHub GitLab
+/// IPv4 IPv6
+/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
+/// NaN NaNs
+/// OAuth GraphQL
+/// OCaml
+/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
+/// WebGL
+/// TensorFlow
+/// TrueType
+/// iOS macOS FreeBSD
+/// TeX LaTeX BibTeX BibLaTeX
+/// MinGW
+/// CamelCase (see also #2395)
+/// be_sure_we_got_to_the_end_of_it
+fn test_allowed() {
+}
+
+/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
+/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
+/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
+/// It can also be [inline_link2].
+///
+/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
+/// [inline_link]: https://foobar
+/// [inline_link2]: https://foobar
+/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
+/// `multiline_ticks` functions.
+///
+/// expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
+/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
+/// be_sure_we_got_to_the_end_of_it
+fn main() {
+ foo_bar();
+ multiline_codeblock();
+ test_emphasis();
+ test_units();
+}
+
+/// ## CamelCaseThing
+/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
+///
+/// # CamelCaseThing
+///
+/// Not a title #897 CamelCaseThing
+/// be_sure_we_got_to_the_end_of_it
+fn issue897() {
+}
+
+/// I am confused by brackets? (`x_y`)
+/// I am confused by brackets? (foo `x_y`)
+/// I am confused by brackets? (`x_y` foo)
+/// be_sure_we_got_to_the_end_of_it
+fn issue900() {
+}
+
+/// Diesel queries also have a similar problem to [Iterator][iterator], where
+/// /// More talking
+/// returning them from a function requires exposing the implementation of that
+/// function. The [`helper_types`][helper_types] module exists to help with this,
+/// but you might want to hide the return type or have it conditionally change.
+/// Boxing can achieve both.
+///
+/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
+/// [helper_types]: ../helper_types/index.html
+/// be_sure_we_got_to_the_end_of_it
+fn issue883() {
+}
+
+/// `foo_bar
+/// baz_quz`
+/// [foo
+/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html)
+fn multiline() {
+}
+
+/** E.g., serialization of an empty list: FooBar
+```
+That's in a code block: `PackedNode`
+```
+
+And BarQuz too.
+be_sure_we_got_to_the_end_of_it
+*/
+fn issue1073() {
+}
+
+/** E.g., serialization of an empty list: FooBar
+```
+That's in a code block: PackedNode
+```
+
+And BarQuz too.
+be_sure_we_got_to_the_end_of_it
+*/
+fn issue1073_alt() {
+}
+
+/// Tests more than three quotes:
+/// ````
+/// DoNotWarn
+/// ```
+/// StillDont
+/// ````
+/// be_sure_we_got_to_the_end_of_it
+fn four_quotes() {
+}
+
+/// See [NIST SP 800-56A, revision 2].
+///
+/// [NIST SP 800-56A, revision 2]:
+/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419
+fn issue_902_comment() {}
+
+#[cfg_attr(feature = "a", doc = " ```")]
+#[cfg_attr(not(feature = "a"), doc = " ```ignore")]
+/// fn main() {
+/// let s = "localhost:10000".to_string();
+/// println!("{}", s);
+/// }
+/// ```
+fn issue_1469() {}
+
+/**
+ * This is a doc comment that should not be a list
+ *This would also be an error under a strict common mark interpretation
+ */
+fn issue_1920() {}
+
+/// Ok: <http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels>
+///
+/// Not ok: http://www.unicode.org
+/// Not ok: https://www.unicode.org
+/// Not ok: http://www.unicode.org/
+/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
+fn issue_1832() {}
+
+/// An iterator over mycrate::Collection's values.
+/// It should not lint a `'static` lifetime in ticks.
+fn issue_2210() {}
+
+/// This should not cause the lint to trigger:
+/// #REQ-data-family.lint_partof_exists
+fn issue_2343() {}
+
+/// This should not cause an ICE:
+/// __|_ _|__||_|
+fn pulldown_cmark_crash() {}
+
++/// This should not lint
++/// (regression test for #7758)
++/// [plain text][path::to::item]
++fn intra_doc_link() {}
++
+// issue #7033 - generic_const_exprs ICE
+struct S<T, const N: usize>
+where [(); N.checked_next_power_of_two().unwrap()]: {
+ arr: [T; N.checked_next_power_of_two().unwrap()],
+ n: usize,
+}
+
+impl<T: Copy + Default, const N: usize> S<T, N>
+where [(); N.checked_next_power_of_two().unwrap()]: {
+ fn new() -> Self {
+ Self {
+ arr: [T::default(); N.checked_next_power_of_two().unwrap()],
+ n: 0,
+ }
+ }
+}
--- /dev/null
- pub trait UnsafeTrait {
+// aux-build:doc_unsafe_macros.rs
+
+#[macro_use]
+extern crate doc_unsafe_macros;
+
+/// This is not sufficiently documented
+pub unsafe fn destroy_the_planet() {
+ unimplemented!();
+}
+
+/// This one is
+///
+/// # Safety
+///
+/// This function shouldn't be called unless the horsemen are ready
+pub unsafe fn apocalypse(universe: &mut ()) {
+ unimplemented!();
+}
+
+/// This is a private function, so docs aren't necessary
+unsafe fn you_dont_see_me() {
+ unimplemented!();
+}
+
+mod private_mod {
+ pub unsafe fn only_crate_wide_accessible() {
+ unimplemented!();
+ }
+
+ pub unsafe fn republished() {
+ unimplemented!();
+ }
+}
+
+pub use private_mod::republished;
+
- impl UnsafeTrait for Struct {
++pub trait SafeTraitUnsafeMethods {
+ unsafe fn woefully_underdocumented(self);
+
+ /// # Safety
+ unsafe fn at_least_somewhat_documented(self);
+}
+
++pub unsafe trait UnsafeTrait {
++ fn method();
++}
++
++/// # Safety
++pub unsafe trait DocumentedUnsafeTrait {
++ fn method2();
++}
++
+pub struct Struct;
+
++impl SafeTraitUnsafeMethods for Struct {
+ unsafe fn woefully_underdocumented(self) {
+ // all is well
+ }
+
+ unsafe fn at_least_somewhat_documented(self) {
+ // all is still well
+ }
+}
+
++unsafe impl UnsafeTrait for Struct {
++ fn method() {}
++}
++
++unsafe impl DocumentedUnsafeTrait for Struct {
++ fn method2() {}
++}
++
+impl Struct {
+ pub unsafe fn more_undocumented_unsafe() -> Self {
+ unimplemented!();
+ }
+
+ /// # Safety
+ pub unsafe fn somewhat_documented(&self) {
+ unimplemented!();
+ }
+
+ unsafe fn private(&self) {
+ unimplemented!();
+ }
+}
+
+macro_rules! very_unsafe {
+ () => {
+ pub unsafe fn whee() {
+ unimplemented!()
+ }
+
+ /// # Safety
+ ///
+ /// Please keep the seat belt fastened
+ pub unsafe fn drive() {
+ whee()
+ }
+ };
+}
+
+very_unsafe!();
+
+// we don't lint code from external macros
+undocd_unsafe!();
+
+fn main() {
+ unsafe {
+ you_dont_see_me();
+ destroy_the_planet();
+ let mut universe = ();
+ apocalypse(&mut universe);
+ private_mod::only_crate_wide_accessible();
+ drive();
+ }
+}
--- /dev/null
- --> $DIR/doc_unsafe.rs:57:5
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:7:1
+ |
+LL | / pub unsafe fn destroy_the_planet() {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
+
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:30:5
+ |
+LL | / pub unsafe fn republished() {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:38:5
+ |
+LL | unsafe fn woefully_underdocumented(self);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
++error: docs for unsafe trait missing `# Safety` section
++ --> $DIR/doc_unsafe.rs:44:1
++ |
++LL | / pub unsafe trait UnsafeTrait {
++LL | | fn method();
++LL | | }
++ | |_^
++
+error: unsafe function's docs miss `# Safety` section
- --> $DIR/doc_unsafe.rs:73:9
++ --> $DIR/doc_unsafe.rs:74:5
+ |
+LL | / pub unsafe fn more_undocumented_unsafe() -> Self {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: unsafe function's docs miss `# Safety` section
- error: aborting due to 5 previous errors
++ --> $DIR/doc_unsafe.rs:90:9
+ |
+LL | / pub unsafe fn whee() {
+LL | | unimplemented!()
+LL | | }
+ | |_________^
+...
+LL | very_unsafe!();
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info)
+
++error: aborting due to 6 previous errors
+
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_variables, dead_code)]
++#![warn(clippy::equatable_if_let)]
++
++use std::cmp::Ordering;
++
++#[derive(PartialEq)]
++enum Enum {
++ TupleVariant(i32, u64),
++ RecordVariant { a: i64, b: u32 },
++ UnitVariant,
++ Recursive(Struct),
++}
++
++#[derive(PartialEq)]
++struct Struct {
++ a: i32,
++ b: bool,
++}
++
++enum NotPartialEq {
++ A,
++ B,
++}
++
++enum NotStructuralEq {
++ A,
++ B,
++}
++
++impl PartialEq for NotStructuralEq {
++ fn eq(&self, _: &NotStructuralEq) -> bool {
++ false
++ }
++}
++
++fn main() {
++ let a = 2;
++ let b = 3;
++ let c = Some(2);
++ let d = Struct { a: 2, b: false };
++ let e = Enum::UnitVariant;
++ let f = NotPartialEq::A;
++ let g = NotStructuralEq::A;
++
++ // true
++
++ if a == 2 {}
++ if a.cmp(&b) == Ordering::Greater {}
++ if c == Some(2) {}
++ if d == (Struct { a: 2, b: false }) {}
++ if e == Enum::TupleVariant(32, 64) {}
++ if e == (Enum::RecordVariant { a: 64, b: 32 }) {}
++ if e == Enum::UnitVariant {}
++ if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {}
++
++ // false
++
++ if let 2 | 3 = a {}
++ if let x @ 2 = a {}
++ if let Some(3 | 4) = c {}
++ if let Struct { a, b: false } = d {}
++ if let Struct { a: 2, b: x } = d {}
++ if let NotPartialEq::A = f {}
++ if g == NotStructuralEq::A {}
++ if let Some(NotPartialEq::A) = Some(f) {}
++ if Some(g) == Some(NotStructuralEq::A) {}
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_variables, dead_code)]
++#![warn(clippy::equatable_if_let)]
++
++use std::cmp::Ordering;
++
++#[derive(PartialEq)]
++enum Enum {
++ TupleVariant(i32, u64),
++ RecordVariant { a: i64, b: u32 },
++ UnitVariant,
++ Recursive(Struct),
++}
++
++#[derive(PartialEq)]
++struct Struct {
++ a: i32,
++ b: bool,
++}
++
++enum NotPartialEq {
++ A,
++ B,
++}
++
++enum NotStructuralEq {
++ A,
++ B,
++}
++
++impl PartialEq for NotStructuralEq {
++ fn eq(&self, _: &NotStructuralEq) -> bool {
++ false
++ }
++}
++
++fn main() {
++ let a = 2;
++ let b = 3;
++ let c = Some(2);
++ let d = Struct { a: 2, b: false };
++ let e = Enum::UnitVariant;
++ let f = NotPartialEq::A;
++ let g = NotStructuralEq::A;
++
++ // true
++
++ if let 2 = a {}
++ if let Ordering::Greater = a.cmp(&b) {}
++ if let Some(2) = c {}
++ if let Struct { a: 2, b: false } = d {}
++ if let Enum::TupleVariant(32, 64) = e {}
++ if let Enum::RecordVariant { a: 64, b: 32 } = e {}
++ if let Enum::UnitVariant = e {}
++ if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
++
++ // false
++
++ if let 2 | 3 = a {}
++ if let x @ 2 = a {}
++ if let Some(3 | 4) = c {}
++ if let Struct { a, b: false } = d {}
++ if let Struct { a: 2, b: x } = d {}
++ if let NotPartialEq::A = f {}
++ if let NotStructuralEq::A = g {}
++ if let Some(NotPartialEq::A) = Some(f) {}
++ if let Some(NotStructuralEq::A) = Some(g) {}
++}
--- /dev/null
--- /dev/null
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:49:8
++ |
++LL | if let 2 = a {}
++ | ^^^^^^^^^ help: try: `a == 2`
++ |
++ = note: `-D clippy::equatable-if-let` implied by `-D warnings`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:50:8
++ |
++LL | if let Ordering::Greater = a.cmp(&b) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:51:8
++ |
++LL | if let Some(2) = c {}
++ | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:52:8
++ |
++LL | if let Struct { a: 2, b: false } = d {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:53:8
++ |
++LL | if let Enum::TupleVariant(32, 64) = e {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:54:8
++ |
++LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:55:8
++ |
++LL | if let Enum::UnitVariant = e {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:56:8
++ |
++LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:66:8
++ |
++LL | if let NotStructuralEq::A = g {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
++
++error: this pattern matching can be expressed using equality
++ --> $DIR/equatable_if_let.rs:68:8
++ |
++LL | if let Some(NotStructuralEq::A) = Some(g) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
++
++error: aborting due to 10 previous errors
++
--- /dev/null
+// run-rustfix
+#![warn(clippy::excessive_precision)]
+#![allow(dead_code, unused_variables, clippy::print_literal)]
+
+fn main() {
+ // Consts
+ const GOOD32: f32 = 0.123_456;
+ const GOOD32_SM: f32 = 0.000_000_000_1;
+ const GOOD32_DOT: f32 = 10_000_000_000.0;
+ const GOOD32_EDGE: f32 = 1.000_000_8;
+ const GOOD64: f64 = 0.123_456_789_012;
+ const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
+ const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
+
+ const BAD32_1: f32 = 0.123_456_79_f32;
+ const BAD32_2: f32 = 0.123_456_79;
+ const BAD32_3: f32 = 0.1;
+ const BAD32_EDGE: f32 = 1.000_001;
+
+ const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
+ const BAD64_2: f64 = 0.123_456_789_012_345_67;
+ const BAD64_3: f64 = 0.1;
+
+ // Literal as param
+ println!("{:?}", 8.888_888_888_888_89);
+
+ // // TODO add inferred type tests for f32
+ // Locals
+ let good32: f32 = 0.123_456_f32;
+ let good32_2: f32 = 0.123_456;
+
+ let good64: f64 = 0.123_456_789_012;
+ let good64_suf: f64 = 0.123_456_789_012f64;
+ let good64_inf = 0.123_456_789_012;
+
+ let bad32: f32 = 1.123_456_8;
+ let bad32_suf: f32 = 1.123_456_8_f32;
+ let bad32_inf = 1.123_456_8_f32;
+
+ let bad64: f64 = 0.123_456_789_012_345_67;
+ let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
+ let bad64_inf = 0.123_456_789_012_345_67;
+
+ // Vectors
+ let good_vec32: Vec<f32> = vec![0.123_456];
+ let good_vec64: Vec<f64> = vec![0.123_456_789];
+
+ let bad_vec32: Vec<f32> = vec![0.123_456_79];
+ let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
+
+ // Exponential float notation
+ let good_e32: f32 = 1e-10;
+ let bad_e32: f32 = 1.123_456_8e-10;
+
+ let good_bige32: f32 = 1E-10;
+ let bad_bige32: f32 = 1.123_456_8E-10;
+
+ // Inferred type
+ let good_inferred: f32 = 1f32 * 1_000_000_000.;
+
+ // issue #2840
+ let num = 0.000_000_000_01e-10f64;
++
++ // issue #7744
++ let _ = 2.225_073_858_507_201e-308_f64;
++
++ // issue #7745
++ let _ = 0_f64;
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::excessive_precision)]
+#![allow(dead_code, unused_variables, clippy::print_literal)]
+
+fn main() {
+ // Consts
+ const GOOD32: f32 = 0.123_456;
+ const GOOD32_SM: f32 = 0.000_000_000_1;
+ const GOOD32_DOT: f32 = 10_000_000_000.0;
+ const GOOD32_EDGE: f32 = 1.000_000_8;
+ const GOOD64: f64 = 0.123_456_789_012;
+ const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
+ const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
+
+ const BAD32_1: f32 = 0.123_456_789_f32;
+ const BAD32_2: f32 = 0.123_456_789;
+ const BAD32_3: f32 = 0.100_000_000_000_1;
+ const BAD32_EDGE: f32 = 1.000_000_9;
+
+ const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
+ const BAD64_2: f64 = 0.123_456_789_012_345_67;
+ const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
+
+ // Literal as param
+ println!("{:?}", 8.888_888_888_888_888_888_888);
+
+ // // TODO add inferred type tests for f32
+ // Locals
+ let good32: f32 = 0.123_456_f32;
+ let good32_2: f32 = 0.123_456;
+
+ let good64: f64 = 0.123_456_789_012;
+ let good64_suf: f64 = 0.123_456_789_012f64;
+ let good64_inf = 0.123_456_789_012;
+
+ let bad32: f32 = 1.123_456_789;
+ let bad32_suf: f32 = 1.123_456_789_f32;
+ let bad32_inf = 1.123_456_789_f32;
+
+ let bad64: f64 = 0.123_456_789_012_345_67;
+ let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
+ let bad64_inf = 0.123_456_789_012_345_67;
+
+ // Vectors
+ let good_vec32: Vec<f32> = vec![0.123_456];
+ let good_vec64: Vec<f64> = vec![0.123_456_789];
+
+ let bad_vec32: Vec<f32> = vec![0.123_456_789];
+ let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
+
+ // Exponential float notation
+ let good_e32: f32 = 1e-10;
+ let bad_e32: f32 = 1.123_456_788_888e-10;
+
+ let good_bige32: f32 = 1E-10;
+ let bad_bige32: f32 = 1.123_456_788_888E-10;
+
+ // Inferred type
+ let good_inferred: f32 = 1f32 * 1_000_000_000.;
+
+ // issue #2840
+ let num = 0.000_000_000_01e-10f64;
++
++ // issue #7744
++ let _ = 2.225_073_858_507_201_1e-308_f64;
++
++ // issue #7745
++ let _ = 1.000_000_000_000_001e-324_f64;
+}
--- /dev/null
- error: aborting due to 13 previous errors
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:15:26
+ |
+LL | const BAD32_1: f32 = 0.123_456_789_f32;
+ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32`
+ |
+ = note: `-D clippy::excessive-precision` implied by `-D warnings`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:16:26
+ |
+LL | const BAD32_2: f32 = 0.123_456_789;
+ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:17:26
+ |
+LL | const BAD32_3: f32 = 0.100_000_000_000_1;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:18:29
+ |
+LL | const BAD32_EDGE: f32 = 1.000_000_9;
+ | ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:22:26
+ |
+LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:25:22
+ |
+LL | println!("{:?}", 8.888_888_888_888_888_888_888);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:36:22
+ |
+LL | let bad32: f32 = 1.123_456_789;
+ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:37:26
+ |
+LL | let bad32_suf: f32 = 1.123_456_789_f32;
+ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:38:21
+ |
+LL | let bad32_inf = 1.123_456_789_f32;
+ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:48:36
+ |
+LL | let bad_vec32: Vec<f32> = vec![0.123_456_789];
+ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:49:36
+ |
+LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:53:24
+ |
+LL | let bad_e32: f32 = 1.123_456_788_888e-10;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:56:27
+ |
+LL | let bad_bige32: f32 = 1.123_456_788_888E-10;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
+
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:65:13
++ |
++LL | let _ = 2.225_073_858_507_201_1e-308_f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `2.225_073_858_507_201e-308_f64`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:68:13
++ |
++LL | let _ = 1.000_000_000_000_001e-324_f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64`
++
++error: aborting due to 15 previous errors
+
--- /dev/null
- #[allow(
- clippy::linkedlist,
- clippy::shadow_unrelated,
- clippy::unnecessary_mut_passed,
- clippy::similar_names
- )]
+// run-rustfix
+
+#![allow(dead_code, unused)]
+
+use std::collections::*;
+
+#[warn(clippy::all)]
+struct Unrelated(Vec<u8>);
+impl Unrelated {
+ fn next(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+
+ fn iter(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+}
+
+#[warn(
+ clippy::needless_range_loop,
+ clippy::explicit_iter_loop,
+ clippy::explicit_into_iter_loop,
+ clippy::iter_next_loop,
+ clippy::for_kv_map
+)]
++#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
+#[allow(unused_variables)]
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+
+ // See #601
+ for i in 0..10 {
+ // no error, id_col does not exist outside the loop
+ let mut id_col = vec![0f64; 10];
+ id_col[i] = 1f64;
+ }
+
+ for _v in &vec {}
+
+ for _v in &mut vec {}
+
+ let out_vec = vec![1, 2, 3];
+ for _v in out_vec {}
+
+ for _v in &vec {} // these are fine
+ for _v in &mut vec {} // these are fine
+
+ for _v in &[1, 2, 3] {}
+
+ for _v in (&mut [1, 2, 3]).iter() {} // no error
+
+ for _v in &[0; 32] {}
+
+ for _v in [0; 33].iter() {} // no error
+
+ let ll: LinkedList<()> = LinkedList::new();
+ for _v in &ll {}
+
+ let vd: VecDeque<()> = VecDeque::new();
+ for _v in &vd {}
+
+ let bh: BinaryHeap<()> = BinaryHeap::new();
+ for _v in &bh {}
+
+ let hm: HashMap<(), ()> = HashMap::new();
+ for _v in &hm {}
+
+ let bt: BTreeMap<(), ()> = BTreeMap::new();
+ for _v in &bt {}
+
+ let hs: HashSet<()> = HashSet::new();
+ for _v in &hs {}
+
+ let bs: BTreeSet<()> = BTreeSet::new();
+ for _v in &bs {}
+
+ let u = Unrelated(vec![]);
+ for _v in u.next() {} // no error
+ for _v in u.iter() {} // no error
+
+ let mut out = vec![];
+ vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
+ let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
+
+ // Loop with explicit counter variable
+
+ // Potential false positives
+ let mut _index = 0;
+ _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ _index += 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ let mut _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index *= 2;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index = 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+
+ for _v in &vec {
+ let mut _index = 0;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index = 0;
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ for _x in 0..1 {
+ _index += 1;
+ }
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for x in &vec {
+ if *x == 1 {
+ _index += 1
+ }
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 1;
+ if false {
+ _index = 0
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ {
+ let mut _x = &mut index;
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ for _v in &vec {
+ index += 1
+ }
+ println!("index: {}", index);
+
+ fn f<T>(_: &T, _: &T) -> bool {
+ unimplemented!()
+ }
+ fn g<T>(_: &mut [T], _: usize, _: usize) {
+ unimplemented!()
+ }
+ for i in 1..vec.len() {
+ if f(&vec[i - 1], &vec[i]) {
+ g(&mut vec, i - 1, i);
+ }
+ }
+
+ for mid in 1..vec.len() {
+ let (_, _) = vec.split_at(mid);
+ }
+}
+
+fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
+ let pivot = v.len() - 1;
+ let mut i = 0;
+ for j in 0..pivot {
+ if v[j] <= v[pivot] {
+ v.swap(i, j);
+ i += 1;
+ }
+ }
+ v.swap(i, pivot);
+ i
+}
+
+#[warn(clippy::needless_range_loop)]
+pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
+ // Same source and destination - don't trigger lint
+ for i in 0..dst.len() {
+ dst[d + i] = dst[s + i];
+ }
+}
+
+mod issue_2496 {
+ pub trait Handle {
+ fn new_for_index(index: usize) -> Self;
+ fn index(&self) -> usize;
+ }
+
+ pub fn test<H: Handle>() -> H {
+ for x in 0..5 {
+ let next_handle = H::new_for_index(x);
+ println!("{}", next_handle.index());
+ }
+ unimplemented!()
+ }
+}
+
+// explicit_into_iter_loop bad suggestions
+#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
+mod issue_4958 {
+ fn takes_iterator<T>(iterator: &T)
+ where
+ for<'a> &'a T: IntoIterator<Item = &'a String>,
+ {
+ for i in iterator {
+ println!("{}", i);
+ }
+ }
+
+ struct T;
+ impl IntoIterator for &T {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ vec![].into_iter()
+ }
+ }
+
+ fn more_tests() {
+ let t = T;
+ let r = &t;
+ let rr = &&t;
+
+ // This case is handled by `explicit_iter_loop`. No idea why.
+ for _ in &t {}
+
+ for _ in r {}
+
+ // No suggestion for this.
+ // We'd have to suggest `for _ in *rr {}` which is less clear.
+ for _ in rr.into_iter() {}
+ }
+}
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ fn f() {
+ for _ in S.into_iter::<u32>() {
+ unimplemented!()
+ }
+ }
+}
--- /dev/null
- #[allow(
- clippy::linkedlist,
- clippy::shadow_unrelated,
- clippy::unnecessary_mut_passed,
- clippy::similar_names
- )]
+// run-rustfix
+
+#![allow(dead_code, unused)]
+
+use std::collections::*;
+
+#[warn(clippy::all)]
+struct Unrelated(Vec<u8>);
+impl Unrelated {
+ fn next(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+
+ fn iter(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+}
+
+#[warn(
+ clippy::needless_range_loop,
+ clippy::explicit_iter_loop,
+ clippy::explicit_into_iter_loop,
+ clippy::iter_next_loop,
+ clippy::for_kv_map
+)]
++#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
+#[allow(unused_variables)]
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+
+ // See #601
+ for i in 0..10 {
+ // no error, id_col does not exist outside the loop
+ let mut id_col = vec![0f64; 10];
+ id_col[i] = 1f64;
+ }
+
+ for _v in vec.iter() {}
+
+ for _v in vec.iter_mut() {}
+
+ let out_vec = vec![1, 2, 3];
+ for _v in out_vec.into_iter() {}
+
+ for _v in &vec {} // these are fine
+ for _v in &mut vec {} // these are fine
+
+ for _v in [1, 2, 3].iter() {}
+
+ for _v in (&mut [1, 2, 3]).iter() {} // no error
+
+ for _v in [0; 32].iter() {}
+
+ for _v in [0; 33].iter() {} // no error
+
+ let ll: LinkedList<()> = LinkedList::new();
+ for _v in ll.iter() {}
+
+ let vd: VecDeque<()> = VecDeque::new();
+ for _v in vd.iter() {}
+
+ let bh: BinaryHeap<()> = BinaryHeap::new();
+ for _v in bh.iter() {}
+
+ let hm: HashMap<(), ()> = HashMap::new();
+ for _v in hm.iter() {}
+
+ let bt: BTreeMap<(), ()> = BTreeMap::new();
+ for _v in bt.iter() {}
+
+ let hs: HashSet<()> = HashSet::new();
+ for _v in hs.iter() {}
+
+ let bs: BTreeSet<()> = BTreeSet::new();
+ for _v in bs.iter() {}
+
+ let u = Unrelated(vec![]);
+ for _v in u.next() {} // no error
+ for _v in u.iter() {} // no error
+
+ let mut out = vec![];
+ vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
+ let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
+
+ // Loop with explicit counter variable
+
+ // Potential false positives
+ let mut _index = 0;
+ _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ _index += 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ let mut _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index *= 2;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index = 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+
+ for _v in &vec {
+ let mut _index = 0;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index = 0;
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ for _x in 0..1 {
+ _index += 1;
+ }
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for x in &vec {
+ if *x == 1 {
+ _index += 1
+ }
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 1;
+ if false {
+ _index = 0
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ {
+ let mut _x = &mut index;
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ for _v in &vec {
+ index += 1
+ }
+ println!("index: {}", index);
+
+ fn f<T>(_: &T, _: &T) -> bool {
+ unimplemented!()
+ }
+ fn g<T>(_: &mut [T], _: usize, _: usize) {
+ unimplemented!()
+ }
+ for i in 1..vec.len() {
+ if f(&vec[i - 1], &vec[i]) {
+ g(&mut vec, i - 1, i);
+ }
+ }
+
+ for mid in 1..vec.len() {
+ let (_, _) = vec.split_at(mid);
+ }
+}
+
+fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
+ let pivot = v.len() - 1;
+ let mut i = 0;
+ for j in 0..pivot {
+ if v[j] <= v[pivot] {
+ v.swap(i, j);
+ i += 1;
+ }
+ }
+ v.swap(i, pivot);
+ i
+}
+
+#[warn(clippy::needless_range_loop)]
+pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
+ // Same source and destination - don't trigger lint
+ for i in 0..dst.len() {
+ dst[d + i] = dst[s + i];
+ }
+}
+
+mod issue_2496 {
+ pub trait Handle {
+ fn new_for_index(index: usize) -> Self;
+ fn index(&self) -> usize;
+ }
+
+ pub fn test<H: Handle>() -> H {
+ for x in 0..5 {
+ let next_handle = H::new_for_index(x);
+ println!("{}", next_handle.index());
+ }
+ unimplemented!()
+ }
+}
+
+// explicit_into_iter_loop bad suggestions
+#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
+mod issue_4958 {
+ fn takes_iterator<T>(iterator: &T)
+ where
+ for<'a> &'a T: IntoIterator<Item = &'a String>,
+ {
+ for i in iterator.into_iter() {
+ println!("{}", i);
+ }
+ }
+
+ struct T;
+ impl IntoIterator for &T {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ vec![].into_iter()
+ }
+ }
+
+ fn more_tests() {
+ let t = T;
+ let r = &t;
+ let rr = &&t;
+
+ // This case is handled by `explicit_iter_loop`. No idea why.
+ for _ in t.into_iter() {}
+
+ for _ in r.into_iter() {}
+
+ // No suggestion for this.
+ // We'd have to suggest `for _ in *rr {}` which is less clear.
+ for _ in rr.into_iter() {}
+ }
+}
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ fn f() {
+ for _ in S.into_iter::<u32>() {
+ unimplemented!()
+ }
+ }
+}
--- /dev/null
- --> $DIR/for_loop_fixable.rs:43:15
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:45:15
++ --> $DIR/for_loop_fixable.rs:38:15
+ |
+LL | for _v in vec.iter() {}
+ | ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
+ |
+ = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:48:15
++ --> $DIR/for_loop_fixable.rs:40:15
+ |
+LL | for _v in vec.iter_mut() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:53:15
++ --> $DIR/for_loop_fixable.rs:43:15
+ |
+LL | for _v in out_vec.into_iter() {}
+ | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec`
+ |
+ = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:57:15
++ --> $DIR/for_loop_fixable.rs:48:15
+ |
+LL | for _v in [1, 2, 3].iter() {}
+ | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:62:15
++ --> $DIR/for_loop_fixable.rs:52:15
+ |
+LL | for _v in [0; 32].iter() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:65:15
++ --> $DIR/for_loop_fixable.rs:57:15
+ |
+LL | for _v in ll.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&ll`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:68:15
++ --> $DIR/for_loop_fixable.rs:60:15
+ |
+LL | for _v in vd.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&vd`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:71:15
++ --> $DIR/for_loop_fixable.rs:63:15
+ |
+LL | for _v in bh.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bh`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:74:15
++ --> $DIR/for_loop_fixable.rs:66:15
+ |
+LL | for _v in hm.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&hm`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:77:15
++ --> $DIR/for_loop_fixable.rs:69:15
+ |
+LL | for _v in bt.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bt`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:80:15
++ --> $DIR/for_loop_fixable.rs:72:15
+ |
+LL | for _v in hs.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&hs`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:255:18
++ --> $DIR/for_loop_fixable.rs:75:15
+ |
+LL | for _v in bs.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bs`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:275:18
++ --> $DIR/for_loop_fixable.rs:250:18
+ |
+LL | for i in iterator.into_iter() {
+ | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
- --> $DIR/for_loop_fixable.rs:277:18
++ --> $DIR/for_loop_fixable.rs:270:18
+ |
+LL | for _ in t.into_iter() {}
+ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:272:18
+ |
+LL | for _ in r.into_iter() {}
+ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
+
+error: aborting due to 15 previous errors
+
--- /dev/null
- #[allow(
- clippy::linkedlist,
- clippy::shadow_unrelated,
- clippy::unnecessary_mut_passed,
- clippy::similar_names,
- unused,
- dead_code
- )]
+// Tests from for_loop.rs that don't have suggestions
+
+#[warn(
+ clippy::needless_range_loop,
+ clippy::explicit_iter_loop,
+ clippy::explicit_into_iter_loop,
+ clippy::iter_next_loop,
+ clippy::for_kv_map
+)]
++#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
+fn main() {
+ let vec = vec![1, 2, 3, 4];
+
+ for _v in vec.iter().next() {}
+}
--- /dev/null
- --> $DIR/for_loop_unfixable.rs:21:15
+error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
++ --> $DIR/for_loop_unfixable.rs:14:15
+ |
+LL | for _v in vec.iter().next() {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::iter-next-loop` implied by `-D warnings`
+
+error: aborting due to previous error
+
--- /dev/null
+#![warn(clippy::if_same_then_else)]
+#![allow(
+ clippy::blacklisted_name,
+ clippy::collapsible_else_if,
++ clippy::equatable_if_let,
+ clippy::collapsible_if,
+ clippy::ifs_same_cond,
+ clippy::needless_return,
+ clippy::single_element_loop,
+ clippy::branches_sharing_code
+)]
+
+fn if_same_then_else2() -> Result<&'static str, ()> {
+ if true {
+ for _ in &[42] {
+ let foo: &Option<_> = &Some::<u8>(42);
+ if foo.is_some() {
+ break;
+ } else {
+ continue;
+ }
+ }
+ } else {
+ //~ ERROR same body as `if` block
+ for _ in &[42] {
+ let bar: &Option<_> = &Some::<u8>(42);
+ if bar.is_some() {
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ if true {
+ if let Some(a) = Some(42) {}
+ } else {
+ //~ ERROR same body as `if` block
+ if let Some(a) = Some(42) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ //~ ERROR same body as `if` block
+ if let (1, .., 3) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ if let (.., 3) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ if let (.., 4) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ if let (.., 1, 3) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let Some(42) = None {}
+ } else {
+ if let Option::Some(42) = None {}
+ }
+
+ if true {
+ if let Some(42) = None::<u8> {}
+ } else {
+ if let Some(42) = None {}
+ }
+
+ if true {
+ if let Some(42) = None::<u8> {}
+ } else {
+ if let Some(42) = None::<u32> {}
+ }
+
+ if true {
+ if let Some(a) = Some(42) {}
+ } else {
+ if let Some(a) = Some(43) {}
+ }
+
+ // Same NaNs
+ let _ = if true {
+ f32::NAN
+ } else {
+ //~ ERROR same body as `if` block
+ f32::NAN
+ };
+
+ if true {
+ Ok("foo")?;
+ } else {
+ //~ ERROR same body as `if` block
+ Ok("foo")?;
+ }
+
+ if true {
+ let foo = "";
+ return Ok(&foo[0..]);
+ } else if false {
+ let foo = "bar";
+ return Ok(&foo[0..]);
+ } else {
+ let foo = "";
+ return Ok(&foo[0..]);
+ }
+
+ if true {
+ let foo = "";
+ return Ok(&foo[0..]);
+ } else if false {
+ let foo = "bar";
+ return Ok(&foo[0..]);
+ } else if true {
+ let foo = "";
+ return Ok(&foo[0..]);
+ } else {
+ let foo = "";
+ return Ok(&foo[0..]);
+ }
+
+ // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559.
+ if true {
+ let foo = "";
+ let (x, y) = (1, 2);
+ return Ok(&foo[x..y]);
+ } else {
+ let foo = "";
+ let (y, x) = (1, 2);
+ return Ok(&foo[x..y]);
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/if_same_then_else2.rs:13:13
+error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:22:12
++ --> $DIR/if_same_then_else2.rs:14:13
+ |
+LL | if true {
+ | _____________^
+LL | | for _ in &[42] {
+LL | | let foo: &Option<_> = &Some::<u8>(42);
+LL | | if foo.is_some() {
+... |
+LL | | }
+LL | | } else {
+ | |_____^
+ |
+ = note: `-D clippy::if-same-then-else` implied by `-D warnings`
+note: same as this
- --> $DIR/if_same_then_else2.rs:34:13
++ --> $DIR/if_same_then_else2.rs:23:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | for _ in &[42] {
+LL | | let bar: &Option<_> = &Some::<u8>(42);
+... |
+LL | | }
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:36:12
++ --> $DIR/if_same_then_else2.rs:35:13
+ |
+LL | if true {
+ | _____________^
+LL | | if let Some(a) = Some(42) {}
+LL | | } else {
+ | |_____^
+ |
+note: same as this
- --> $DIR/if_same_then_else2.rs:41:13
++ --> $DIR/if_same_then_else2.rs:37:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | if let Some(a) = Some(42) {}
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:43:12
++ --> $DIR/if_same_then_else2.rs:42:13
+ |
+LL | if true {
+ | _____________^
+LL | | if let (1, .., 3) = (1, 2, 3) {}
+LL | | } else {
+ | |_____^
+ |
+note: same as this
- --> $DIR/if_same_then_else2.rs:91:21
++ --> $DIR/if_same_then_else2.rs:44:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | if let (1, .., 3) = (1, 2, 3) {}
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:93:12
++ --> $DIR/if_same_then_else2.rs:92:21
+ |
+LL | let _ = if true {
+ | _____________________^
+LL | | f32::NAN
+LL | | } else {
+ | |_____^
+ |
+note: same as this
- --> $DIR/if_same_then_else2.rs:98:13
++ --> $DIR/if_same_then_else2.rs:94:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | f32::NAN
+LL | | };
+ | |_____^
+
+error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:100:12
++ --> $DIR/if_same_then_else2.rs:99:13
+ |
+LL | if true {
+ | _____________^
+LL | | Ok("foo")?;
+LL | | } else {
+ | |_____^
+ |
+note: same as this
- --> $DIR/if_same_then_else2.rs:122:20
++ --> $DIR/if_same_then_else2.rs:101:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | Ok("foo")?;
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:125:12
++ --> $DIR/if_same_then_else2.rs:123:20
+ |
+LL | } else if true {
+ | ____________________^
+LL | | let foo = "";
+LL | | return Ok(&foo[0..]);
+LL | | } else {
+ | |_____^
+ |
+note: same as this
++ --> $DIR/if_same_then_else2.rs:126:12
+ |
+LL | } else {
+ | ____________^
+LL | | let foo = "";
+LL | | return Ok(&foo[0..]);
+LL | | }
+ | |_____^
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+// run-rustfix
+#![warn(clippy::if_then_panic)]
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ assert!(a.is_empty(), "qaqaq{:?}", a);
+ assert!(a.is_empty(), "qwqwq");
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
++ let b = vec![1, 2, 3];
++ assert!(!b.is_empty(), "panic1");
++ assert!(!(b.is_empty() && a.is_empty()), "panic2");
++ assert!(!(a.is_empty() && !b.is_empty()), "panic3");
++ assert!(!(b.is_empty() || a.is_empty()), "panic4");
++ assert!(!(a.is_empty() || !b.is_empty()), "panic5");
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::if_then_panic)]
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ if !a.is_empty() {
+ panic!("qaqaq{:?}", a);
+ }
+ if !a.is_empty() {
+ panic!("qwqwq");
+ }
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
++ let b = vec![1, 2, 3];
++ if b.is_empty() {
++ panic!("panic1");
++ }
++ if b.is_empty() && a.is_empty() {
++ panic!("panic2");
++ }
++ if a.is_empty() && !b.is_empty() {
++ panic!("panic3");
++ }
++ if b.is_empty() || a.is_empty() {
++ panic!("panic4");
++ }
++ if a.is_empty() || !b.is_empty() {
++ panic!("panic5");
++ }
+}
--- /dev/null
- error: aborting due to 2 previous errors
+error: only a `panic!` in `if`-then statement
+ --> $DIR/if_then_panic.rs:19:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qaqaq{:?}", a);
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
+ |
+ = note: `-D clippy::if-then-panic` implied by `-D warnings`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/if_then_panic.rs:22:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qwqwq");
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
+
++error: only a `panic!` in `if`-then statement
++ --> $DIR/if_then_panic.rs:39:5
++ |
++LL | / if b.is_empty() {
++LL | | panic!("panic1");
++LL | | }
++ | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/if_then_panic.rs:42:5
++ |
++LL | / if b.is_empty() && a.is_empty() {
++LL | | panic!("panic2");
++LL | | }
++ | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/if_then_panic.rs:45:5
++ |
++LL | / if a.is_empty() && !b.is_empty() {
++LL | | panic!("panic3");
++LL | | }
++ | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/if_then_panic.rs:48:5
++ |
++LL | / if b.is_empty() || a.is_empty() {
++LL | | panic!("panic4");
++LL | | }
++ | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/if_then_panic.rs:51:5
++ |
++LL | / if a.is_empty() || !b.is_empty() {
++LL | | panic!("panic5");
++LL | | }
++ | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
++
++error: aborting due to 7 previous errors
+
--- /dev/null
++// edition:2018
+// aux-build:implicit_hasher_macros.rs
+#![deny(clippy::implicit_hasher)]
+#![allow(unused)]
+
+#[macro_use]
+extern crate implicit_hasher_macros;
+
+use std::cmp::Eq;
+use std::collections::{HashMap, HashSet};
+use std::hash::{BuildHasher, Hash};
+
+pub trait Foo<T>: Sized {
+ fn make() -> (Self, Self);
+}
+
+impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
+ fn make() -> (Self, Self) {
+ // OK, don't suggest to modify these
+ let _: HashMap<i32, i32> = HashMap::new();
+ let _: HashSet<i32> = HashSet::new();
+
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+}
+impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
+ fn make() -> (Self, Self) {
+ ((HashMap::new(),), (HashMap::with_capacity(10),))
+ }
+}
+impl Foo<i16> for HashMap<String, String> {
+ fn make() -> (Self, Self) {
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+}
+
+impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
+ fn make() -> (Self, Self) {
+ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+ }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
+ fn make() -> (Self, Self) {
+ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+ }
+}
+
+impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
+ fn make() -> (Self, Self) {
+ (HashSet::new(), HashSet::with_capacity(10))
+ }
+}
+impl Foo<i16> for HashSet<String> {
+ fn make() -> (Self, Self) {
+ (HashSet::new(), HashSet::with_capacity(10))
+ }
+}
+
+impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
+ fn make() -> (Self, Self) {
+ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+ }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
+ fn make() -> (Self, Self) {
+ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+ }
+}
+
+pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+
+macro_rules! gen {
+ (impl) => {
+ impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
+ fn make() -> (Self, Self) {
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+ }
+ };
+
+ (fn $name:ident) => {
+ pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ };
+}
+#[rustfmt::skip]
+gen!(impl);
+gen!(fn bar);
+
+// When the macro is in a different file, the suggestion spans can't be combined properly
+// and should not cause an ICE
+// See #2707
+#[macro_use]
+#[path = "auxiliary/test_macro.rs"]
+pub mod test_macro;
+__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
+
+// #4260
+implicit_hasher_fn!();
+
++// #7712
++pub async fn election_vote(_data: HashMap<i32, i32>) {}
++
+fn main() {}
--- /dev/null
- --> $DIR/implicit_hasher.rs:16:35
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:2:9
++ --> $DIR/implicit_hasher.rs:17:35
+ |
+LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
+ | ^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
- --> $DIR/implicit_hasher.rs:25:36
++ --> $DIR/implicit_hasher.rs:3:9
+ |
+LL | #![deny(clippy::implicit_hasher)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:30:19
++ --> $DIR/implicit_hasher.rs:26:36
+ |
+LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
+ | ^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:47:32
++ --> $DIR/implicit_hasher.rs:31:19
+ |
+LL | impl Foo<i16> for HashMap<String, String> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
+ | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:52:19
++ --> $DIR/implicit_hasher.rs:48:32
+ |
+LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
+ | ^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:69:23
++ --> $DIR/implicit_hasher.rs:53:19
+ |
+LL | impl Foo<i16> for HashSet<String> {
+ | ^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
+ | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:69:53
++ --> $DIR/implicit_hasher.rs:70:23
+ |
+LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:73:43
++ --> $DIR/implicit_hasher.rs:70:53
+ |
+LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:81:33
++ --> $DIR/implicit_hasher.rs:74:43
+ |
+LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
+ | ^^^^^^^^^^^^^
+...
+LL | gen!(impl);
+ | ----------- in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:81:63
++ --> $DIR/implicit_hasher.rs:82:33
+ |
+LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | gen!(fn bar);
+ | ------------- in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashSet` should be generalized over different hashers
- error: aborting due to 10 previous errors
++ --> $DIR/implicit_hasher.rs:82:63
+ |
+LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^
+...
+LL | gen!(fn bar);
+ | ------------- in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
+
++error: parameter of type `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:101:35
++ |
++LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
++ | ^^^^^^^^^^^^^^^^^
++ |
++help: consider adding a type parameter
++ |
++LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
++ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
++
++error: aborting due to 11 previous errors
+
--- /dev/null
- #![allow(
- unused,
- clippy::shadow_reuse,
- clippy::shadow_unrelated,
- clippy::no_effect,
- clippy::unnecessary_operation,
- clippy::op_ref
- )]
+#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref)]
+
+#[rustfmt::skip]
+fn main() {
+ let mut i = 1i32;
+ let mut var1 = 0i32;
+ let mut var2 = -1i32;
+ 1 + i;
+ i * 2;
+ 1 %
+ i / 2; // no error, this is part of the expression in the preceding line
+ i - 2 + 2 - i;
+ -i;
+ i >> 1;
+ i << 1;
+
+ // no error, overflows are checked by `overflowing_literals`
+ -1;
+ -(-1);
+
+ i & 1; // no wrapping
+ i | 1;
+ i ^ 1;
+
+ i += 1;
+ i -= 1;
+ i *= 2;
+ i /= 2;
+ i /= 0;
+ i /= -1;
+ i /= var1;
+ i /= var2;
+ i %= 2;
+ i %= 0;
+ i %= -1;
+ i %= var1;
+ i %= var2;
+ i <<= 3;
+ i >>= 2;
+
+ // no errors
+ i |= 1;
+ i &= 1;
+ i ^= i;
+
+ // No errors for the following items because they are constant expressions
+ enum Foo {
+ Bar = -2,
+ }
+ struct Baz([i32; 1 + 1]);
+ union Qux {
+ field: [i32; 1 + 1],
+ }
+ type Alias = [i32; 1 + 1];
+
+ const FOO: i32 = -2;
+ static BAR: i32 = -2;
+
+ let _: [i32; 1 + 1] = [0, 0];
+
+ let _: [i32; 1 + 1] = {
+ let a: [i32; 1 + 1] = [0, 0];
+ a
+ };
+
+ trait Trait {
+ const ASSOC: i32 = 1 + 1;
+ }
+
+ impl Trait for Foo {
+ const ASSOC: i32 = {
+ let _: [i32; 1 + 1];
+ fn foo() {}
+ 1 + 1
+ };
+ }
+}
+
+// warn on references as well! (#5328)
+pub fn int_arith_ref() {
+ 3 + &1;
+ &3 + 1;
+ &3 + &1;
+}
+
+pub fn foo(x: &i32) -> i32 {
+ let a = 5;
+ a + x
+}
+
+pub fn bar(x: &i32, y: &i32) -> i32 {
+ x + y
+}
+
+pub fn baz(x: i32, y: &i32) -> i32 {
+ x + y
+}
+
+pub fn qux(x: i32, y: i32) -> i32 {
+ (&x + &y)
+}
--- /dev/null
- --> $DIR/integer_arithmetic.rs:37:5
+error: this operation will panic at runtime
- --> $DIR/integer_arithmetic.rs:42:5
++ --> $DIR/integer_arithmetic.rs:30:5
+ |
+LL | i /= 0;
+ | ^^^^^^ attempt to divide `_` by zero
+ |
+ = note: `#[deny(unconditional_panic)]` on by default
+
+error: this operation will panic at runtime
- --> $DIR/integer_arithmetic.rs:16:5
++ --> $DIR/integer_arithmetic.rs:35:5
+ |
+LL | i %= 0;
+ | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:17:5
++ --> $DIR/integer_arithmetic.rs:9:5
+ |
+LL | 1 + i;
+ | ^^^^^
+ |
+ = note: `-D clippy::integer-arithmetic` implied by `-D warnings`
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:18:5
++ --> $DIR/integer_arithmetic.rs:10:5
+ |
+LL | i * 2;
+ | ^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:20:5
++ --> $DIR/integer_arithmetic.rs:11:5
+ |
+LL | / 1 %
+LL | | i / 2; // no error, this is part of the expression in the preceding line
+ | |_____^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:21:5
++ --> $DIR/integer_arithmetic.rs:13:5
+ |
+LL | i - 2 + 2 - i;
+ | ^^^^^^^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:22:5
++ --> $DIR/integer_arithmetic.rs:14:5
+ |
+LL | -i;
+ | ^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:23:5
++ --> $DIR/integer_arithmetic.rs:15:5
+ |
+LL | i >> 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:33:5
++ --> $DIR/integer_arithmetic.rs:16:5
+ |
+LL | i << 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:34:5
++ --> $DIR/integer_arithmetic.rs:26:5
+ |
+LL | i += 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:35:5
++ --> $DIR/integer_arithmetic.rs:27:5
+ |
+LL | i -= 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:38:11
++ --> $DIR/integer_arithmetic.rs:28:5
+ |
+LL | i *= 2;
+ | ^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:39:5
++ --> $DIR/integer_arithmetic.rs:31:11
+ |
+LL | i /= -1;
+ | ^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:40:5
++ --> $DIR/integer_arithmetic.rs:32:5
+ |
+LL | i /= var1;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:43:11
++ --> $DIR/integer_arithmetic.rs:33:5
+ |
+LL | i /= var2;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:44:5
++ --> $DIR/integer_arithmetic.rs:36:11
+ |
+LL | i %= -1;
+ | ^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:45:5
++ --> $DIR/integer_arithmetic.rs:37:5
+ |
+LL | i %= var1;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:46:5
++ --> $DIR/integer_arithmetic.rs:38:5
+ |
+LL | i %= var2;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:47:5
++ --> $DIR/integer_arithmetic.rs:39:5
+ |
+LL | i <<= 3;
+ | ^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:89:5
++ --> $DIR/integer_arithmetic.rs:40:5
+ |
+LL | i >>= 2;
+ | ^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:90:5
++ --> $DIR/integer_arithmetic.rs:82:5
+ |
+LL | 3 + &1;
+ | ^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:91:5
++ --> $DIR/integer_arithmetic.rs:83:5
+ |
+LL | &3 + 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:96:5
++ --> $DIR/integer_arithmetic.rs:84:5
+ |
+LL | &3 + &1;
+ | ^^^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:100:5
++ --> $DIR/integer_arithmetic.rs:89:5
+ |
+LL | a + x
+ | ^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:104:5
++ --> $DIR/integer_arithmetic.rs:93:5
+ |
+LL | x + y
+ | ^^^^^
+
+error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:108:5
++ --> $DIR/integer_arithmetic.rs:97:5
+ |
+LL | x + y
+ | ^^^^^
+
+error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:101:5
+ |
+LL | (&x + &y)
+ | ^^^^^^^^^
+
+error: aborting due to 27 previous errors
+
--- /dev/null
+// aux-build:macro_rules.rs
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![warn(clippy::large_enum_variant)]
+
+#[macro_use]
+extern crate macro_rules;
+
+enum LargeEnum {
+ A(i32),
+ B([i32; 8000]),
+}
+
+enum GenericEnumOk<T> {
+ A(i32),
+ B([T; 8000]),
+}
+
+enum GenericEnum2<T> {
+ A(i32),
+ B([i32; 8000]),
+ C(T, [i32; 8000]),
+}
+
+trait SomeTrait {
+ type Item;
+}
+
+enum LargeEnumGeneric<A: SomeTrait> {
+ Var(A::Item),
+}
+
+enum LargeEnum2 {
+ VariantOk(i32, u32),
+ ContainingLargeEnum(LargeEnum),
+}
++
+enum LargeEnum3 {
+ ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
+ VoidVariant,
+ StructLikeLittle { x: i32, y: i32 },
+}
+
+enum LargeEnum4 {
+ VariantOk(i32, u32),
+ StructLikeLarge { x: [i32; 8000], y: i32 },
+}
+
+enum LargeEnum5 {
+ VariantOk(i32, u32),
+ StructLikeLarge2 { x: [i32; 8000] },
+}
+
+enum LargeEnumOk {
+ LargeA([i32; 8000]),
+ LargeB([i32; 8001]),
+}
+
++enum LargeEnum6 {
++ A,
++ B([u8; 255]),
++ C([u8; 200]),
++}
++
++enum LargeEnum7 {
++ A,
++ B([u8; 1255]),
++ C([u8; 200]),
++}
++
++enum LargeEnum8 {
++ VariantOk(i32, u32),
++ ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
++}
++
+fn main() {
+ large_enum_variant!();
+}
--- /dev/null
- --> $DIR/large_enum_variant.rs:46:5
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:12:5
+ |
+LL | B([i32; 8000]),
+ | ^^^^^^^^^^^^^^ this variant is 32000 bytes
+ |
+ = note: `-D clippy::large-enum-variant` implied by `-D warnings`
+note: and the second-largest variant is 4 bytes:
+ --> $DIR/large_enum_variant.rs:11:5
+ |
+LL | A(i32),
+ | ^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | B(Box<[i32; 8000]>),
+ | ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:36:5
+ |
+LL | ContainingLargeEnum(LargeEnum),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
+ |
+note: and the second-largest variant is 8 bytes:
+ --> $DIR/large_enum_variant.rs:35:5
+ |
+LL | VariantOk(i32, u32),
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | ContainingLargeEnum(Box<LargeEnum>),
+ | ~~~~~~~~~~~~~~
+
+error: large size difference between variants
- --> $DIR/large_enum_variant.rs:45:5
++ --> $DIR/large_enum_variant.rs:40:5
++ |
++LL | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70004 bytes
++ |
++note: and the second-largest variant is 8 bytes:
++ --> $DIR/large_enum_variant.rs:42:5
++ |
++LL | StructLikeLittle { x: i32, y: i32 },
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++ |
++LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
++ | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
++
++error: large size difference between variants
++ --> $DIR/large_enum_variant.rs:47:5
+ |
+LL | StructLikeLarge { x: [i32; 8000], y: i32 },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
+ |
+note: and the second-largest variant is 8 bytes:
- --> $DIR/large_enum_variant.rs:46:5
++ --> $DIR/large_enum_variant.rs:46:5
+ |
+LL | VariantOk(i32, u32),
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
- LL | StructLikeLarge { x: [i32; 8000], y: i32 },
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
- --> $DIR/large_enum_variant.rs:51:5
++LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
++ | ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
- --> $DIR/large_enum_variant.rs:50:5
++ --> $DIR/large_enum_variant.rs:52:5
+ |
+LL | StructLikeLarge2 { x: [i32; 8000] },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
+ |
+note: and the second-largest variant is 8 bytes:
- error: aborting due to 4 previous errors
++ --> $DIR/large_enum_variant.rs:51:5
+ |
+LL | VariantOk(i32, u32),
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | StructLikeLarge2 { x: Box<[i32; 8000]> },
+ | ~~~~~~~~~~~~~~~~
+
++error: large size difference between variants
++ --> $DIR/large_enum_variant.rs:68:5
++ |
++LL | B([u8; 1255]),
++ | ^^^^^^^^^^^^^ this variant is 1255 bytes
++ |
++note: and the second-largest variant is 200 bytes:
++ --> $DIR/large_enum_variant.rs:69:5
++ |
++LL | C([u8; 200]),
++ | ^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++ |
++LL | B(Box<[u8; 1255]>),
++ | ~~~~~~~~~~~~~~~
++
++error: large size difference between variants
++ --> $DIR/large_enum_variant.rs:74:5
++ |
++LL | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70128 bytes
++ |
++note: and the second-largest variant is 8 bytes:
++ --> $DIR/large_enum_variant.rs:73:5
++ |
++LL | VariantOk(i32, u32),
++ | ^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++ |
++LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]),
++ | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
++
++error: aborting due to 7 previous errors
+
--- /dev/null
- #![warn(clippy::all, clippy::pedantic)]
- #![allow(clippy::iter_cloned_collect)]
- #![allow(clippy::clone_on_copy, clippy::redundant_clone)]
- #![allow(clippy::let_underscore_drop)]
- #![allow(clippy::missing_docs_in_private_items)]
- #![allow(clippy::redundant_closure_for_method_calls)]
- #![allow(clippy::many_single_char_names)]
+// run-rustfix
++#![warn(clippy::map_clone)]
++#![allow(
++ clippy::clone_on_copy,
++ clippy::iter_cloned_collect,
++ clippy::many_single_char_names,
++ clippy::redundant_clone
++)]
+
+fn main() {
+ let _: Vec<i8> = vec![5_i8; 6].iter().copied().collect();
+ let _: Vec<String> = vec![String::new()].iter().cloned().collect();
+ let _: Vec<u32> = vec![42, 43].iter().copied().collect();
+ let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
+ let _: Option<u64> = Some(&16).copied();
+ let _: Option<u8> = Some(&1).copied();
+
+ // Don't lint these
+ let v = vec![5_i8; 6];
+ let a = 0;
+ let b = &a;
+ let _ = v.iter().map(|_x| *b);
+ let _ = v.iter().map(|_x| a.clone());
+ let _ = v.iter().map(|&_x| a);
+
+ // Issue #498
+ let _ = std::env::args();
+
+ // Issue #4824 item types that aren't references
+ {
+ use std::rc::Rc;
+
+ let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
+ let _: Option<u32> = o.map(|x| *x);
+ let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ }
+
+ // Issue #5524 mutable references
+ {
+ let mut c = 42;
+ let v = vec![&mut c];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ let mut d = 21;
+ let v = vec![&mut d];
+ let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
+ }
+
+ // Issue #6299
+ {
+ let mut aa = 5;
+ let mut bb = 3;
+ let items = vec![&mut aa, &mut bb];
+ let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect();
+ }
+
+ // Issue #6239 deref coercion and clone deref
+ {
+ use std::cell::RefCell;
+
+ let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone());
+ }
+}
--- /dev/null
- #![warn(clippy::all, clippy::pedantic)]
- #![allow(clippy::iter_cloned_collect)]
- #![allow(clippy::clone_on_copy, clippy::redundant_clone)]
- #![allow(clippy::let_underscore_drop)]
- #![allow(clippy::missing_docs_in_private_items)]
- #![allow(clippy::redundant_closure_for_method_calls)]
- #![allow(clippy::many_single_char_names)]
+// run-rustfix
++#![warn(clippy::map_clone)]
++#![allow(
++ clippy::clone_on_copy,
++ clippy::iter_cloned_collect,
++ clippy::many_single_char_names,
++ clippy::redundant_clone
++)]
+
+fn main() {
+ let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
+ let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
+ let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
+ let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
+ let _: Option<u64> = Some(&16).map(|b| *b);
+ let _: Option<u8> = Some(&1).map(|x| x.clone());
+
+ // Don't lint these
+ let v = vec![5_i8; 6];
+ let a = 0;
+ let b = &a;
+ let _ = v.iter().map(|_x| *b);
+ let _ = v.iter().map(|_x| a.clone());
+ let _ = v.iter().map(|&_x| a);
+
+ // Issue #498
+ let _ = std::env::args().map(|v| v.clone());
+
+ // Issue #4824 item types that aren't references
+ {
+ use std::rc::Rc;
+
+ let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
+ let _: Option<u32> = o.map(|x| *x);
+ let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ }
+
+ // Issue #5524 mutable references
+ {
+ let mut c = 42;
+ let v = vec![&mut c];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ let mut d = 21;
+ let v = vec![&mut d];
+ let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
+ }
+
+ // Issue #6299
+ {
+ let mut aa = 5;
+ let mut bb = 3;
+ let items = vec![&mut aa, &mut bb];
+ let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect();
+ }
+
+ // Issue #6239 deref coercion and clone deref
+ {
+ use std::cell::RefCell;
+
+ let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone());
+ }
+}
--- /dev/null
- #![allow(unreachable_patterns, dead_code)]
+// run-rustfix
+
+#![warn(clippy::match_like_matches_macro)]
++#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
+
+fn main() {
+ let x = Some(5);
+
+ // Lint
+ let _y = matches!(x, Some(0));
+
+ // Lint
+ let _w = matches!(x, Some(_));
+
+ // Turn into is_none
+ let _z = x.is_none();
+
+ // Lint
+ let _zz = !matches!(x, Some(r) if r == 0);
+
+ // Lint
+ let _zzz = matches!(x, Some(5));
+
+ // No lint
+ let _a = match x {
+ Some(_) => false,
+ _ => false,
+ };
+
+ // No lint
+ let _ab = match x {
+ Some(0) => false,
+ _ => true,
+ None => false,
+ };
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ }
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = matches!(x, E::A(_) | E::B(_));
+ }
+ {
+ // lint
+ let _ans = !matches!(x, E::B(_) | E::C);
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+
+ {
+ // should print "z" in suggestion (#6503)
+ let z = &Some(3);
+ let _z = matches!(z, Some(3));
+ }
+
+ {
+ // this could also print "z" in suggestion..?
+ let z = Some(3);
+ let _z = matches!(&z, Some(3));
+ }
+
+ {
+ enum AnEnum {
+ X,
+ Y,
+ }
+
+ fn foo(_x: AnEnum) {}
+
+ fn main() {
+ let z = AnEnum::X;
+ // we can't remove the reference here!
+ let _ = matches!(&z, AnEnum::X);
+ foo(z);
+ }
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ // we need the reference here because later val is consumed by fun()
+ let _res = matches!(&val, &Some(ref _a));
+ fun(val);
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ let _res = matches!(&val, &Some(ref _a));
+ fun(val);
+ }
+}
--- /dev/null
- #![allow(unreachable_patterns, dead_code)]
+// run-rustfix
+
+#![warn(clippy::match_like_matches_macro)]
++#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
+
+fn main() {
+ let x = Some(5);
+
+ // Lint
+ let _y = match x {
+ Some(0) => true,
+ _ => false,
+ };
+
+ // Lint
+ let _w = match x {
+ Some(_) => true,
+ _ => false,
+ };
+
+ // Turn into is_none
+ let _z = match x {
+ Some(_) => false,
+ None => true,
+ };
+
+ // Lint
+ let _zz = match x {
+ Some(r) if r == 0 => false,
+ _ => true,
+ };
+
+ // Lint
+ let _zzz = if let Some(5) = x { true } else { false };
+
+ // No lint
+ let _a = match x {
+ Some(_) => false,
+ _ => false,
+ };
+
+ // No lint
+ let _ab = match x {
+ Some(0) => false,
+ _ => true,
+ None => false,
+ };
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ }
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+ {
+ // lint
+ let _ans = match x {
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+
+ {
+ // should print "z" in suggestion (#6503)
+ let z = &Some(3);
+ let _z = match &z {
+ Some(3) => true,
+ _ => false,
+ };
+ }
+
+ {
+ // this could also print "z" in suggestion..?
+ let z = Some(3);
+ let _z = match &z {
+ Some(3) => true,
+ _ => false,
+ };
+ }
+
+ {
+ enum AnEnum {
+ X,
+ Y,
+ }
+
+ fn foo(_x: AnEnum) {}
+
+ fn main() {
+ let z = AnEnum::X;
+ // we can't remove the reference here!
+ let _ = match &z {
+ AnEnum::X => true,
+ _ => false,
+ };
+ foo(z);
+ }
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ // we need the reference here because later val is consumed by fun()
+ let _res = match &val {
+ &Some(ref _a) => true,
+ _ => false,
+ };
+ fun(val);
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ let _res = match &val {
+ &Some(ref _a) => true,
+ _ => false,
+ };
+ fun(val);
+ }
+}
--- /dev/null
- #![allow(clippy::if_same_then_else)]
+#![feature(exclusive_range_pattern)]
+#![feature(half_open_range_patterns)]
+#![warn(clippy::match_overlapping_arm)]
+#![allow(clippy::redundant_pattern_matching)]
++#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+
+/// Tests for match_overlapping_arm
+
+fn overlapping() {
+ const FOO: u64 = 2;
+
+ match 42 {
+ 0..=10 => println!("0 ... 10"),
+ 0..=11 => println!("0 ... 11"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..=5 => println!("0 ... 5"),
+ 6..=7 => println!("6 ... 7"),
+ FOO..=11 => println!("0 ... 11"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..=5 => println!("0 ... 5"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..=2 => println!("0 ... 2"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..=10 => println!("0 ... 10"),
+ 11..=50 => println!("11 ... 50"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..2 => println!("0 .. 2"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..10 => println!("0 .. 10"),
+ 10..50 => println!("10 .. 50"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..11 => println!("0 .. 11"),
+ 0..=11 => println!("0 ... 11"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..7 => println!("5 .. 7"),
+ 0..10 => println!("0 .. 10"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..10 => println!("5 .. 10"),
+ 0..=10 => println!("0 ... 10"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..14 => println!("0 .. 14"),
+ 5..10 => println!("5 .. 10"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..14 => println!("5 .. 14"),
+ 0..=10 => println!("0 ... 10"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..7 => println!("0 .. 7"),
+ 0..=10 => println!("0 ... 10"),
+ _ => (),
+ }
+
+ /*
+ // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns
+ match 42 {
+ 0.. => println!("0 .. 42"),
+ 3.. => println!("3 .. 42"),
+ _ => (),
+ }
+
+ match 42 {
+ ..=23 => println!("0 ... 23"),
+ ..26 => println!("0 .. 26"),
+ _ => (),
+ }
+ */
+
+ if let None = Some(42) {
+ // nothing
+ } else if let None = Some(42) {
+ // another nothing :-)
+ }
+}
+
+fn main() {}
--- /dev/null
+#![warn(clippy::match_ref_pats)]
++#![allow(clippy::equatable_if_let)]
+
+fn ref_pats() {
+ {
+ let v = &Some(0);
+ match v {
+ &Some(v) => println!("{:?}", v),
+ &None => println!("none"),
+ }
+ match v {
+ // This doesn't trigger; we have a different pattern.
+ &Some(v) => println!("some"),
+ other => println!("other"),
+ }
+ }
+ let tup = &(1, 2);
+ match tup {
+ &(v, 1) => println!("{}", v),
+ _ => println!("none"),
+ }
+ // Special case: using `&` both in expr and pats.
+ let w = Some(0);
+ match &w {
+ &Some(v) => println!("{:?}", v),
+ &None => println!("none"),
+ }
+ // False positive: only wildcard pattern.
+ let w = Some(0);
+ #[allow(clippy::match_single_binding)]
+ match w {
+ _ => println!("none"),
+ }
+
+ let a = &Some(0);
+ if let &None = a {
+ println!("none");
+ }
+
+ let b = Some(0);
+ if let &None = &b {
+ println!("none");
+ }
+}
+
+mod ice_3719 {
+ macro_rules! foo_variant(
+ ($idx:expr) => (Foo::get($idx).unwrap())
+ );
+
+ enum Foo {
+ A,
+ B,
+ }
+
+ impl Foo {
+ fn get(idx: u8) -> Option<&'static Self> {
+ match idx {
+ 0 => Some(&Foo::A),
+ 1 => Some(&Foo::B),
+ _ => None,
+ }
+ }
+ }
+
+ fn ice_3719() {
+ // ICE #3719
+ match foo_variant!(0) {
+ &Foo::A => println!("A"),
+ _ => println!("Wild"),
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/match_ref_pats.rs:6:9
+error: you don't need to add `&` to all patterns
- --> $DIR/match_ref_pats.rs:17:5
++ --> $DIR/match_ref_pats.rs:7:9
+ |
+LL | / match v {
+LL | | &Some(v) => println!("{:?}", v),
+LL | | &None => println!("none"),
+LL | | }
+ | |_________^
+ |
+ = note: `-D clippy::match-ref-pats` implied by `-D warnings`
+help: instead of prefixing all patterns with `&`, you can dereference the expression
+ |
+LL ~ match *v {
+LL ~ Some(v) => println!("{:?}", v),
+LL ~ None => println!("none"),
+ |
+
+error: you don't need to add `&` to all patterns
- --> $DIR/match_ref_pats.rs:23:5
++ --> $DIR/match_ref_pats.rs:18:5
+ |
+LL | / match tup {
+LL | | &(v, 1) => println!("{}", v),
+LL | | _ => println!("none"),
+LL | | }
+ | |_____^
+ |
+help: instead of prefixing all patterns with `&`, you can dereference the expression
+ |
+LL ~ match *tup {
+LL ~ (v, 1) => println!("{}", v),
+ |
+
+error: you don't need to add `&` to both the expression and the patterns
- --> $DIR/match_ref_pats.rs:35:12
++ --> $DIR/match_ref_pats.rs:24:5
+ |
+LL | / match &w {
+LL | | &Some(v) => println!("{:?}", v),
+LL | | &None => println!("none"),
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ match w {
+LL ~ Some(v) => println!("{:?}", v),
+LL ~ None => println!("none"),
+ |
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/match_ref_pats.rs:35:5
++ --> $DIR/match_ref_pats.rs:36:12
+ |
+LL | if let &None = a {
+ | -------^^^^^---- help: try this: `if a.is_none()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: you don't need to add `&` to all patterns
- --> $DIR/match_ref_pats.rs:40:12
++ --> $DIR/match_ref_pats.rs:36:5
+ |
+LL | / if let &None = a {
+LL | | println!("none");
+LL | | }
+ | |_____^
+ |
+help: instead of prefixing all patterns with `&`, you can dereference the expression
+ |
+LL | if let None = *a {
+ | ~~~~ ~~
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/match_ref_pats.rs:40:5
++ --> $DIR/match_ref_pats.rs:41:12
+ |
+LL | if let &None = &b {
+ | -------^^^^^----- help: try this: `if b.is_none()`
+
+error: you don't need to add `&` to both the expression and the patterns
- --> $DIR/match_ref_pats.rs:67:9
++ --> $DIR/match_ref_pats.rs:41:5
+ |
+LL | / if let &None = &b {
+LL | | println!("none");
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL | if let None = b {
+ | ~~~~ ~
+
+error: you don't need to add `&` to all patterns
++ --> $DIR/match_ref_pats.rs:68:9
+ |
+LL | / match foo_variant!(0) {
+LL | | &Foo::A => println!("A"),
+LL | | _ => println!("Wild"),
+LL | | }
+ | |_________^
+ |
+help: instead of prefixing all patterns with `&`, you can dereference the expression
+ |
+LL ~ match *foo_variant!(0) {
+LL ~ Foo::A => println!("A"),
+ |
+
+error: aborting due to 8 previous errors
+
--- /dev/null
- #![allow(
- unused,
- clippy::shadow_reuse,
- clippy::shadow_unrelated,
- clippy::no_effect,
- clippy::unnecessary_operation,
- clippy::modulo_one
- )]
+#![warn(clippy::modulo_arithmetic)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)]
+
+fn main() {
+ // Lint when both sides are const and of the opposite sign
+ -1.6 % 2.1;
+ 1.6 % -2.1;
+ (1.1 - 2.3) % (1.1 + 2.3);
+ (1.1 + 2.3) % (1.1 - 2.3);
+
+ // Lint on floating point numbers
+ let a_f32: f32 = -1.6;
+ let mut b_f32: f32 = 2.1;
+ a_f32 % b_f32;
+ b_f32 % a_f32;
+ b_f32 %= a_f32;
+
+ let a_f64: f64 = -1.6;
+ let mut b_f64: f64 = 2.1;
+ a_f64 % b_f64;
+ b_f64 % a_f64;
+ b_f64 %= a_f64;
+
+ // No lint when both sides are const and of the same sign
+ 1.6 % 2.1;
+ -1.6 % -2.1;
+ (1.1 + 2.3) % (-1.1 + 2.3);
+ (-1.1 - 2.3) % (1.1 - 2.3);
+}
--- /dev/null
- --> $DIR/modulo_arithmetic_float.rs:13:5
+error: you are using modulo operator on constants with different signs: `-1.600 % 2.100`
- --> $DIR/modulo_arithmetic_float.rs:14:5
++ --> $DIR/modulo_arithmetic_float.rs:6:5
+ |
+LL | -1.6 % 2.1;
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on constants with different signs: `1.600 % -2.100`
- --> $DIR/modulo_arithmetic_float.rs:15:5
++ --> $DIR/modulo_arithmetic_float.rs:7:5
+ |
+LL | 1.6 % -2.1;
+ | ^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on constants with different signs: `-1.200 % 3.400`
- --> $DIR/modulo_arithmetic_float.rs:16:5
++ --> $DIR/modulo_arithmetic_float.rs:8:5
+ |
+LL | (1.1 - 2.3) % (1.1 + 2.3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on constants with different signs: `3.400 % -1.200`
- --> $DIR/modulo_arithmetic_float.rs:21:5
++ --> $DIR/modulo_arithmetic_float.rs:9:5
+ |
+LL | (1.1 + 2.3) % (1.1 - 2.3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_float.rs:22:5
++ --> $DIR/modulo_arithmetic_float.rs:14:5
+ |
+LL | a_f32 % b_f32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_float.rs:23:5
++ --> $DIR/modulo_arithmetic_float.rs:15:5
+ |
+LL | b_f32 % a_f32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_float.rs:27:5
++ --> $DIR/modulo_arithmetic_float.rs:16:5
+ |
+LL | b_f32 %= a_f32;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_float.rs:28:5
++ --> $DIR/modulo_arithmetic_float.rs:20:5
+ |
+LL | a_f64 % b_f64;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_float.rs:29:5
++ --> $DIR/modulo_arithmetic_float.rs:21:5
+ |
+LL | b_f64 % a_f64;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_float.rs:22:5
+ |
+LL | b_f64 %= a_f64;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: aborting due to 10 previous errors
+
--- /dev/null
- #![allow(
- unused,
- clippy::shadow_reuse,
- clippy::shadow_unrelated,
- clippy::no_effect,
- clippy::unnecessary_operation,
- clippy::modulo_one
- )]
+#![warn(clippy::modulo_arithmetic)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)]
+
+fn main() {
+ // Lint on signed integral numbers
+ let a = -1;
+ let mut b = 2;
+ a % b;
+ b % a;
+ b %= a;
+
+ let a_i8: i8 = 1;
+ let mut b_i8: i8 = 2;
+ a_i8 % b_i8;
+ b_i8 %= a_i8;
+
+ let a_i16: i16 = 1;
+ let mut b_i16: i16 = 2;
+ a_i16 % b_i16;
+ b_i16 %= a_i16;
+
+ let a_i32: i32 = 1;
+ let mut b_i32: i32 = 2;
+ a_i32 % b_i32;
+ b_i32 %= a_i32;
+
+ let a_i64: i64 = 1;
+ let mut b_i64: i64 = 2;
+ a_i64 % b_i64;
+ b_i64 %= a_i64;
+
+ let a_i128: i128 = 1;
+ let mut b_i128: i128 = 2;
+ a_i128 % b_i128;
+ b_i128 %= a_i128;
+
+ let a_isize: isize = 1;
+ let mut b_isize: isize = 2;
+ a_isize % b_isize;
+ b_isize %= a_isize;
+
+ let a = 1;
+ let mut b = 2;
+ a % b;
+ b %= a;
+
+ // No lint on unsigned integral value
+ let a_u8: u8 = 17;
+ let b_u8: u8 = 3;
+ a_u8 % b_u8;
+ let mut a_u8: u8 = 1;
+ a_u8 %= 2;
+
+ let a_u16: u16 = 17;
+ let b_u16: u16 = 3;
+ a_u16 % b_u16;
+ let mut a_u16: u16 = 1;
+ a_u16 %= 2;
+
+ let a_u32: u32 = 17;
+ let b_u32: u32 = 3;
+ a_u32 % b_u32;
+ let mut a_u32: u32 = 1;
+ a_u32 %= 2;
+
+ let a_u64: u64 = 17;
+ let b_u64: u64 = 3;
+ a_u64 % b_u64;
+ let mut a_u64: u64 = 1;
+ a_u64 %= 2;
+
+ let a_u128: u128 = 17;
+ let b_u128: u128 = 3;
+ a_u128 % b_u128;
+ let mut a_u128: u128 = 1;
+ a_u128 %= 2;
+
+ let a_usize: usize = 17;
+ let b_usize: usize = 3;
+ a_usize % b_usize;
+ let mut a_usize: usize = 1;
+ a_usize %= 2;
+}
--- /dev/null
- --> $DIR/modulo_arithmetic_integral.rs:15:5
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:16:5
++ --> $DIR/modulo_arithmetic_integral.rs:8:5
+ |
+LL | a % b;
+ | ^^^^^
+ |
+ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:17:5
++ --> $DIR/modulo_arithmetic_integral.rs:9:5
+ |
+LL | b % a;
+ | ^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:21:5
++ --> $DIR/modulo_arithmetic_integral.rs:10:5
+ |
+LL | b %= a;
+ | ^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:22:5
++ --> $DIR/modulo_arithmetic_integral.rs:14:5
+ |
+LL | a_i8 % b_i8;
+ | ^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:26:5
++ --> $DIR/modulo_arithmetic_integral.rs:15:5
+ |
+LL | b_i8 %= a_i8;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:27:5
++ --> $DIR/modulo_arithmetic_integral.rs:19:5
+ |
+LL | a_i16 % b_i16;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:31:5
++ --> $DIR/modulo_arithmetic_integral.rs:20:5
+ |
+LL | b_i16 %= a_i16;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:32:5
++ --> $DIR/modulo_arithmetic_integral.rs:24:5
+ |
+LL | a_i32 % b_i32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:36:5
++ --> $DIR/modulo_arithmetic_integral.rs:25:5
+ |
+LL | b_i32 %= a_i32;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:37:5
++ --> $DIR/modulo_arithmetic_integral.rs:29:5
+ |
+LL | a_i64 % b_i64;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:41:5
++ --> $DIR/modulo_arithmetic_integral.rs:30:5
+ |
+LL | b_i64 %= a_i64;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:42:5
++ --> $DIR/modulo_arithmetic_integral.rs:34:5
+ |
+LL | a_i128 % b_i128;
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:46:5
++ --> $DIR/modulo_arithmetic_integral.rs:35:5
+ |
+LL | b_i128 %= a_i128;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:47:5
++ --> $DIR/modulo_arithmetic_integral.rs:39:5
+ |
+LL | a_isize % b_isize;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:51:5
++ --> $DIR/modulo_arithmetic_integral.rs:40:5
+ |
+LL | b_isize %= a_isize;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
- --> $DIR/modulo_arithmetic_integral.rs:52:5
++ --> $DIR/modulo_arithmetic_integral.rs:44:5
+ |
+LL | a % b;
+ | ^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:45:5
+ |
+LL | b %= a;
+ | ^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: aborting due to 17 previous errors
+
--- /dev/null
- #![allow(
- unused,
- clippy::shadow_reuse,
- clippy::shadow_unrelated,
- clippy::no_effect,
- clippy::unnecessary_operation,
- clippy::modulo_one
- )]
+#![warn(clippy::modulo_arithmetic)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)]
+
+fn main() {
+ // Lint when both sides are const and of the opposite sign
+ -1 % 2;
+ 1 % -2;
+ (1 - 2) % (1 + 2);
+ (1 + 2) % (1 - 2);
+ 35 * (7 - 4 * 2) % (-500 * -600);
+
+ -1i8 % 2i8;
+ 1i8 % -2i8;
+ -1i16 % 2i16;
+ 1i16 % -2i16;
+ -1i32 % 2i32;
+ 1i32 % -2i32;
+ -1i64 % 2i64;
+ 1i64 % -2i64;
+ -1i128 % 2i128;
+ 1i128 % -2i128;
+ -1isize % 2isize;
+ 1isize % -2isize;
+
+ // No lint when both sides are const and of the same sign
+ 1 % 2;
+ -1 % -2;
+ (1 + 2) % (-1 + 2);
+ (-1 - 2) % (1 - 2);
+
+ 1u8 % 2u8;
+ 1u16 % 2u16;
+ 1u32 % 2u32;
+ 1u64 % 2u64;
+ 1u128 % 2u128;
+ 1usize % 2usize;
+}
--- /dev/null
- --> $DIR/modulo_arithmetic_integral_const.rs:13:5
+error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:14:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:6:5
+ |
+LL | -1 % 2;
+ | ^^^^^^
+ |
+ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
- --> $DIR/modulo_arithmetic_integral_const.rs:15:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:7:5
+ |
+LL | 1 % -2;
+ | ^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 3`
- --> $DIR/modulo_arithmetic_integral_const.rs:16:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:8:5
+ |
+LL | (1 - 2) % (1 + 2);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `3 % -1`
- --> $DIR/modulo_arithmetic_integral_const.rs:17:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:9:5
+ |
+LL | (1 + 2) % (1 - 2);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-35 % 300000`
- --> $DIR/modulo_arithmetic_integral_const.rs:19:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:10:5
+ |
+LL | 35 * (7 - 4 * 2) % (-500 * -600);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:20:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:12:5
+ |
+LL | -1i8 % 2i8;
+ | ^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
- --> $DIR/modulo_arithmetic_integral_const.rs:21:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:13:5
+ |
+LL | 1i8 % -2i8;
+ | ^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:22:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:14:5
+ |
+LL | -1i16 % 2i16;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
- --> $DIR/modulo_arithmetic_integral_const.rs:23:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:15:5
+ |
+LL | 1i16 % -2i16;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:24:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:16:5
+ |
+LL | -1i32 % 2i32;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
- --> $DIR/modulo_arithmetic_integral_const.rs:25:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:17:5
+ |
+LL | 1i32 % -2i32;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:26:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:18:5
+ |
+LL | -1i64 % 2i64;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
- --> $DIR/modulo_arithmetic_integral_const.rs:27:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:19:5
+ |
+LL | 1i64 % -2i64;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:28:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:20:5
+ |
+LL | -1i128 % 2i128;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
- --> $DIR/modulo_arithmetic_integral_const.rs:29:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:21:5
+ |
+LL | 1i128 % -2i128;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:30:5
++ --> $DIR/modulo_arithmetic_integral_const.rs:22:5
+ |
+LL | -1isize % 2isize;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:23:5
+ |
+LL | 1isize % -2isize;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: aborting due to 17 previous errors
+
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::needless_bool)]
+#![allow(
+ unused,
+ dead_code,
+ clippy::no_effect,
+ clippy::if_same_then_else,
++ clippy::equatable_if_let,
+ clippy::needless_return,
+ clippy::self_named_constructors
+)]
+
+use std::cell::Cell;
+
+macro_rules! bool_comparison_trigger {
+ ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => (
+
+ #[derive(Clone)]
+ pub struct Trigger {
+ $($i: (Cell<bool>, bool, bool)),+
+ }
+
+ #[allow(dead_code)]
+ impl Trigger {
+ pub fn trigger(&self, key: &str) -> bool {
+ $(
+ if let stringify!($i) = key {
+ return self.$i.1 && self.$i.2 == $def;
+ }
+ )+
+ false
+ }
+ }
+ )
+}
+
+fn main() {
+ let x = true;
+ let y = false;
+ x;
+ !x;
+ !(x && y);
+ if x {
+ x
+ } else {
+ false
+ }; // would also be questionable, but we don't catch this yet
+ bool_ret3(x);
+ bool_ret4(x);
+ bool_ret5(x, x);
+ bool_ret6(x, x);
+ needless_bool(x);
+ needless_bool2(x);
+ needless_bool3(x);
+}
+
+fn bool_ret3(x: bool) -> bool {
+ return x;
+}
+
+fn bool_ret4(x: bool) -> bool {
+ return !x;
+}
+
+fn bool_ret5(x: bool, y: bool) -> bool {
+ return x && y;
+}
+
+fn bool_ret6(x: bool, y: bool) -> bool {
+ return !(x && y);
+}
+
+fn needless_bool(x: bool) {
+ if x {};
+}
+
+fn needless_bool2(x: bool) {
+ if !x {};
+}
+
+fn needless_bool3(x: bool) {
+ bool_comparison_trigger! {
+ test_one: false, false;
+ test_three: false, false;
+ test_two: true, true;
+ }
+
+ if x {};
+ if !x {};
+}
+
+fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
+ let b = false;
+ let returns_bool = || false;
+
+ let x = if b {
+ true
+ } else { !returns_bool() };
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::needless_bool)]
+#![allow(
+ unused,
+ dead_code,
+ clippy::no_effect,
+ clippy::if_same_then_else,
++ clippy::equatable_if_let,
+ clippy::needless_return,
+ clippy::self_named_constructors
+)]
+
+use std::cell::Cell;
+
+macro_rules! bool_comparison_trigger {
+ ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => (
+
+ #[derive(Clone)]
+ pub struct Trigger {
+ $($i: (Cell<bool>, bool, bool)),+
+ }
+
+ #[allow(dead_code)]
+ impl Trigger {
+ pub fn trigger(&self, key: &str) -> bool {
+ $(
+ if let stringify!($i) = key {
+ return self.$i.1 && self.$i.2 == $def;
+ }
+ )+
+ false
+ }
+ }
+ )
+}
+
+fn main() {
+ let x = true;
+ let y = false;
+ if x {
+ true
+ } else {
+ false
+ };
+ if x {
+ false
+ } else {
+ true
+ };
+ if x && y {
+ false
+ } else {
+ true
+ };
+ if x {
+ x
+ } else {
+ false
+ }; // would also be questionable, but we don't catch this yet
+ bool_ret3(x);
+ bool_ret4(x);
+ bool_ret5(x, x);
+ bool_ret6(x, x);
+ needless_bool(x);
+ needless_bool2(x);
+ needless_bool3(x);
+}
+
+fn bool_ret3(x: bool) -> bool {
+ if x {
+ return true;
+ } else {
+ return false;
+ };
+}
+
+fn bool_ret4(x: bool) -> bool {
+ if x {
+ return false;
+ } else {
+ return true;
+ };
+}
+
+fn bool_ret5(x: bool, y: bool) -> bool {
+ if x && y {
+ return true;
+ } else {
+ return false;
+ };
+}
+
+fn bool_ret6(x: bool, y: bool) -> bool {
+ if x && y {
+ return false;
+ } else {
+ return true;
+ };
+}
+
+fn needless_bool(x: bool) {
+ if x == true {};
+}
+
+fn needless_bool2(x: bool) {
+ if x == false {};
+}
+
+fn needless_bool3(x: bool) {
+ bool_comparison_trigger! {
+ test_one: false, false;
+ test_three: false, false;
+ test_two: true, true;
+ }
+
+ if x == true {};
+ if x == false {};
+}
+
+fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
+ let b = false;
+ let returns_bool = || false;
+
+ let x = if b {
+ true
+ } else if returns_bool() {
+ false
+ } else {
+ true
+ };
+}
--- /dev/null
- --> $DIR/fixable.rs:40:5
+error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:45:5
++ --> $DIR/fixable.rs:41:5
+ |
+LL | / if x {
+LL | | true
+LL | | } else {
+LL | | false
+LL | | };
+ | |_____^ help: you can reduce it to: `x`
+ |
+ = note: `-D clippy::needless-bool` implied by `-D warnings`
+
+error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:50:5
++ --> $DIR/fixable.rs:46:5
+ |
+LL | / if x {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `!x`
+
+error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:70:5
++ --> $DIR/fixable.rs:51:5
+ |
+LL | / if x && y {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `!(x && y)`
+
+error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:78:5
++ --> $DIR/fixable.rs:71:5
+ |
+LL | / if x {
+LL | | return true;
+LL | | } else {
+LL | | return false;
+LL | | };
+ | |_____^ help: you can reduce it to: `return x`
+
+error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:86:5
++ --> $DIR/fixable.rs:79:5
+ |
+LL | / if x {
+LL | | return false;
+LL | | } else {
+LL | | return true;
+LL | | };
+ | |_____^ help: you can reduce it to: `return !x`
+
+error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:94:5
++ --> $DIR/fixable.rs:87:5
+ |
+LL | / if x && y {
+LL | | return true;
+LL | | } else {
+LL | | return false;
+LL | | };
+ | |_____^ help: you can reduce it to: `return x && y`
+
+error: this if-then-else expression returns a bool literal
- --> $DIR/fixable.rs:102:8
++ --> $DIR/fixable.rs:95:5
+ |
+LL | / if x && y {
+LL | | return false;
+LL | | } else {
+LL | | return true;
+LL | | };
+ | |_____^ help: you can reduce it to: `return !(x && y)`
+
+error: equality checks against true are unnecessary
- --> $DIR/fixable.rs:106:8
++ --> $DIR/fixable.rs:103:8
+ |
+LL | if x == true {};
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+ |
+ = note: `-D clippy::bool-comparison` implied by `-D warnings`
+
+error: equality checks against false can be replaced by a negation
- --> $DIR/fixable.rs:116:8
++ --> $DIR/fixable.rs:107:8
+ |
+LL | if x == false {};
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: equality checks against true are unnecessary
- --> $DIR/fixable.rs:117:8
++ --> $DIR/fixable.rs:117:8
+ |
+LL | if x == true {};
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+
+error: equality checks against false can be replaced by a negation
- --> $DIR/fixable.rs:126:12
++ --> $DIR/fixable.rs:118:8
+ |
+LL | if x == false {};
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:127:12
+ |
+LL | } else if returns_bool() {
+ | ____________^
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `{ !returns_bool() }`
+
+error: aborting due to 12 previous errors
+
--- /dev/null
- #![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
+// run-rustfix
+// edition:2018
+
+#![feature(let_else)]
+#![allow(unused)]
++#![allow(
++ clippy::if_same_then_else,
++ clippy::single_match,
++ clippy::needless_bool,
++ clippy::equatable_if_let
++)]
+#![warn(clippy::needless_return)]
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+fn test_no_semicolon() -> bool {
+ true
+}
+
+fn test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+fn test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+fn test_void_fun() {
+
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+
+ } else {
+
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => {},
+ }
+}
+
+fn read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| {})
+ }
+
+ fn test_closure() {
+ let _ = || {
+
+ };
+ let _ = || {};
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+async fn async_test_no_semicolon() -> bool {
+ true
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+
+ } else {
+
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => {},
+ }
+}
+
+async fn async_read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn main() {}
--- /dev/null
- #![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
+// run-rustfix
+// edition:2018
+
+#![feature(let_else)]
+#![allow(unused)]
++#![allow(
++ clippy::if_same_then_else,
++ clippy::single_match,
++ clippy::needless_bool,
++ clippy::equatable_if_let
++)]
+#![warn(clippy::needless_return)]
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+fn test_no_semicolon() -> bool {
+ return true;
+}
+
+fn test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+fn test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+fn test_void_fun() {
+ return;
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+fn read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| return)
+ }
+
+ fn test_closure() {
+ let _ = || {
+ return;
+ };
+ let _ = || return;
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| return Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+async fn async_test_no_semicolon() -> bool {
+ return true;
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+ return;
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+async fn async_read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/needless_return.rs:20:5
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:24:5
++ --> $DIR/needless_return.rs:25:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+ |
+ = note: `-D clippy::needless-return` implied by `-D warnings`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:29:9
++ --> $DIR/needless_return.rs:29:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:31:9
++ --> $DIR/needless_return.rs:34:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:37:17
++ --> $DIR/needless_return.rs:36:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:39:13
++ --> $DIR/needless_return.rs:42:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:46:9
++ --> $DIR/needless_return.rs:44:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:48:16
++ --> $DIR/needless_return.rs:51:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:56:5
++ --> $DIR/needless_return.rs:53:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:61:9
++ --> $DIR/needless_return.rs:61:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:63:9
++ --> $DIR/needless_return.rs:66:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:70:14
++ --> $DIR/needless_return.rs:68:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:85:9
++ --> $DIR/needless_return.rs:75:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:87:9
++ --> $DIR/needless_return.rs:90:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:108:32
++ --> $DIR/needless_return.rs:92:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:113:13
++ --> $DIR/needless_return.rs:113:32
+ |
+LL | bar.unwrap_or_else(|_| return)
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:115:20
++ --> $DIR/needless_return.rs:118:13
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:121:32
++ --> $DIR/needless_return.rs:120:20
+ |
+LL | let _ = || return;
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:130:5
++ --> $DIR/needless_return.rs:126:32
+ |
+LL | res.unwrap_or_else(|_| return Foo)
+ | ^^^^^^^^^^ help: remove `return`: `Foo`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:134:5
++ --> $DIR/needless_return.rs:135:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:139:9
++ --> $DIR/needless_return.rs:139:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:141:9
++ --> $DIR/needless_return.rs:144:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:147:17
++ --> $DIR/needless_return.rs:146:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:149:13
++ --> $DIR/needless_return.rs:152:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:156:9
++ --> $DIR/needless_return.rs:154:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:158:16
++ --> $DIR/needless_return.rs:161:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:166:5
++ --> $DIR/needless_return.rs:163:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:171:9
++ --> $DIR/needless_return.rs:171:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:173:9
++ --> $DIR/needless_return.rs:176:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:180:14
++ --> $DIR/needless_return.rs:178:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:195:9
++ --> $DIR/needless_return.rs:185:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:197:9
++ --> $DIR/needless_return.rs:200:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
++ --> $DIR/needless_return.rs:202:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: aborting due to 32 previous errors
+
--- /dev/null
--- /dev/null
++#![warn(clippy::non_send_fields_in_send_ty)]
++#![feature(extern_types)]
++
++use std::cell::UnsafeCell;
++use std::ptr::NonNull;
++use std::rc::Rc;
++use std::sync::{Arc, Mutex, MutexGuard};
++
++// disrustor / RUSTSEC-2020-0150
++pub struct RingBuffer<T> {
++ data: Vec<UnsafeCell<T>>,
++ capacity: usize,
++ mask: usize,
++}
++
++unsafe impl<T> Send for RingBuffer<T> {}
++
++// noise_search / RUSTSEC-2020-0141
++pub struct MvccRwLock<T> {
++ raw: *const T,
++ lock: Mutex<Box<T>>,
++}
++
++unsafe impl<T> Send for MvccRwLock<T> {}
++
++// async-coap / RUSTSEC-2020-0124
++pub struct ArcGuard<RC, T> {
++ inner: T,
++ head: Arc<RC>,
++}
++
++unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
++
++// rusb / RUSTSEC-2020-0098
++extern "C" {
++ type libusb_device_handle;
++}
++
++pub trait UsbContext {
++ // some user trait that does not guarantee `Send`
++}
++
++pub struct DeviceHandle<T: UsbContext> {
++ context: T,
++ handle: NonNull<libusb_device_handle>,
++}
++
++unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
++
++// Other basic tests
++pub struct NoGeneric {
++ rc_is_not_send: Rc<String>,
++}
++
++unsafe impl Send for NoGeneric {}
++
++pub struct MultiField<T> {
++ field1: T,
++ field2: T,
++ field3: T,
++}
++
++unsafe impl<T> Send for MultiField<T> {}
++
++pub enum MyOption<T> {
++ MySome(T),
++ MyNone,
++}
++
++unsafe impl<T> Send for MyOption<T> {}
++
++// Multiple type parameters
++pub struct MultiParam<A, B> {
++ vec: Vec<(A, B)>,
++}
++
++unsafe impl<A, B> Send for MultiParam<A, B> {}
++
++// Tests for raw pointer heuristic
++extern "C" {
++ type NonSend;
++}
++
++pub struct HeuristicTest {
++ // raw pointers are allowed
++ field1: Vec<*const NonSend>,
++ field2: [*const NonSend; 3],
++ field3: (*const NonSend, *const NonSend, *const NonSend),
++ // not allowed when it contains concrete `!Send` field
++ field4: (*const NonSend, Rc<u8>),
++ // nested raw pointer is also allowed
++ field5: Vec<Vec<*const NonSend>>,
++}
++
++unsafe impl Send for HeuristicTest {}
++
++// Test attributes
++#[allow(clippy::non_send_fields_in_send_ty)]
++pub struct AttrTest1<T>(T);
++
++pub struct AttrTest2<T> {
++ #[allow(clippy::non_send_fields_in_send_ty)]
++ field: T,
++}
++
++pub enum AttrTest3<T> {
++ #[allow(clippy::non_send_fields_in_send_ty)]
++ Enum1(T),
++ Enum2(T),
++}
++
++unsafe impl<T> Send for AttrTest1<T> {}
++unsafe impl<T> Send for AttrTest2<T> {}
++unsafe impl<T> Send for AttrTest3<T> {}
++
++// Multiple non-overlapping `Send` for a single type
++pub struct Complex<A, B> {
++ field1: A,
++ field2: B,
++}
++
++unsafe impl<P> Send for Complex<P, u32> {}
++
++// `MutexGuard` is non-Send
++unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this implementation is unsound, as some fields in `RingBuffer<T>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:16:1
++ |
++LL | unsafe impl<T> Send for RingBuffer<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings`
++note: the type of field `data` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:11:5
++ |
++LL | data: Vec<UnsafeCell<T>>,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: add bounds on type parameter `T` that satisfy `Vec<UnsafeCell<T>>: Send`
++
++error: this implementation is unsound, as some fields in `MvccRwLock<T>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:24:1
++ |
++LL | unsafe impl<T> Send for MvccRwLock<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `lock` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:21:5
++ |
++LL | lock: Mutex<Box<T>>,
++ | ^^^^^^^^^^^^^^^^^^^
++ = help: add bounds on type parameter `T` that satisfy `Mutex<Box<T>>: Send`
++
++error: this implementation is unsound, as some fields in `ArcGuard<RC, T>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:32:1
++ |
++LL | unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `head` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:29:5
++ |
++LL | head: Arc<RC>,
++ | ^^^^^^^^^^^^^
++ = help: add bounds on type parameter `RC` that satisfy `Arc<RC>: Send`
++
++error: this implementation is unsound, as some fields in `DeviceHandle<T>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:48:1
++ |
++LL | unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `context` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:44:5
++ |
++LL | context: T,
++ | ^^^^^^^^^^
++ = help: add `T: Send` bound in `Send` impl
++
++error: this implementation is unsound, as some fields in `NoGeneric` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:55:1
++ |
++LL | unsafe impl Send for NoGeneric {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `rc_is_not_send` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:52:5
++ |
++LL | rc_is_not_send: Rc<String>,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++
++error: this implementation is unsound, as some fields in `MultiField<T>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:63:1
++ |
++LL | unsafe impl<T> Send for MultiField<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `field1` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:58:5
++ |
++LL | field1: T,
++ | ^^^^^^^^^
++ = help: add `T: Send` bound in `Send` impl
++note: the type of field `field2` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:59:5
++ |
++LL | field2: T,
++ | ^^^^^^^^^
++ = help: add `T: Send` bound in `Send` impl
++note: the type of field `field3` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:60:5
++ |
++LL | field3: T,
++ | ^^^^^^^^^
++ = help: add `T: Send` bound in `Send` impl
++
++error: this implementation is unsound, as some fields in `MyOption<T>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:70:1
++ |
++LL | unsafe impl<T> Send for MyOption<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `0` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:66:12
++ |
++LL | MySome(T),
++ | ^
++ = help: add `T: Send` bound in `Send` impl
++
++error: this implementation is unsound, as some fields in `MultiParam<A, B>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:77:1
++ |
++LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `vec` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:74:5
++ |
++LL | vec: Vec<(A, B)>,
++ | ^^^^^^^^^^^^^^^^
++ = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send`
++
++error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:95:1
++ |
++LL | unsafe impl Send for HeuristicTest {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `field4` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:90:5
++ |
++LL | field4: (*const NonSend, Rc<u8>),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++
++error: this implementation is unsound, as some fields in `AttrTest3<T>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:114:1
++ |
++LL | unsafe impl<T> Send for AttrTest3<T> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `0` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:109:11
++ |
++LL | Enum2(T),
++ | ^
++ = help: add `T: Send` bound in `Send` impl
++
++error: this implementation is unsound, as some fields in `Complex<P, u32>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:122:1
++ |
++LL | unsafe impl<P> Send for Complex<P, u32> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `field1` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:118:5
++ |
++LL | field1: A,
++ | ^^^^^^^^^
++ = help: add `P: Send` bound in `Send` impl
++
++error: this implementation is unsound, as some fields in `Complex<Q, MutexGuard<'static, bool>>` are `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:125:1
++ |
++LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the type of field `field2` is `!Send`
++ --> $DIR/non_send_fields_in_send_ty.rs:119:5
++ |
++LL | field2: B,
++ | ^^^^^^^^^
++ = help: use a thread-safe type that implements `Send`
++
++error: aborting due to 12 previous errors
++
--- /dev/null
- #![allow(clippy::ref_option_ref)]
+// edition:2018
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+#![allow(clippy::redundant_closure)]
++#![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+ string.map_or((false, "hello"), |x| (true, x))
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+ if string.is_none() {
+ None
+ } else if let Some(x) = string {
+ Some((true, x))
+ } else {
+ Some((false, ""))
+ }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+ let _ = string.map_or(0, |s| s.len());
+ let _ = num.as_ref().map_or(&0, |s| s);
+ let _ = num.as_mut().map_or(&mut 0, |s| {
+ *s += 1;
+ s
+ });
+ let _ = num.as_ref().map_or(&0, |s| s);
+ let _ = num.map_or(0, |mut s| {
+ s += 1;
+ s
+ });
+ let _ = num.as_mut().map_or(&mut 0, |s| {
+ *s += 1;
+ s
+ });
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+ arg.map_or(13, |x| {
+ let y = x * x;
+ y * y
+ })
+}
+
+fn impure_else(arg: Option<i32>) {
+ let side_effect = || {
+ println!("return 1");
+ 1
+ };
+ let _ = arg.map_or_else(|| side_effect(), |x| x);
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+ let _ = arg.map_or_else(|| {
+ let mut y = 1;
+ y = (y + 2 / y) / 2;
+ y = (y + 2 / y) / 2;
+ y
+ }, |x| x * x * x * x);
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+ let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+ for _ in 0..10 {
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ continue;
+ };
+ }
+ let _ = if let Some(x) = arg {
+ return x;
+ } else {
+ 5
+ };
+ 7
+}
+
+fn main() {
+ let optional = Some(5);
+ let _ = optional.map_or(5, |x| x + 2);
+ let _ = bad1(None);
+ let _ = else_if_option(None);
+ unop_bad(&None, None);
+ let _ = longer_body(None);
+ test_map_or_else(None);
+ let _ = negative_tests(None);
+ let _ = impure_else(None);
+
+ let _ = Some(0).map_or(0, |x| loop {
+ if x == 0 {
+ break x;
+ }
+ });
+
+ // #7576
+ const fn _f(x: Option<u32>) -> u32 {
+ // Don't lint, `map_or` isn't const
+ if let Some(x) = x { x } else { 10 }
+ }
+
+ // #5822
+ let s = String::new();
+ // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ s.len()
+ };
+
+ let s = String::new();
+ // Lint, both branches immutably borrow `s`.
+ let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
+
+ let s = String::new();
+ // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+ let _ = Some(0).map_or(1, |x| {
+ let s = s;
+ s.len() + x
+ });
+
+ let s = Some(String::new());
+ // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+ let _ = if let Some(x) = &s {
+ x.len()
+ } else {
+ let _s = s;
+ 10
+ };
+
+ let mut s = Some(String::new());
+ // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s`
+ let _ = if let Some(x) = &mut s {
+ x.push_str("test");
+ x.len()
+ } else {
+ let _s = &s;
+ 10
+ };
+
+ async fn _f1(x: u32) -> u32 {
+ x
+ }
+
+ async fn _f2() {
+ // Don't lint. `await` can't be moved into a closure.
+ let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+ }
+}
--- /dev/null
- #![allow(clippy::ref_option_ref)]
+// edition:2018
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+#![allow(clippy::redundant_closure)]
++#![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+ if let Some(x) = string {
+ (true, x)
+ } else {
+ (false, "hello")
+ }
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+ if string.is_none() {
+ None
+ } else if let Some(x) = string {
+ Some((true, x))
+ } else {
+ Some((false, ""))
+ }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+ let _ = if let Some(s) = *string { s.len() } else { 0 };
+ let _ = if let Some(s) = &num { s } else { &0 };
+ let _ = if let Some(s) = &mut num {
+ *s += 1;
+ s
+ } else {
+ &mut 0
+ };
+ let _ = if let Some(ref s) = num { s } else { &0 };
+ let _ = if let Some(mut s) = num {
+ s += 1;
+ s
+ } else {
+ 0
+ };
+ let _ = if let Some(ref mut s) = num {
+ *s += 1;
+ s
+ } else {
+ &mut 0
+ };
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+ if let Some(x) = arg {
+ let y = x * x;
+ y * y
+ } else {
+ 13
+ }
+}
+
+fn impure_else(arg: Option<i32>) {
+ let side_effect = || {
+ println!("return 1");
+ 1
+ };
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ // map_or_else must be suggested
+ side_effect()
+ };
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+ let _ = if let Some(x) = arg {
+ x * x * x * x
+ } else {
+ let mut y = 1;
+ y = (y + 2 / y) / 2;
+ y = (y + 2 / y) / 2;
+ y
+ };
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+ let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+ for _ in 0..10 {
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ continue;
+ };
+ }
+ let _ = if let Some(x) = arg {
+ return x;
+ } else {
+ 5
+ };
+ 7
+}
+
+fn main() {
+ let optional = Some(5);
+ let _ = if let Some(x) = optional { x + 2 } else { 5 };
+ let _ = bad1(None);
+ let _ = else_if_option(None);
+ unop_bad(&None, None);
+ let _ = longer_body(None);
+ test_map_or_else(None);
+ let _ = negative_tests(None);
+ let _ = impure_else(None);
+
+ let _ = if let Some(x) = Some(0) {
+ loop {
+ if x == 0 {
+ break x;
+ }
+ }
+ } else {
+ 0
+ };
+
+ // #7576
+ const fn _f(x: Option<u32>) -> u32 {
+ // Don't lint, `map_or` isn't const
+ if let Some(x) = x { x } else { 10 }
+ }
+
+ // #5822
+ let s = String::new();
+ // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ s.len()
+ };
+
+ let s = String::new();
+ // Lint, both branches immutably borrow `s`.
+ let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
+
+ let s = String::new();
+ // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ 1
+ };
+
+ let s = Some(String::new());
+ // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+ let _ = if let Some(x) = &s {
+ x.len()
+ } else {
+ let _s = s;
+ 10
+ };
+
+ let mut s = Some(String::new());
+ // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s`
+ let _ = if let Some(x) = &mut s {
+ x.push_str("test");
+ x.len()
+ } else {
+ let _s = &s;
+ 10
+ };
+
+ async fn _f1(x: u32) -> u32 {
+ x
+ }
+
+ async fn _f2() {
+ // Don't lint. `await` can't be moved into a closure.
+ let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+ }
+}
--- /dev/null
- #![allow(clippy::if_same_then_else)]
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
++#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+ let m = std::sync::Mutex::new((0, 0));
+
+ // Result
+ if m.lock().is_ok() {}
+ if Err::<(), _>(m.lock().unwrap().0).is_err() {}
+
+ {
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+ }
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {
+ } else {
+ }
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+ if Err::<std::sync::MutexGuard<()>, _>(()).is_err() {}
+
+ if Ok::<_, ()>(String::new()).is_ok() {}
+ if Err::<(), _>((String::new(), ())).is_err() {}
+
+ // Option
+ if Some(m.lock()).is_some() {}
+ if Some(m.lock().unwrap().0).is_some() {}
+
+ {
+ if None::<std::sync::MutexGuard<()>>.is_none() {}
+ }
+ if None::<std::sync::MutexGuard<()>>.is_none() {
+ } else {
+ }
+
+ if None::<std::sync::MutexGuard<()>>.is_none() {}
+
+ if Some(String::new()).is_some() {}
+ if Some((String::new(), ())).is_some() {}
+
+ // Poll
+ if Ready(m.lock()).is_ready() {}
+ if Ready(m.lock().unwrap().0).is_ready() {}
+
+ {
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+ }
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {
+ } else {
+ }
+
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+
+ if Ready(String::new()).is_ready() {}
+ if Ready((String::new(), ())).is_ready() {}
+}
--- /dev/null
- #![allow(clippy::if_same_then_else)]
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
++#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+ let m = std::sync::Mutex::new((0, 0));
+
+ // Result
+ if let Ok(_) = m.lock() {}
+ if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+
+ {
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ }
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+ } else {
+ }
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+
+ if let Ok(_) = Ok::<_, ()>(String::new()) {}
+ if let Err(_) = Err::<(), _>((String::new(), ())) {}
+
+ // Option
+ if let Some(_) = Some(m.lock()) {}
+ if let Some(_) = Some(m.lock().unwrap().0) {}
+
+ {
+ if let None = None::<std::sync::MutexGuard<()>> {}
+ }
+ if let None = None::<std::sync::MutexGuard<()>> {
+ } else {
+ }
+
+ if let None = None::<std::sync::MutexGuard<()>> {}
+
+ if let Some(_) = Some(String::new()) {}
+ if let Some(_) = Some((String::new(), ())) {}
+
+ // Poll
+ if let Ready(_) = Ready(m.lock()) {}
+ if let Ready(_) = Ready(m.lock().unwrap().0) {}
+
+ {
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+ }
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {
+ } else {
+ }
+
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+
+ if let Ready(_) = Ready(String::new()) {}
+ if let Ready(_) = Ready((String::new(), ())) {}
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
++ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+fn main() {
+ if None::<()>.is_none() {}
+
+ if Some(42).is_some() {}
+
+ if Some(42).is_some() {
+ foo();
+ } else {
+ bar();
+ }
+
+ while Some(42).is_some() {}
+
+ while Some(42).is_none() {}
+
+ while None::<()>.is_none() {}
+
+ let mut v = vec![1, 2, 3];
+ while v.pop().is_some() {
+ foo();
+ }
+
+ if None::<i32>.is_none() {}
+
+ if Some(42).is_some() {}
+
+ Some(42).is_some();
+
+ None::<()>.is_none();
+
+ let _ = None::<()>.is_none();
+
+ let opt = Some(false);
+ let _ = if opt.is_some() { true } else { false };
+
+ issue6067();
+
+ let _ = if gen_opt().is_some() {
+ 1
+ } else if gen_opt().is_none() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_opt() -> Option<()> {
+ None
+}
+
+fn foo() {}
+
+fn bar() {}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+ if Some(42).is_some() {}
+
+ if None::<()>.is_none() {}
+
+ while Some(42).is_some() {}
+
+ while None::<()>.is_none() {}
+
+ Some(42).is_some();
+
+ None::<()>.is_none();
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
++ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+fn main() {
+ if let None = None::<()> {}
+
+ if let Some(_) = Some(42) {}
+
+ if let Some(_) = Some(42) {
+ foo();
+ } else {
+ bar();
+ }
+
+ while let Some(_) = Some(42) {}
+
+ while let None = Some(42) {}
+
+ while let None = None::<()> {}
+
+ let mut v = vec![1, 2, 3];
+ while let Some(_) = v.pop() {
+ foo();
+ }
+
+ if None::<i32>.is_none() {}
+
+ if Some(42).is_some() {}
+
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+
+ let _ = match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+
+ let opt = Some(false);
+ let _ = if let Some(_) = opt { true } else { false };
+
+ issue6067();
+
+ let _ = if let Some(_) = gen_opt() {
+ 1
+ } else if let None = gen_opt() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_opt() -> Option<()> {
+ None
+}
+
+fn foo() {}
+
+fn bar() {}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+ if let Some(_) = Some(42) {}
+
+ if let None = None::<()> {}
+
+ while let Some(_) = Some(42) {}
+
+ while let None = None::<()> {}
+
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+}
--- /dev/null
- --> $DIR/redundant_pattern_matching_option.rs:13:12
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:15:12
++ --> $DIR/redundant_pattern_matching_option.rs:14:12
+ |
+LL | if let None = None::<()> {}
+ | -------^^^^------------- help: try this: `if None::<()>.is_none()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:17:12
++ --> $DIR/redundant_pattern_matching_option.rs:16:12
+ |
+LL | if let Some(_) = Some(42) {}
+ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:23:15
++ --> $DIR/redundant_pattern_matching_option.rs:18:12
+ |
+LL | if let Some(_) = Some(42) {
+ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:25:15
++ --> $DIR/redundant_pattern_matching_option.rs:24:15
+ |
+LL | while let Some(_) = Some(42) {}
+ | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:27:15
++ --> $DIR/redundant_pattern_matching_option.rs:26:15
+ |
+LL | while let None = Some(42) {}
+ | ----------^^^^----------- help: try this: `while Some(42).is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:30:15
++ --> $DIR/redundant_pattern_matching_option.rs:28:15
+ |
+LL | while let None = None::<()> {}
+ | ----------^^^^------------- help: try this: `while None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:38:5
++ --> $DIR/redundant_pattern_matching_option.rs:31:15
+ |
+LL | while let Some(_) = v.pop() {
+ | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:43:5
++ --> $DIR/redundant_pattern_matching_option.rs:39:5
+ |
+LL | / match Some(42) {
+LL | | Some(_) => true,
+LL | | None => false,
+LL | | };
+ | |_____^ help: try this: `Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:48:13
++ --> $DIR/redundant_pattern_matching_option.rs:44:5
+ |
+LL | / match None::<()> {
+LL | | Some(_) => false,
+LL | | None => true,
+LL | | };
+ | |_____^ help: try this: `None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:54:20
++ --> $DIR/redundant_pattern_matching_option.rs:49:13
+ |
+LL | let _ = match None::<()> {
+ | _____________^
+LL | | Some(_) => false,
+LL | | None => true,
+LL | | };
+ | |_____^ help: try this: `None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:58:20
++ --> $DIR/redundant_pattern_matching_option.rs:55:20
+ |
+LL | let _ = if let Some(_) = opt { true } else { false };
+ | -------^^^^^^^------ help: try this: `if opt.is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:60:19
++ --> $DIR/redundant_pattern_matching_option.rs:59:20
+ |
+LL | let _ = if let Some(_) = gen_opt() {
+ | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:79:12
++ --> $DIR/redundant_pattern_matching_option.rs:61:19
+ |
+LL | } else if let None = gen_opt() {
+ | -------^^^^------------ help: try this: `if gen_opt().is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:81:12
++ --> $DIR/redundant_pattern_matching_option.rs:80:12
+ |
+LL | if let Some(_) = Some(42) {}
+ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:83:15
++ --> $DIR/redundant_pattern_matching_option.rs:82:12
+ |
+LL | if let None = None::<()> {}
+ | -------^^^^------------- help: try this: `if None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:85:15
++ --> $DIR/redundant_pattern_matching_option.rs:84:15
+ |
+LL | while let Some(_) = Some(42) {}
+ | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
- --> $DIR/redundant_pattern_matching_option.rs:87:5
++ --> $DIR/redundant_pattern_matching_option.rs:86:15
+ |
+LL | while let None = None::<()> {}
+ | ----------^^^^------------- help: try this: `while None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
- --> $DIR/redundant_pattern_matching_option.rs:92:5
++ --> $DIR/redundant_pattern_matching_option.rs:88:5
+ |
+LL | / match Some(42) {
+LL | | Some(_) => true,
+LL | | None => false,
+LL | | };
+ | |_____^ help: try this: `Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
++ --> $DIR/redundant_pattern_matching_option.rs:93:5
+ |
+LL | / match None::<()> {
+LL | | Some(_) => false,
+LL | | None => true,
+LL | | };
+ | |_____^ help: try this: `None::<()>.is_none()`
+
+error: aborting due to 19 previous errors
+
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
++ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+use std::task::Poll::{self, Pending, Ready};
+
+fn main() {
+ if Pending::<()>.is_pending() {}
+
+ if Ready(42).is_ready() {}
+
+ if Ready(42).is_ready() {
+ foo();
+ } else {
+ bar();
+ }
+
+ while Ready(42).is_ready() {}
+
+ while Ready(42).is_pending() {}
+
+ while Pending::<()>.is_pending() {}
+
+ if Pending::<i32>.is_pending() {}
+
+ if Ready(42).is_ready() {}
+
+ Ready(42).is_ready();
+
+ Pending::<()>.is_pending();
+
+ let _ = Pending::<()>.is_pending();
+
+ let poll = Ready(false);
+ let _ = if poll.is_ready() { true } else { false };
+
+ poll_const();
+
+ let _ = if gen_poll().is_ready() {
+ 1
+ } else if gen_poll().is_pending() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_poll() -> Poll<()> {
+ Pending
+}
+
+fn foo() {}
+
+fn bar() {}
+
+const fn poll_const() {
+ if Ready(42).is_ready() {}
+
+ if Pending::<()>.is_pending() {}
+
+ while Ready(42).is_ready() {}
+
+ while Pending::<()>.is_pending() {}
+
+ Ready(42).is_ready();
+
+ Pending::<()>.is_pending();
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
++ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+use std::task::Poll::{self, Pending, Ready};
+
+fn main() {
+ if let Pending = Pending::<()> {}
+
+ if let Ready(_) = Ready(42) {}
+
+ if let Ready(_) = Ready(42) {
+ foo();
+ } else {
+ bar();
+ }
+
+ while let Ready(_) = Ready(42) {}
+
+ while let Pending = Ready(42) {}
+
+ while let Pending = Pending::<()> {}
+
+ if Pending::<i32>.is_pending() {}
+
+ if Ready(42).is_ready() {}
+
+ match Ready(42) {
+ Ready(_) => true,
+ Pending => false,
+ };
+
+ match Pending::<()> {
+ Ready(_) => false,
+ Pending => true,
+ };
+
+ let _ = match Pending::<()> {
+ Ready(_) => false,
+ Pending => true,
+ };
+
+ let poll = Ready(false);
+ let _ = if let Ready(_) = poll { true } else { false };
+
+ poll_const();
+
+ let _ = if let Ready(_) = gen_poll() {
+ 1
+ } else if let Pending = gen_poll() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_poll() -> Poll<()> {
+ Pending
+}
+
+fn foo() {}
+
+fn bar() {}
+
+const fn poll_const() {
+ if let Ready(_) = Ready(42) {}
+
+ if let Pending = Pending::<()> {}
+
+ while let Ready(_) = Ready(42) {}
+
+ while let Pending = Pending::<()> {}
+
+ match Ready(42) {
+ Ready(_) => true,
+ Pending => false,
+ };
+
+ match Pending::<()> {
+ Ready(_) => false,
+ Pending => true,
+ };
+}
--- /dev/null
- --> $DIR/redundant_pattern_matching_poll.rs:15:12
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:17:12
++ --> $DIR/redundant_pattern_matching_poll.rs:16:12
+ |
+LL | if let Pending = Pending::<()> {}
+ | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:19:12
++ --> $DIR/redundant_pattern_matching_poll.rs:18:12
+ |
+LL | if let Ready(_) = Ready(42) {}
+ | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:25:15
++ --> $DIR/redundant_pattern_matching_poll.rs:20:12
+ |
+LL | if let Ready(_) = Ready(42) {
+ | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:27:15
++ --> $DIR/redundant_pattern_matching_poll.rs:26:15
+ |
+LL | while let Ready(_) = Ready(42) {}
+ | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:29:15
++ --> $DIR/redundant_pattern_matching_poll.rs:28:15
+ |
+LL | while let Pending = Ready(42) {}
+ | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()`
+
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:35:5
++ --> $DIR/redundant_pattern_matching_poll.rs:30:15
+ |
+LL | while let Pending = Pending::<()> {}
+ | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:40:5
++ --> $DIR/redundant_pattern_matching_poll.rs:36:5
+ |
+LL | / match Ready(42) {
+LL | | Ready(_) => true,
+LL | | Pending => false,
+LL | | };
+ | |_____^ help: try this: `Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:45:13
++ --> $DIR/redundant_pattern_matching_poll.rs:41:5
+ |
+LL | / match Pending::<()> {
+LL | | Ready(_) => false,
+LL | | Pending => true,
+LL | | };
+ | |_____^ help: try this: `Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:51:20
++ --> $DIR/redundant_pattern_matching_poll.rs:46:13
+ |
+LL | let _ = match Pending::<()> {
+ | _____________^
+LL | | Ready(_) => false,
+LL | | Pending => true,
+LL | | };
+ | |_____^ help: try this: `Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:55:20
++ --> $DIR/redundant_pattern_matching_poll.rs:52:20
+ |
+LL | let _ = if let Ready(_) = poll { true } else { false };
+ | -------^^^^^^^^------- help: try this: `if poll.is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:57:19
++ --> $DIR/redundant_pattern_matching_poll.rs:56:20
+ |
+LL | let _ = if let Ready(_) = gen_poll() {
+ | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:73:12
++ --> $DIR/redundant_pattern_matching_poll.rs:58:19
+ |
+LL | } else if let Pending = gen_poll() {
+ | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:75:12
++ --> $DIR/redundant_pattern_matching_poll.rs:74:12
+ |
+LL | if let Ready(_) = Ready(42) {}
+ | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:77:15
++ --> $DIR/redundant_pattern_matching_poll.rs:76:12
+ |
+LL | if let Pending = Pending::<()> {}
+ | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:79:15
++ --> $DIR/redundant_pattern_matching_poll.rs:78:15
+ |
+LL | while let Ready(_) = Ready(42) {}
+ | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
- --> $DIR/redundant_pattern_matching_poll.rs:81:5
++ --> $DIR/redundant_pattern_matching_poll.rs:80:15
+ |
+LL | while let Pending = Pending::<()> {}
+ | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
- --> $DIR/redundant_pattern_matching_poll.rs:86:5
++ --> $DIR/redundant_pattern_matching_poll.rs:82:5
+ |
+LL | / match Ready(42) {
+LL | | Ready(_) => true,
+LL | | Pending => false,
+LL | | };
+ | |_____^ help: try this: `Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
++ --> $DIR/redundant_pattern_matching_poll.rs:87:5
+ |
+LL | / match Pending::<()> {
+LL | | Ready(_) => false,
+LL | | Pending => true,
+LL | | };
+ | |_____^ help: try this: `Pending::<()>.is_pending()`
+
+error: aborting due to 18 previous errors
+
--- /dev/null
- #![warn(
- clippy::all,
- clippy::pedantic,
- clippy::shadow_same,
- clippy::shadow_reuse,
- clippy::shadow_unrelated
- )]
- #![allow(
- unused_parens,
- unused_variables,
- clippy::manual_unwrap_or,
- clippy::missing_docs_in_private_items,
- clippy::single_match
- )]
-
- fn id<T>(x: T) -> T {
- x
++#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
++
++fn shadow_same() {
++ let x = 1;
++ let x = x;
++ let mut x = &x;
++ let x = &mut x;
++ let x = *x;
+}
+
- #[must_use]
- fn first(x: (isize, isize)) -> isize {
- x.0
++fn shadow_reuse() -> Option<()> {
++ let x = ([[0]], ());
++ let x = x.0;
++ let x = x[0];
++ let [x] = x;
++ let x = Some(x);
++ let x = foo(x);
++ let x = || x;
++ let x = Some(1).map(|_| x)?;
++ None
+}
+
- fn main() {
- let mut x = 1;
- let x = &mut x;
- let x = { x };
- let x = (&*x);
- let x = { *x + 1 };
- let x = id(x);
- let x = (1, x);
- let x = first(x);
- let y = 1;
- let x = y;
-
- let x;
- x = 42;
-
- let o = Some(1_u8);
-
- if let Some(p) = o {
- assert_eq!(1, p);
++fn shadow_unrelated() {
++ let x = 1;
++ let x = 2;
++}
++
++fn syntax() {
++ fn f(x: u32) {
++ let x = 1;
++ }
++ let x = 1;
++ match Some(1) {
++ Some(1) => {},
++ Some(x) => {
++ let x = 1;
++ },
++ _ => {},
+ }
- match o {
- Some(p) => p, // no error, because the p above is in its own scope
- None => 0,
++ if let Some(x) = Some(1) {}
++ while let Some(x) = Some(1) {}
++ let _ = |[x]: [u32; 1]| {
++ let x = 1;
+ };
++}
+
- match (x, o) {
- (1, Some(a)) | (a, Some(1)) => (), // no error though `a` appears twice
- _ => (),
++fn negative() {
++ match Some(1) {
++ Some(x) if x == 1 => {},
++ Some(x) => {},
++ None => {},
+ }
++ match [None, Some(1)] {
++ [Some(x), None] | [None, Some(x)] => {},
++ _ => {},
++ }
++ if let Some(x) = Some(1) {
++ let y = 1;
++ } else {
++ let x = 1;
++ let y = 1;
++ }
++ let x = 1;
++ #[allow(clippy::shadow_unrelated)]
++ let x = 1;
++}
++
++fn foo<T>(_: T) {}
++
++fn question_mark() -> Option<()> {
++ let val = 1;
++ // `?` expands with a `val` binding
++ None?;
++ None
+}
++
++fn main() {}
--- /dev/null
- error: `x` is shadowed by itself in `&mut x`
- --> $DIR/shadow.rs:27:5
++error: `x` is shadowed by itself in `x`
++ --> $DIR/shadow.rs:5:9
+ |
- LL | let x = &mut x;
- | ^^^^^^^^^^^^^^^
++LL | let x = x;
++ | ^
+ |
+ = note: `-D clippy::shadow-same` implied by `-D warnings`
+note: previous binding is here
- --> $DIR/shadow.rs:26:13
++ --> $DIR/shadow.rs:4:9
+ |
- LL | let mut x = 1;
- | ^
++LL | let x = 1;
++ | ^
+
- error: `x` is shadowed by itself in `{ x }`
- --> $DIR/shadow.rs:28:5
++error: `mut x` is shadowed by itself in `&x`
++ --> $DIR/shadow.rs:6:13
+ |
- LL | let x = { x };
- | ^^^^^^^^^^^^^^
++LL | let mut x = &x;
++ | ^
+ |
+note: previous binding is here
- --> $DIR/shadow.rs:27:9
++ --> $DIR/shadow.rs:5:9
++ |
++LL | let x = x;
++ | ^
++
++error: `x` is shadowed by itself in `&mut x`
++ --> $DIR/shadow.rs:7:9
+ |
+LL | let x = &mut x;
+ | ^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:6:9
++ |
++LL | let mut x = &x;
++ | ^^^^^
+
- error: `x` is shadowed by itself in `(&*x)`
- --> $DIR/shadow.rs:29:5
++error: `x` is shadowed by itself in `*x`
++ --> $DIR/shadow.rs:8:9
+ |
- LL | let x = (&*x);
- | ^^^^^^^^^^^^^^
++LL | let x = *x;
++ | ^
+ |
+note: previous binding is here
- --> $DIR/shadow.rs:28:9
++ --> $DIR/shadow.rs:7:9
+ |
- LL | let x = { x };
++LL | let x = &mut x;
+ | ^
+
- error: `x` is shadowed by `{ *x + 1 }` which reuses the original value
- --> $DIR/shadow.rs:30:9
++error: `x` is shadowed by `x.0` which reuses the original value
++ --> $DIR/shadow.rs:13:9
+ |
- LL | let x = { *x + 1 };
++LL | let x = x.0;
+ | ^
+ |
+ = note: `-D clippy::shadow-reuse` implied by `-D warnings`
- note: initialization happens here
- --> $DIR/shadow.rs:30:13
- |
- LL | let x = { *x + 1 };
- | ^^^^^^^^^^
+note: previous binding is here
- --> $DIR/shadow.rs:29:9
++ --> $DIR/shadow.rs:12:9
+ |
- LL | let x = (&*x);
++LL | let x = ([[0]], ());
+ | ^
+
- error: `x` is shadowed by `id(x)` which reuses the original value
- --> $DIR/shadow.rs:31:9
++error: `x` is shadowed by `x[0]` which reuses the original value
++ --> $DIR/shadow.rs:14:9
+ |
- LL | let x = id(x);
++LL | let x = x[0];
+ | ^
+ |
- note: initialization happens here
- --> $DIR/shadow.rs:31:13
++note: previous binding is here
++ --> $DIR/shadow.rs:13:9
++ |
++LL | let x = x.0;
++ | ^
++
++error: `x` is shadowed by `x` which reuses the original value
++ --> $DIR/shadow.rs:15:10
++ |
++LL | let [x] = x;
++ | ^
+ |
- LL | let x = id(x);
- | ^^^^^
+note: previous binding is here
- --> $DIR/shadow.rs:30:9
++ --> $DIR/shadow.rs:14:9
+ |
- LL | let x = { *x + 1 };
++LL | let x = x[0];
+ | ^
+
- error: `x` is shadowed by `(1, x)` which reuses the original value
- --> $DIR/shadow.rs:32:9
++error: `x` is shadowed by `Some(x)` which reuses the original value
++ --> $DIR/shadow.rs:16:9
+ |
- LL | let x = (1, x);
++LL | let x = Some(x);
+ | ^
+ |
- note: initialization happens here
- --> $DIR/shadow.rs:32:13
++note: previous binding is here
++ --> $DIR/shadow.rs:15:10
++ |
++LL | let [x] = x;
++ | ^
++
++error: `x` is shadowed by `foo(x)` which reuses the original value
++ --> $DIR/shadow.rs:17:9
++ |
++LL | let x = foo(x);
++ | ^
+ |
- LL | let x = (1, x);
- | ^^^^^^
+note: previous binding is here
- --> $DIR/shadow.rs:31:9
++ --> $DIR/shadow.rs:16:9
+ |
- LL | let x = id(x);
++LL | let x = Some(x);
+ | ^
+
- error: `x` is shadowed by `first(x)` which reuses the original value
- --> $DIR/shadow.rs:33:9
++error: `x` is shadowed by `|| x` which reuses the original value
++ --> $DIR/shadow.rs:18:9
+ |
- LL | let x = first(x);
++LL | let x = || x;
+ | ^
+ |
- note: initialization happens here
- --> $DIR/shadow.rs:33:13
++note: previous binding is here
++ --> $DIR/shadow.rs:17:9
++ |
++LL | let x = foo(x);
++ | ^
++
++error: `x` is shadowed by `Some(1).map(|_| x)?` which reuses the original value
++ --> $DIR/shadow.rs:19:9
++ |
++LL | let x = Some(1).map(|_| x)?;
++ | ^
+ |
- LL | let x = first(x);
- | ^^^^^^^^
+note: previous binding is here
- --> $DIR/shadow.rs:32:9
++ --> $DIR/shadow.rs:18:9
+ |
- LL | let x = (1, x);
++LL | let x = || x;
+ | ^
+
- error: `x` is being shadowed
- --> $DIR/shadow.rs:35:9
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:25:9
+ |
- LL | let x = y;
++LL | let x = 2;
+ | ^
+ |
+ = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
- note: initialization happens here
- --> $DIR/shadow.rs:35:13
++note: previous binding is here
++ --> $DIR/shadow.rs:24:9
+ |
- LL | let x = y;
++LL | let x = 1;
++ | ^
++
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:30:13
++ |
++LL | let x = 1;
+ | ^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:29:10
++ |
++LL | fn f(x: u32) {
++ | ^
++
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:35:14
++ |
++LL | Some(x) => {
++ | ^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:32:9
++ |
++LL | let x = 1;
++ | ^
++
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:36:17
++ |
++LL | let x = 1;
++ | ^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:35:14
++ |
++LL | Some(x) => {
++ | ^
++
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:40:17
++ |
++LL | if let Some(x) = Some(1) {}
++ | ^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:32:9
++ |
++LL | let x = 1;
++ | ^
++
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:41:20
++ |
++LL | while let Some(x) = Some(1) {}
++ | ^
++ |
+note: previous binding is here
- --> $DIR/shadow.rs:33:9
++ --> $DIR/shadow.rs:32:9
+ |
- LL | let x = first(x);
++LL | let x = 1;
+ | ^
+
- error: `x` shadows a previous declaration
- --> $DIR/shadow.rs:37:5
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:42:15
+ |
- LL | let x;
- | ^^^^^^
++LL | let _ = |[x]: [u32; 1]| {
++ | ^
+ |
+note: previous binding is here
- --> $DIR/shadow.rs:35:9
++ --> $DIR/shadow.rs:32:9
+ |
- LL | let x = y;
++LL | let x = 1;
+ | ^
+
- error: aborting due to 9 previous errors
++error: `x` shadows a previous, unrelated binding
++ --> $DIR/shadow.rs:43:13
++ |
++LL | let x = 1;
++ | ^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:42:15
++ |
++LL | let _ = |[x]: [u32; 1]| {
++ | ^
++
++error: aborting due to 19 previous errors
+
--- /dev/null
- = help: make sure you did not confuse `map` with `filter` or `for_each`
+error: this call to `map()` won't have an effect on the call to `count()`
+ --> $DIR/suspicious_map.rs:4:13
+ |
+LL | let _ = (0..3).map(|x| x + 2).count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::suspicious-map` implied by `-D warnings`
- = help: make sure you did not confuse `map` with `filter` or `for_each`
++ = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
+
+error: this call to `map()` won't have an effect on the call to `count()`
+ --> $DIR/suspicious_map.rs:7:13
+ |
+LL | let _ = (0..3).map(f).count();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
++ = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
- #![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)]
+// run-rustfix
+
+#![warn(clippy::while_let_on_iterator)]
++#![allow(
++ clippy::never_loop,
++ unreachable_code,
++ unused_mut,
++ dead_code,
++ clippy::equatable_if_let
++)]
+
+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() {
+ 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];
+
+ 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
+
+ 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 issue6491() {
+ // Used in outer loop, needs &mut
+ let mut it = 1..40;
+ while let Some(n) = it.next() {
+ for m in it.by_ref() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+
+ // This is fine, inner loop uses a new iterator.
+ let mut it = 1..40;
+ for n in it {
+ let mut it = 1..40;
+ for m in it {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Weird binding shouldn't change anything.
+ let (mut it, _) = (1..40, 0);
+ for m in it {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Used after the loop, needs &mut.
+ let mut it = 1..40;
+ for m in it.by_ref() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("next item {}", it.next().unwrap());
+
+ println!("n still is {}", n);
+ }
+}
+
+fn issue6231() {
+ // Closure in the outer loop, needs &mut
+ let mut it = 1..40;
+ let mut opt = Some(0);
+ while let Some(n) = opt.take().or_else(|| it.next()) {
+ for m in it.by_ref() {
+ if n % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+}
+
+fn issue1924() {
+ struct S<T>(T);
+ impl<T: Iterator<Item = u32>> S<T> {
+ fn f(&mut self) -> Option<u32> {
+ // Used as a field.
+ for i in self.0.by_ref() {
+ if !(3..=7).contains(&i) {
+ return Some(i);
+ }
+ }
+ None
+ }
+
+ fn f2(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.next() {
+ if i == 1 {
+ return self.f();
+ }
+ }
+ None
+ }
+ }
+ impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
+ fn f3(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.0.0.f();
+ }
+ }
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.f3();
+ }
+ }
+ // This one is fine, a different field is borrowed
+ for i in self.0.0.0.by_ref() {
+ if i == 1 {
+ return self.0.1.take();
+ } else {
+ self.0.1 = Some(i);
+ }
+ }
+ None
+ }
+ }
+
+ struct S2<T>(T, u32);
+ impl<T: Iterator<Item = u32>> Iterator for S2<T> {
+ type Item = u32;
+ fn next(&mut self) -> Option<u32> {
+ self.0.next()
+ }
+ }
+
+ // Don't lint, field of the iterator is accessed in the loop
+ let mut it = S2(1..40, 0);
+ while let Some(n) = it.next() {
+ if n == it.1 {
+ break;
+ }
+ }
+
+ // Needs &mut, field of the iterator is accessed after the loop
+ let mut it = S2(1..40, 0);
+ for n in it.by_ref() {
+ if n == 0 {
+ break;
+ }
+ }
+ println!("iterator field {}", it.1);
+}
+
+fn issue7249() {
+ let mut it = 0..10;
+ let mut x = || {
+ // Needs &mut, the closure can be called multiple times
+ for x in it.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ };
+ x();
+ x();
+}
+
+fn issue7510() {
+ let mut it = 0..10;
+ let it = &mut it;
+ // Needs to reborrow `it` as the binding isn't mutable
+ for x in it.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.next().unwrap());
+
+ struct S<T>(T);
+ let mut it = 0..10;
+ let it = S(&mut it);
+ // Needs to reborrow `it.0` as the binding isn't mutable
+ for x in it.0.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.0.next().unwrap());
+}
+
+fn exact_match_with_single_field() {
+ struct S<T>(T);
+ let mut s = S(0..10);
+ // Don't lint. `s.0` is used inside the loop.
+ while let Some(_) = s.0.next() {
+ let _ = &mut s.0;
+ }
+}
+
+fn main() {
+ let mut it = 0..20;
+ for _ in it {
+ println!("test");
+ }
+}
--- /dev/null
- #![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)]
+// run-rustfix
+
+#![warn(clippy::while_let_on_iterator)]
++#![allow(
++ clippy::never_loop,
++ unreachable_code,
++ unused_mut,
++ dead_code,
++ clippy::equatable_if_let
++)]
+
+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() {
+ 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];
+
+ 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
+
+ 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 issue6491() {
+ // Used in outer loop, needs &mut
+ let mut it = 1..40;
+ while let Some(n) = it.next() {
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+
+ // This is fine, inner loop uses a new iterator.
+ let mut it = 1..40;
+ while let Some(n) = it.next() {
+ let mut it = 1..40;
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Weird binding shouldn't change anything.
+ let (mut it, _) = (1..40, 0);
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Used after the loop, needs &mut.
+ let mut it = 1..40;
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("next item {}", it.next().unwrap());
+
+ println!("n still is {}", n);
+ }
+}
+
+fn issue6231() {
+ // Closure in the outer loop, needs &mut
+ let mut it = 1..40;
+ let mut opt = Some(0);
+ while let Some(n) = opt.take().or_else(|| it.next()) {
+ while let Some(m) = it.next() {
+ if n % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+}
+
+fn issue1924() {
+ struct S<T>(T);
+ impl<T: Iterator<Item = u32>> S<T> {
+ fn f(&mut self) -> Option<u32> {
+ // Used as a field.
+ while let Some(i) = self.0.next() {
+ if i < 3 || i > 7 {
+ return Some(i);
+ }
+ }
+ None
+ }
+
+ fn f2(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.next() {
+ if i == 1 {
+ return self.f();
+ }
+ }
+ None
+ }
+ }
+ impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
+ fn f3(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.0.0.f();
+ }
+ }
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.f3();
+ }
+ }
+ // This one is fine, a different field is borrowed
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.0.1.take();
+ } else {
+ self.0.1 = Some(i);
+ }
+ }
+ None
+ }
+ }
+
+ struct S2<T>(T, u32);
+ impl<T: Iterator<Item = u32>> Iterator for S2<T> {
+ type Item = u32;
+ fn next(&mut self) -> Option<u32> {
+ self.0.next()
+ }
+ }
+
+ // Don't lint, field of the iterator is accessed in the loop
+ let mut it = S2(1..40, 0);
+ while let Some(n) = it.next() {
+ if n == it.1 {
+ break;
+ }
+ }
+
+ // Needs &mut, field of the iterator is accessed after the loop
+ let mut it = S2(1..40, 0);
+ while let Some(n) = it.next() {
+ if n == 0 {
+ break;
+ }
+ }
+ println!("iterator field {}", it.1);
+}
+
+fn issue7249() {
+ let mut it = 0..10;
+ let mut x = || {
+ // Needs &mut, the closure can be called multiple times
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ };
+ x();
+ x();
+}
+
+fn issue7510() {
+ let mut it = 0..10;
+ let it = &mut it;
+ // Needs to reborrow `it` as the binding isn't mutable
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.next().unwrap());
+
+ struct S<T>(T);
+ let mut it = 0..10;
+ let it = S(&mut it);
+ // Needs to reborrow `it.0` as the binding isn't mutable
+ while let Some(x) = it.0.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.0.next().unwrap());
+}
+
+fn exact_match_with_single_field() {
+ struct S<T>(T);
+ let mut s = S(0..10);
+ // Don't lint. `s.0` is used inside the loop.
+ while let Some(_) = s.0.next() {
+ let _ = &mut s.0;
+ }
+}
+
+fn main() {
+ let mut it = 0..20;
+ while let Some(..) = it.next() {
+ println!("test");
+ }
+}
--- /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:14: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:19: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:94:9
++ --> $DIR/while_let_on_iterator.rs:24:5
+ |
+LL | while let Some(_) = iter.next() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:101:9
++ --> $DIR/while_let_on_iterator.rs:100: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:114:9
++ --> $DIR/while_let_on_iterator.rs:107: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:134:9
++ --> $DIR/while_let_on_iterator.rs:120: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:191:9
++ --> $DIR/while_let_on_iterator.rs:140:9
+ |
+LL | while let Some(_) = y.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:202:5
++ --> $DIR/while_let_on_iterator.rs:197:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:204:9
++ --> $DIR/while_let_on_iterator.rs:208:5
+ |
+LL | while let Some(n) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:213:9
++ --> $DIR/while_let_on_iterator.rs:210:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:222:9
++ --> $DIR/while_let_on_iterator.rs:219:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:239:9
++ --> $DIR/while_let_on_iterator.rs:228:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:254:13
++ --> $DIR/while_let_on_iterator.rs:245:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:255:20
++ --> $DIR/while_let_on_iterator.rs:260:13
+ |
+LL | while let Some(i) = self.0.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
+
+error: manual `!RangeInclusive::contains` implementation
- --> $DIR/while_let_on_iterator.rs:286:13
++ --> $DIR/while_let_on_iterator.rs:261:20
+ |
+LL | if i < 3 || i > 7 {
+ | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)`
+ |
+ = note: `-D clippy::manual-range-contains` implied by `-D warnings`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:315:5
++ --> $DIR/while_let_on_iterator.rs:292:13
+ |
+LL | while let Some(i) = self.0.0.0.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:327:9
++ --> $DIR/while_let_on_iterator.rs:321:5
+ |
+LL | while let Some(n) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:341:5
++ --> $DIR/while_let_on_iterator.rs:333:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:352:5
++ --> $DIR/while_let_on_iterator.rs:347:5
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:371:5
++ --> $DIR/while_let_on_iterator.rs:358:5
+ |
+LL | while let Some(x) = it.0.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
+
+error: this loop could be written as a `for` loop
++ --> $DIR/while_let_on_iterator.rs:377:5
+ |
+LL | while let Some(..) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
+
+error: aborting due to 21 previous errors
+
--- /dev/null
+#!/bin/sh
+
+# hide output
+set -e
+
+# Update lints
+cargo dev update_lints
+git add clippy_lints/src/lib.rs
++git add clippy_lints/src/lib.*.rs
+
+# Formatting:
+# Git will not automatically add the formatted code to the staged changes once
+# fmt was executed. This collects all staged files rs files that are currently staged.
+# They will later be added back.
+#
+# This was proudly stolen and adjusted from here:
+# https://medium.com/@harshitbangar/automatic-code-formatting-with-git-66c3c5c26798
+files=$( (git diff --cached --name-only --diff-filter=ACMR | grep -Ei "\.rs$") || true)
+if [ ! -z "${files}" ]; then
+ cargo dev fmt
+ git add $(echo "$files" | paste -s -d " " -)
+fi