--- /dev/null
- dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
- lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
+[alias]
+uitest = "test --test compile-test"
++dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
++lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
+collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
+
+[build]
+# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
+rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
++target-dir = "target"
--- /dev/null
- `@rustbot label +<label>`
+---
+name: Blank Issue
+about: Create a blank issue.
+---
+
+
+<!--
+Additional labels can be added to this issue by including the following command
+(without the space after the @ symbol):
+
++@ rustbot label +<label>
+
+Common labels for this issue type are:
+* C-an-interesting-project
+* C-enhancement
+* C-question
+* C-tracking-issue
+-->
--- /dev/null
- `@rustbot label +<label>`
+---
+name: Bug Report
+about: Create a bug report for Clippy
+labels: C-bug
+---
+<!--
+Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
+along with any information you feel relevant to replicating the bug.
+-->
+
+I tried this code:
+
+```rust
+<code>
+```
+
+I expected to see this happen: *explanation*
+
+Instead, this happened: *explanation*
+
+### Meta
+
+**Rust version (`rustc -Vv`):**
+
+```
+rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+binary: rustc
+commit-hash: f455e46eae1a227d735091091144601b467e1565
+commit-date: 2020-06-20
+host: x86_64-unknown-linux-gnu
+release: 1.46.0-nightly
+LLVM version: 10.0
+```
+
+<!--
+Additional labels can be added to this issue by including the following command
+(without the space after the @ symbol):
+
++@ rustbot label +<label>
+
+Common labels for this issue type are:
+* `I-suggestion-causes-error`
+-->
--- /dev/null
- `@rustbot label +<label>`
+---
+name: Bug Report (False Positive)
+about: Create a bug report about a wrongly emitted lint warning
+labels: C-bug, I-false-positive
+---
+<!--
+Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
+along with any information you feel relevant to replicating the bug.
+-->
+Lint name:
+
+
+I tried this code:
+
+```rust
+<code>
+```
+
+I expected to see this happen: *explanation*
+
+Instead, this happened: *explanation*
+
+### Meta
+
+**Rust version (`rustc -Vv`):**
+```
+rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+binary: rustc
+commit-hash: f455e46eae1a227d735091091144601b467e1565
+commit-date: 2020-06-20
+host: x86_64-unknown-linux-gnu
+release: 1.46.0-nightly
+LLVM version: 10.0
+```
+
+<!--
+Additional labels can be added to this issue by including the following command
+(without the space after the @ symbol):
+
++@ rustbot label +<label>
+
+Common labels for this issue type are:
+* I-suggestion-causes-error
+-->
--- /dev/null
- [74d1561...master](https://github.com/rust-lang/rust-clippy/compare/74d1561...master)
+# 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
+
- Current beta, release 2021-09-09
++[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
+
- ### Documentation Improvements
-
- * Reworked Clippy's website:
- [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
- [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
- * Added applicability information about lints
- * Added a link to jump into the implementation
- * Improved loading times
- * Adapted some styling
- * Clippy now uses a lint to generate its documentation
- [#7298](https://github.com/rust-lang/rust-clippy/pull/7298)
-
++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)
+
- Current stable, released 2021-07-29
+## Rust 1.54
+
- * [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
++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)
- * [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
++* [`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)
- [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
++* `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
- [`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
++[`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
+[`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
+[`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
- edition = "2018"
+[package]
+name = "clippy"
+version = "0.1.57"
+description = "A bunch of helpful lints to avoid common pitfalls in Rust"
+repository = "https://github.com/rust-lang/rust-clippy"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
+categories = ["development-tools", "development-tools::cargo-plugins"]
+build = "build.rs"
++edition = "2021"
+publish = false
+
+[[bin]]
+name = "cargo-clippy"
+test = false
+path = "src/main.rs"
+
+[[bin]]
+name = "clippy-driver"
+path = "src/driver.rs"
+
+[dependencies]
+clippy_lints = { version = "0.1", path = "clippy_lints" }
+semver = "0.11"
+rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
+tempfile = { version = "3.2", optional = true }
+
+[dev-dependencies]
+cargo_metadata = "0.12"
+compiletest_rs = { version = "0.7", features = ["tmp"] }
+tester = "0.9"
+regex = "1.5"
+# This is used by the `collect-metadata` alias.
+filetime = "0.2"
+
+# A noop dependency that changes in the Rust repository, it's a bit of a hack.
+# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
+# for more information.
+rustc-workspace-hack = "1.0"
+
+# UI test dependencies
+clippy_utils = { path = "clippy_utils" }
+derive-new = "0.5"
+if_chain = "1.0"
+itertools = "0.10"
+quote = "1.0"
+serde = { version = "1.0", features = ["derive"] }
+syn = { version = "1.0", features = ["full"] }
+
+[build-dependencies]
+rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
+
+[features]
+deny-warnings = ["clippy_lints/deny-warnings"]
+integration = ["tempfile"]
+internal-lints = ["clippy_lints/internal-lints"]
+metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
+
+[package.metadata.rust-analyzer]
+# This package uses #[feature(rustc_private)]
+rustc_private = true
--- /dev/null
- | `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
+# Clippy
+
+[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto)
+[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
+
+A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
+
+[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+
+Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
+You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
+
+| Category | Description | Default level |
+| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
+| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
+| `clippy::correctness` | code that is outright wrong or useless | **deny** |
+| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
+| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
+| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
+| `clippy::perf` | code that can be written to run faster | **warn** |
++| `clippy::pedantic` | lints which are rather strict or have occasional false positives | allow |
+| `clippy::nursery` | new lints that are still under development | allow |
+| `clippy::cargo` | lints for the cargo manifest | allow |
+
+More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
+
+The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
+for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
+very selectively, if at all.
+
+Table of contents:
+
+* [Usage instructions](#usage)
+* [Configuration](#configuration)
+* [Contributing](#contributing)
+* [License](#license)
+
+## Usage
+
+Below are instructions on how to use Clippy as a subcommand, compiled from source
+or in Travis CI.
+
+### As a cargo subcommand (`cargo clippy`)
+
+One way to use Clippy is by installing Clippy through rustup as a cargo
+subcommand.
+
+#### Step 1: Install Rustup
+
+You can install [Rustup](https://rustup.rs/) on supported platforms. This will help
+us install Clippy and its dependencies.
+
+If you already have Rustup installed, update to ensure you have the latest
+Rustup and compiler:
+
+```terminal
+rustup update
+```
+
+#### Step 2: Install Clippy
+
+Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command:
+
+```terminal
+rustup component add clippy
+```
+If it says that it can't find the `clippy` component, please run `rustup self update`.
+
+#### Step 3: Run Clippy
+
+Now you can run Clippy by invoking the following command:
+
+```terminal
+cargo clippy
+```
+
+#### Automatically applying Clippy suggestions
+
+Clippy can automatically apply some lint suggestions, just like the compiler.
+
+```terminal
+cargo clippy --fix
+```
+
+#### Workspaces
+
+All the usual workspace options should work with Clippy. For example the following command
+will run Clippy on the `example` crate:
+
+```terminal
+cargo clippy -p example
+```
+
+As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies.
+If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this:
+
+```terminal
+cargo clippy -p example -- --no-deps
+```
+
+### As a rustc replacement (`clippy-driver`)
+
+Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
+your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
+
+```terminal
+rustc --edition 2018 -Cpanic=abort foo.rs
+```
+
+Then, to enable Clippy, you will need to call:
+
+```terminal
+clippy-driver --edition 2018 -Cpanic=abort foo.rs
+```
+
+Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
+
+### Travis CI
+
+You can add Clippy to Travis CI in the same way you use it locally:
+
+```yml
+language: rust
+rust:
+ - stable
+ - beta
+before_script:
+ - rustup component add clippy
+script:
+ - cargo clippy
+ # if you want the build job to fail when encountering warnings, use
+ - cargo clippy -- -D warnings
+ # in order to also check tests and non-default crate features, use
+ - cargo clippy --all-targets --all-features -- -D warnings
+ - cargo test
+ # etc.
+```
+
+Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
+That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
+an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
+line. (You can swap `clippy::all` with the specific lint category you are targeting.)
+
+## Configuration
+
+Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
+value` mapping eg.
+
+```toml
+avoid-breaking-exported-api = false
+blacklisted-names = ["toto", "tata", "titi"]
+cognitive-complexity-threshold = 30
+```
+
+See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
+lints can be configured and the meaning of the variables.
+
+To deactivate the “for further information visit *lint-link*” message you can
+define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
+
+### Allowing/denying lints
+
+You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
+
+* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
+
+* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
+ `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
+ lints prone to false positives.
+
+* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
+
+* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
+
+Note: `allow` means to suppress the lint for your code. With `warn` the lint
+will only emit a warning, while with `deny` the lint will emit an error, when
+triggering for your code. An error causes clippy to exit with an error code, so
+is useful in scripts like CI/CD.
+
+If you do not want to include your lint levels in your code, you can globally
+enable/disable lints by passing extra flags to Clippy during the run:
+
+To allow `lint_name`, run
+
+```terminal
+cargo clippy -- -A clippy::lint_name
+```
+
+And to warn on `lint_name`, run
+
+```terminal
+cargo clippy -- -W clippy::lint_name
+```
+
+This also works with lint groups. For example you
+can run Clippy with warnings for all lints enabled:
+```terminal
+cargo clippy -- -W clippy::pedantic
+```
+
+If you care only about a single lint, you can allow all others and then explicitly warn on
+the lint(s) you are interested in:
+```terminal
+cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
+```
+
+### Specifying the minimum supported Rust version
+
+Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
+specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
+
+```toml
+msrv = "1.30.0"
+```
+
+The MSRV can also be specified as an inner attribute, like below.
+
+```rust
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.30.0"]
+
+fn main() {
+ ...
+}
+```
+
+You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
+is equivalent to `msrv = 1.30.0`.
+
+Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
+
+Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
+
+## Contributing
+
+If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
+
+## License
+
+Copyright 2014-2021 The Rust Project Developers
+
+Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
+<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
+option. Files in the project may not be
+copied, modified, or distributed except according to those terms.
--- /dev/null
- edition = "2018"
+[package]
+name = "clippy_dev"
+version = "0.0.1"
++edition = "2021"
+
+[dependencies]
+bytecount = "0.6"
+clap = "2.33"
+itertools = "0.10"
+opener = "0.5"
+regex = "1.5"
+shell-escape = "0.1"
+walkdir = "2.3"
+
+[features]
+deny-warnings = []
--- /dev/null
- use std::env;
+//! `bless` updates the reference files in the repo with changed output files
+//! from the last test run.
+
- // NOTE: this is duplicated with tests/cargo/mod.rs What to do?
- pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
- Some(v) => v.into(),
- None => env::current_dir().unwrap().join("target"),
- });
-
+use std::ffi::OsStr;
+use std::fs;
+use std::lazy::SyncLazy;
+use std::path::{Path, PathBuf};
+use walkdir::WalkDir;
+
+use crate::clippy_project_root;
+
- let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
- let mut path = PathBuf::from(&**CARGO_TARGET_DIR);
- path.push(profile);
- path.push("cargo-clippy");
+static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
- let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
- let mut path = PathBuf::new();
- path.push(CARGO_TARGET_DIR.clone());
- path.push(profile);
- path.push("test_build_base");
++ let mut path = std::env::current_exe().unwrap();
++ path.set_file_name("cargo-clippy");
+ fs::metadata(path).ok()?.modified().ok()
+});
+
+/// # Panics
+///
+/// Panics if the path to a test file is broken
+pub fn bless(ignore_timestamp: bool) {
+ let test_suite_dirs = [
+ clippy_project_root().join("tests").join("ui"),
+ clippy_project_root().join("tests").join("ui-internal"),
+ clippy_project_root().join("tests").join("ui-toml"),
+ clippy_project_root().join("tests").join("ui-cargo"),
+ ];
+ for test_suite_dir in &test_suite_dirs {
+ WalkDir::new(test_suite_dir)
+ .into_iter()
+ .filter_map(Result::ok)
+ .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+ .for_each(|f| {
+ let test_name = f.path().strip_prefix(test_suite_dir).unwrap();
+ for &ext in &["stdout", "stderr", "fixed"] {
+ let test_name_ext = format!("stage-id.{}", ext);
+ update_reference_file(
+ f.path().with_extension(ext),
+ test_name.with_extension(test_name_ext),
+ ignore_timestamp,
+ );
+ }
+ });
+ }
+}
+
+fn update_reference_file(reference_file_path: PathBuf, test_name: PathBuf, ignore_timestamp: bool) {
+ let test_output_path = build_dir().join(test_name);
+ let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();
+
+ // If compiletest did not write any changes during the test run,
+ // we don't have to update anything
+ if !test_output_path.exists() {
+ return;
+ }
+
+ // If the test output was not updated since the last clippy build, it may be outdated
+ if !ignore_timestamp && !updated_since_clippy_build(&test_output_path).unwrap_or(true) {
+ return;
+ }
+
+ let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
+ let reference_file = fs::read(&reference_file_path).unwrap_or_default();
+
+ if test_output_file != reference_file {
+ // If a test run caused an output file to change, update the reference file
+ println!("updating {}", &relative_reference_file_path.display());
+ fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
+
+ // We need to re-read the file now because it was potentially updated from copying
+ let reference_file = fs::read(&reference_file_path).unwrap_or_default();
+
+ if reference_file.is_empty() {
+ // If we copied over an empty output file, we remove the now empty reference file
+ println!("removing {}", &relative_reference_file_path.display());
+ fs::remove_file(reference_file_path).expect("Could not remove reference file");
+ }
+ }
+}
+
+fn updated_since_clippy_build(path: &Path) -> Option<bool> {
+ let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
+ let modified = fs::metadata(path).ok()?.modified().ok()?;
+ Some(modified >= clippy_build_time)
+}
+
+fn build_dir() -> PathBuf {
++ let mut path = std::env::current_exe().unwrap();
++ path.set_file_name("test");
+ path
+}
--- /dev/null
- edition = "2018"
+[package]
+name = "clippy_lints"
+version = "0.1.57"
+description = "A bunch of helpful lints to avoid common pitfalls in Rust"
+repository = "https://github.com/rust-lang/rust-clippy"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
++edition = "2021"
+
+[dependencies]
+cargo_metadata = "0.12"
+clippy_utils = { path = "../clippy_utils" }
+if_chain = "1.0"
+itertools = "0.10"
+pulldown-cmark = { version = "0.8", default-features = false }
+quine-mc_cluskey = "0.2"
+regex-syntax = "0.6"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = { version = "1.0", optional = true }
+toml = "0.5"
+unicode-normalization = "0.1"
+unicode-script = { version = "0.5", default-features = false }
+semver = "0.11"
+rustc-semver = "1.1"
+# NOTE: cargo requires serde feat in its url dep
+# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
+url = { version = "2.2", features = ["serde"] }
+
+[features]
+deny-warnings = ["clippy_utils/deny-warnings"]
+# build clippy with internal lints enabled, off by default
+internal-lints = ["clippy_utils/internal-lints"]
+metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
+
+[package.metadata.rust-analyzer]
+# This crate uses #[feature(rustc_private)]
+rustc_private = true
--- /dev/null
- Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath,
+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},
- use rustc_middle::ty::TypeFoldable;
++ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
- if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) {
- return;
+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());
+ 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
- use clippy_utils::diagnostics::span_lint;
++use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::fn_def_id;
+
- use rustc_data_structures::fx::FxHashSet;
- use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
++use rustc_hir::{def::Res, def_id::DefIdMap, Crate, Expr};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
- use rustc_span::Symbol;
++
++use crate::utils::conf;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Denies the configured methods and functions in clippy.toml
+ ///
+ /// ### Why is this bad?
- /// Some methods are undesirable in certain contexts,
- /// and it's beneficial to lint for them as needed.
++ /// Some methods are undesirable in certain contexts, and it's beneficial to
++ /// lint for them as needed.
+ ///
+ /// ### Example
+ /// An example clippy.toml configuration:
+ /// ```toml
+ /// # clippy.toml
- /// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
++ /// disallowed-methods = [
++ /// # Can use a string as the path of the disallowed method.
++ /// "std::boxed::Box::new",
++ /// # Can also use an inline table with a `path` key.
++ /// { path = "std::time::Instant::now" },
++ /// # When using an inline table, can add a `reason` for why the method
++ /// # is disallowed.
++ /// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
++ /// ]
+ /// ```
+ ///
+ /// ```rust,ignore
+ /// // Example code where clippy issues a warning
+ /// let xs = vec![1, 2, 3, 4];
+ /// xs.leak(); // Vec::leak is disallowed in the config.
++ /// // The diagnostic contains the message "no leaking memory".
+ ///
+ /// let _now = Instant::now(); // Instant::now is disallowed in the config.
++ ///
++ /// let _box = Box::new(3); // Box::new is disallowed in the config.
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// // Example code which does not raise clippy warning
+ /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
+ /// xs.push(123); // Vec::push is _not_ disallowed in the config.
+ /// ```
+ pub DISALLOWED_METHOD,
+ nursery,
+ "use of a disallowed method call"
+}
+
+#[derive(Clone, Debug)]
+pub struct DisallowedMethod {
- disallowed: FxHashSet<Vec<Symbol>>,
- def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
++ conf_disallowed: Vec<conf::DisallowedMethod>,
++ disallowed: DefIdMap<Option<String>>,
+}
+
+impl DisallowedMethod {
- pub fn new(disallowed: &FxHashSet<String>) -> Self {
++ pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
+ Self {
- disallowed: disallowed
- .iter()
- .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
- .collect(),
- def_ids: FxHashSet::default(),
++ conf_disallowed,
++ disallowed: DefIdMap::default(),
+ }
+ }
+}
+
+impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
+
+impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
+ fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
- for path in &self.disallowed {
- let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
- if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
- {
- self.def_ids.insert((id, path.clone()));
++ for conf in &self.conf_disallowed {
++ let (path, reason) = match conf {
++ conf::DisallowedMethod::Simple(path) => (path, None),
++ conf::DisallowedMethod::WithReason { path, reason } => (
++ path,
++ reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
++ ),
++ };
++ let segs: Vec<_> = path.split("::").collect();
++ if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
++ self.disallowed.insert(id, reason);
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let Some(def_id) = fn_def_id(cx, expr) {
- if self.def_ids.iter().any(|(id, _)| def_id == *id) {
- let func_path = cx.get_def_path(def_id);
- let func_path_string = func_path
- .into_iter()
- .map(Symbol::to_ident_string)
- .collect::<Vec<_>>()
- .join("::");
-
- span_lint(
- cx,
- DISALLOWED_METHOD,
- expr.span,
- &format!("use of a disallowed method `{}`", func_path_string),
- );
++ let def_id = match fn_def_id(cx, expr) {
++ Some(def_id) => def_id,
++ None => return,
++ };
++ let reason = match self.disallowed.get(&def_id) {
++ Some(reason) => reason,
++ None => return,
++ };
++ let func_path = cx.tcx.def_path_str(def_id);
++ let msg = format!("use of a disallowed method `{}`", func_path);
++ span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| {
++ if let Some(reason) = reason {
++ diag.note(reason);
+ }
- }
++ });
+ }
+}
--- /dev/null
- .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
+use clippy_utils::diagnostics::span_lint;
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{
+ def::Res, def_id::DefId, Crate, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
+};
+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
+ /// Denies the configured types in clippy.toml.
+ ///
+ /// ### Why is this bad?
+ /// Some types are undesirable in certain contexts.
+ ///
+ /// ### Example:
+ /// An example clippy.toml configuration:
+ /// ```toml
+ /// # clippy.toml
+ /// disallowed-types = ["std::collections::BTreeMap"]
+ /// ```
+ ///
+ /// ```rust,ignore
+ /// use std::collections::BTreeMap;
+ /// // or its use
+ /// let x = std::collections::BTreeMap::new();
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// // A similar type that is allowed by the config
+ /// use std::collections::HashMap;
+ /// ```
+ pub DISALLOWED_TYPE,
+ nursery,
+ "use of a disallowed type"
+}
+#[derive(Clone, Debug)]
+pub struct DisallowedType {
+ disallowed: FxHashSet<Vec<Symbol>>,
+ def_ids: FxHashSet<DefId>,
+ prim_tys: FxHashSet<PrimTy>,
+}
+
+impl DisallowedType {
+ pub fn new(disallowed: &FxHashSet<String>) -> Self {
+ Self {
+ disallowed: disallowed
+ .iter()
++ .map(|s| s.split("::").map(Symbol::intern).collect::<Vec<_>>())
+ .collect(),
+ def_ids: FxHashSet::default(),
+ prim_tys: FxHashSet::default(),
+ }
+ }
+
+ fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
+ match res {
+ Res::Def(_, did) => {
+ if self.def_ids.contains(did) {
+ emit(cx, &cx.tcx.def_path_str(*did), span);
+ }
+ },
+ Res::PrimTy(prim) => {
+ if self.prim_tys.contains(prim) {
+ emit(cx, prim.name_str(), span);
+ }
+ },
+ _ => {},
+ }
+ }
+}
+
+impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
+
+impl<'tcx> LateLintPass<'tcx> for DisallowedType {
+ fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
+ for path in &self.disallowed {
+ let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
+ match clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>()) {
+ Res::Def(_, id) => {
+ self.def_ids.insert(id);
+ },
+ Res::PrimTy(ty) => {
+ self.prim_tys.insert(ty);
+ },
+ _ => {},
+ }
+ }
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ if let ItemKind::Use(path, UseKind::Single) = &item.kind {
+ self.check_res_emit(cx, &path.res, item.span);
+ }
+ }
+
+ fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
+ if let TyKind::Path(path) = &ty.kind {
+ self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
+ }
+ }
+
+ fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
+ self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
+ }
+}
+
+fn emit(cx: &LateContext<'_>, name: &str, span: Span) {
+ span_lint(
+ cx,
+ DISALLOWED_TYPE,
+ span,
+ &format!("`{}` is not allowed according to config", name),
+ );
+}
--- /dev/null
- trait_self_ty =
- Some(TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id()).self_ty().skip_binder());
+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 let AssocItemKind::Fn { has_self: true } = trait_item.kind {
++ 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
- use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+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::{get_enclosing_loop_or_closure, higher};
- use clippy_utils::{is_adjusted, iter_input_pats};
+use clippy_utils::usage::UsedAfterExprVisitor;
- use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
- use rustc_lint::{LateContext, LateLintPass, LintContext};
- use rustc_middle::lint::in_external_macro;
- use rustc_middle::ty::{self, ClosureKind, Ty};
++use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
- /// ### Known problems
- /// [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),
- /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),
- /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)
- ///
- ///
++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.
+ ///
- if in_external_macro(cx.sess(), expr.span) {
+ /// ### 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<'_>) {
-
- match expr.kind {
- ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
- for arg in args {
- // skip `foo(macro!())`
- if arg.span.ctxt() == expr.span.ctxt() {
- check_closure(cx, arg);
- }
- }
- },
- _ => (),
- }
- }
- }
-
- fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
- let body = cx.tcx.hir().body(eid);
- let ex = &body.value;
-
- if ex.span.ctxt() != expr.span.ctxt() {
- if decl.inputs.is_empty() {
- if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) {
++ if expr.span.from_expansion() {
+ return;
+ }
- if_chain!(
- if let ExprKind::Call(caller, args) = ex.kind;
-
- if let ExprKind::Path(_) = caller.kind;
-
- // Not the same number of arguments, there is no way the closure is the same as the function return;
- if args.len() == decl.inputs.len();
-
- // Are the expression or the arguments type-adjusted? Then we need the closure
- if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));
-
- let fn_ty = cx.typeck_results().expr_ty(caller);
-
- if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
-
- if !type_is_unsafe_function(cx, fn_ty);
-
- if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
++ 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;
+ }
+
- if let Some(mut snippet) = snippet_opt(cx, caller.span) {
++ 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 ty::Closure(_, substs) = fn_ty.kind();
++ if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+ if_chain! {
- if UsedAfterExprVisitor::is_found(cx, caller)
- || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
++ if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
+ if let ClosureKind::FnMut = substs.as_closure().kind();
- if let ExprKind::MethodCall(path, _, args, _) = ex.kind;
-
- // Not the same number of arguments, there is no way the closure is the same as the function return;
- if args.len() == decl.inputs.len();
-
- // Are the expression or the arguments type-adjusted? Then we need the closure
- if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));
-
- let method_def_id = cx.typeck_results().type_dependent_def_id(ex.hir_id).unwrap();
- if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));
-
- if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
-
- if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);
-
++ 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!(
- span_lint_and_sugg(
- cx,
- REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
- expr.span,
- "redundant closure",
- "replace the closure with the method itself",
- format!("{}::{}", name, path.ident.name),
- Applicability::MachineApplicable,
- );
++ 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 {
- /// Tries to determine the type for universal function call to be used instead of the closure
- fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option<String> {
- let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0];
- let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id);
-
- if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
- if match_borrow_depth(expected_type_of_self, actual_type_of_self)
- && implements_trait(cx, actual_type_of_self, trait_id, &[])
- {
- return Some(cx.tcx.def_path_str(trait_id));
- }
++ 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,
++ );
++ })
+ }
+ );
+ }
+}
+
-
- cx.tcx.impl_of_method(method_def_id).and_then(|_| {
- //a type may implicitly implement other type's methods (e.g. Deref)
- if match_types(expected_type_of_self, actual_type_of_self) {
- Some(get_type_name(cx, actual_type_of_self))
- } else {
- None
++fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
++ if params.len() != call_args.len() {
++ return false;
+ }
- fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
- match (&lhs.kind(), &rhs.kind()) {
- (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2),
- (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
- }
- }
-
- fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
- match (&lhs.kind(), &rhs.kind()) {
- (ty::Bool, ty::Bool)
- | (ty::Char, ty::Char)
- | (ty::Int(_), ty::Int(_))
- | (ty::Uint(_), ty::Uint(_))
- | (ty::Str, ty::Str) => true,
- (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2),
- (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
- (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
- (_, _) => 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 get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String {
- match ty.kind() {
- ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
- ty::Ref(_, r, _) => get_type_name(cx, r),
- _ => ty.to_string(),
++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;
+ }
- fn compare_inputs(
- closure_inputs: &mut dyn Iterator<Item = &Param<'_>>,
- call_args: &mut dyn Iterator<Item = &Expr<'_>>,
- ) -> bool {
- for (closure_input, function_arg) in closure_inputs.zip(call_args) {
- if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind {
- // XXXManishearth Should I be checking the binding mode here?
- if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind {
- if p.segments.len() != 1 {
- // If it's a proper path, it can't be a local variable
- return false;
- }
- if p.segments[0].ident.name != ident.name {
- // The two idents should be the same
- return false;
- }
- } else {
- 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)
+}
+
- } else {
- return false;
- }
++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(),
+ }
- true
++ },
+ }
+}
--- /dev/null
- } else if digits > max as usize && sym_str != float_str {
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal;
+use if_chain::if_chain;
+use rustc_ast::ast::{self, LitFloatType, LitKind};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, FloatTy};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use std::fmt;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for float literals with a precision greater
+ /// than that supported by the underlying type.
+ ///
+ /// ### Why is this bad?
+ /// Rust will truncate the literal silently.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let v: f32 = 0.123_456_789_9;
+ /// println!("{}", v); // 0.123_456_789
+ ///
+ /// // Good
+ /// let v: f64 = 0.123_456_789_9;
+ /// println!("{}", v); // 0.123_456_789_9
+ /// ```
+ pub EXCESSIVE_PRECISION,
+ style,
+ "excessive precision for float literal"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for whole number float literals that
+ /// cannot be represented as the underlying type without loss.
+ ///
+ /// ### Why is this bad?
+ /// Rust will silently lose precision during
+ /// conversion to a float.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let _: f32 = 16_777_217.0; // 16_777_216.0
+ ///
+ /// // Good
+ /// let _: f32 = 16_777_216.0;
+ /// let _: f64 = 16_777_217.0;
+ /// ```
+ pub LOSSY_FLOAT_LITERAL,
+ restriction,
+ "lossy whole number float literals"
+}
+
+declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
+
+impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ let ty = cx.typeck_results().expr_ty(expr);
+ if_chain! {
+ if let ty::Float(fty) = *ty.kind();
+ if let hir::ExprKind::Lit(ref lit) = expr.kind;
+ if let LitKind::Float(sym, lit_float_ty) = lit.node;
+ then {
+ let sym_str = sym.as_str();
+ let formatter = FloatFormat::new(&sym_str);
+ // Try to bail out if the float is for sure fine.
+ // If its within the 2 decimal digits of being out of precision we
+ // check if the parsed representation is the same as the string
+ // since we'll need the truncated string anyway.
+ let digits = count_digits(&sym_str);
+ let max = max_digits(fty);
+ let type_suffix = match lit_float_ty {
+ LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"),
+ LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"),
+ LitFloatType::Unsuffixed => None
+ };
+ let (is_whole, mut float_str) = match fty {
+ FloatTy::F32 => {
+ let value = sym_str.parse::<f32>().unwrap();
+
+ (value.fract() == 0.0, formatter.format(value))
+ },
+ FloatTy::F64 => {
+ let value = sym_str.parse::<f64>().unwrap();
+
+ (value.fract() == 0.0, formatter.format(value))
+ },
+ };
+
+ if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
+ // Normalize the literal by stripping the fractional portion
+ if sym_str.split('.').next().unwrap() != float_str {
+ // If the type suffix is missing the suggestion would be
+ // incorrectly interpreted as an integer so adding a `.0`
+ // suffix to prevent that.
+ if type_suffix.is_none() {
+ float_str.push_str(".0");
+ }
+
+ span_lint_and_sugg(
+ cx,
+ LOSSY_FLOAT_LITERAL,
+ expr.span,
+ "literal cannot be represented as the underlying type without loss of precision",
+ "consider changing the type or replacing it with",
+ numeric_literal::format(&float_str, type_suffix, true),
+ Applicability::MachineApplicable,
+ );
+ }
++ } else if digits > max as usize && float_str.len() < sym_str.len() {
+ span_lint_and_sugg(
+ cx,
+ EXCESSIVE_PRECISION,
+ expr.span,
+ "float has excessive precision",
+ "consider changing the type or truncating it to",
+ numeric_literal::format(&float_str, type_suffix, true),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+}
+
+#[must_use]
+fn max_digits(fty: FloatTy) -> u32 {
+ match fty {
+ FloatTy::F32 => f32::DIGITS,
+ FloatTy::F64 => f64::DIGITS,
+ }
+}
+
+/// Counts the digits excluding leading zeros
+#[must_use]
+fn count_digits(s: &str) -> usize {
+ // Note that s does not contain the f32/64 suffix, and underscores have been stripped
+ s.chars()
+ .filter(|c| *c != '-' && *c != '.')
+ .take_while(|c| *c != 'e' && *c != 'E')
+ .fold(0, |count, c| {
+ // leading zeros
+ if c == '0' && count == 0 { count } else { count + 1 }
+ })
+}
+
+enum FloatFormat {
+ LowerExp,
+ UpperExp,
+ Normal,
+}
+impl FloatFormat {
+ #[must_use]
+ fn new(s: &str) -> Self {
+ s.chars()
+ .find_map(|x| match x {
+ 'e' => Some(Self::LowerExp),
+ 'E' => Some(Self::UpperExp),
+ _ => None,
+ })
+ .unwrap_or(Self::Normal)
+ }
+ fn format<T>(&self, f: T) -> String
+ where
+ T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
+ {
+ match self {
+ Self::LowerExp => format!("{:e}", f),
+ Self::UpperExp => format!("{:E}", f),
+ Self::Normal => format!("{}", f),
+ }
+ }
+}
--- /dev/null
- if format_args.args.iter().all(|e| is_display_arg(e));
- if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
+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_type, 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.as_str() == "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
- if !differing_macro_contexts(first.span, second.span)
- && !first.span.from_expansion()
- && is_if(first)
- && (is_block(second) || is_if(second))
- {
- // where the else would be
- let else_span = first.span.between(second.span);
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
+use clippy_utils::differing_macro_contexts;
+use clippy_utils::source::snippet_opt;
+use if_chain::if_chain;
+use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of the non-existent `=*`, `=!` and `=-`
+ /// operators.
+ ///
+ /// ### Why is this bad?
+ /// This is either a typo of `*=`, `!=` or `-=` or
+ /// confusing.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
+ /// ```
+ pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
+ suspicious,
+ "suspicious formatting of `*=`, `-=` or `!=`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks the formatting of a unary operator on the right hand side
+ /// of a binary operator. It lints if there is no space between the binary and unary operators,
+ /// but there is a space between the unary and its operand.
+ ///
+ /// ### Why is this bad?
+ /// This is either a typo in the binary operator or confusing.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator
+ /// }
+ ///
+ /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
+ /// }
+ /// ```
+ pub SUSPICIOUS_UNARY_OP_FORMATTING,
+ suspicious,
+ "suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for formatting of `else`. It lints if the `else`
+ /// is followed immediately by a newline or the `else` seems to be missing.
+ ///
+ /// ### Why is this bad?
+ /// This is probably some refactoring remnant, even if the
+ /// code is correct, it might look confusing.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// if foo {
+ /// } { // looks like an `else` is missing here
+ /// }
+ ///
+ /// if foo {
+ /// } if bar { // looks like an `else` is missing here
+ /// }
+ ///
+ /// if foo {
+ /// } else
+ ///
+ /// { // this is the `else` block of the previous `if`, but should it be?
+ /// }
+ ///
+ /// if foo {
+ /// } else
+ ///
+ /// if bar { // this is the `else` block of the previous `if`, but should it be?
+ /// }
+ /// ```
+ pub SUSPICIOUS_ELSE_FORMATTING,
+ suspicious,
+ "suspicious formatting of `else`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for possible missing comma in an array. It lints if
+ /// an array element is a binary operator expression and it lies on two lines.
+ ///
+ /// ### Why is this bad?
+ /// This could lead to unexpected results.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let a = &[
+ /// -1, -2, -3 // <= no comma here
+ /// -4, -5, -6
+ /// ];
+ /// ```
+ pub POSSIBLE_MISSING_COMMA,
+ correctness,
+ "possible missing comma in array"
+}
+
+declare_lint_pass!(Formatting => [
+ SUSPICIOUS_ASSIGNMENT_FORMATTING,
+ SUSPICIOUS_UNARY_OP_FORMATTING,
+ SUSPICIOUS_ELSE_FORMATTING,
+ POSSIBLE_MISSING_COMMA
+]);
+
+impl EarlyLintPass for Formatting {
+ fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
+ for w in block.stmts.windows(2) {
+ if let (StmtKind::Expr(first), StmtKind::Expr(second) | StmtKind::Semi(second)) = (&w[0].kind, &w[1].kind) {
+ check_missing_else(cx, first, second);
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ check_assign(cx, expr);
+ check_unop(cx, expr);
+ check_else(cx, expr);
+ check_array(cx, expr);
+ }
+}
+
+/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
+fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
+ if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
+ if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion() {
+ let eq_span = lhs.span.between(rhs.span);
+ if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
+ if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
+ let op = UnOp::to_string(op);
+ let eqop_span = lhs.span.between(sub_rhs.span);
+ if eq_snippet.ends_with('=') {
+ span_lint_and_note(
+ cx,
+ SUSPICIOUS_ASSIGNMENT_FORMATTING,
+ eqop_span,
+ &format!(
+ "this looks like you are trying to use `.. {op}= ..`, but you \
+ really are doing `.. = ({op} ..)`",
+ op = op
+ ),
+ None,
+ &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
+ );
+ }
+ }
+ }
+ }
+ }
+}
+
+/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
+fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
+ if_chain! {
+ if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
+ if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion();
+ // span between BinOp LHS and RHS
+ let binop_span = lhs.span.between(rhs.span);
+ // if RHS is an UnOp
+ if let ExprKind::Unary(op, ref un_rhs) = rhs.kind;
+ // from UnOp operator to UnOp operand
+ let unop_operand_span = rhs.span.until(un_rhs.span);
+ if let Some(binop_snippet) = snippet_opt(cx, binop_span);
+ if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span);
+ let binop_str = BinOpKind::to_string(&binop.node);
+ // no space after BinOp operator and space after UnOp operator
+ if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ');
+ then {
+ let unop_str = UnOp::to_string(op);
+ let eqop_span = lhs.span.between(un_rhs.span);
+ span_lint_and_help(
+ cx,
+ SUSPICIOUS_UNARY_OP_FORMATTING,
+ eqop_span,
+ &format!(
+ "by not having a space between `{binop}` and `{unop}` it looks like \
+ `{binop}{unop}` is a single operator",
+ binop = binop_str,
+ unop = unop_str
+ ),
+ None,
+ &format!(
+ "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`",
+ binop = binop_str,
+ unop = unop_str
+ ),
+ );
+ }
+ }
+}
+
+/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
+fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
+ if_chain! {
+ if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
+ if is_block(else_) || is_if(else_);
+ if !differing_macro_contexts(then.span, else_.span);
+ if !then.span.from_expansion() && !in_external_macro(cx.sess, expr.span);
+
+ // workaround for rust-lang/rust#43081
+ if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
+
+ // this will be a span from the closing ‘}’ of the “then” block (excluding) to
+ // the “if” of the “else if” block (excluding)
+ let else_span = then.span.between(else_.span);
+
+ // the snippet should look like " else \n " with maybe comments anywhere
+ // it’s bad when there is a ‘\n’ after the “else”
+ if let Some(else_snippet) = snippet_opt(cx, else_span);
+ if let Some((pre_else, post_else)) = else_snippet.split_once("else");
+ if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
+
+ then {
+ // Allow allman style braces `} \n else \n {`
+ if_chain! {
+ if is_block(else_);
+ if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
+ // Exactly one eol before and after the else
+ if !pre_else_post_eol.contains('\n');
+ if !post_else_post_eol.contains('\n');
+ then {
+ return;
+ }
+ }
+
+ let else_desc = if is_if(else_) { "if" } else { "{..}" };
+ span_lint_and_note(
+ cx,
+ SUSPICIOUS_ELSE_FORMATTING,
+ else_span,
+ &format!("this is an `else {}` but the formatting might hide it", else_desc),
+ None,
+ &format!(
+ "to remove this lint, remove the `else` or remove the new line between \
+ `else` and `{}`",
+ else_desc,
+ ),
+ );
+ }
+ }
+}
+
+#[must_use]
+fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
+ // &, *, -
+ bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
+}
+
+fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize {
+ cx.sess.source_map().lookup_char_pos(span.lo()).col.0
+}
+
+/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
+fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
+ if let ExprKind::Array(ref array) = expr.kind {
+ for element in array {
+ if_chain! {
+ if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
+ if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span);
+ let space_span = lhs.span.between(op.span);
+ if let Some(space_snippet) = snippet_opt(cx, space_span);
+ let lint_span = lhs.span.with_lo(lhs.span.hi());
+ if space_snippet.contains('\n');
+ if indentation(cx, op.span) <= indentation(cx, lhs.span);
+ then {
+ span_lint_and_note(
+ cx,
+ POSSIBLE_MISSING_COMMA,
+ lint_span,
+ "possibly missing a comma here",
+ None,
+ "to remove this lint, add a comma or write the expr in a single line",
+ );
+ }
+ }
+ }
+ }
+}
+
+fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
- if let Some(else_snippet) = snippet_opt(cx, else_span) {
- if !else_snippet.contains('\n') {
- let (looks_like, next_thing) = if is_if(second) {
- ("an `else if`", "the second `if`")
- } else {
- ("an `else {..}`", "the next block")
- };
++ if_chain! {
++ if !differing_macro_contexts(first.span, second.span);
++ if !first.span.from_expansion();
++ if let ExprKind::If(cond_expr, ..) = &first.kind;
++ if is_block(second) || is_if(second);
+
- span_lint_and_note(
- cx,
- SUSPICIOUS_ELSE_FORMATTING,
- else_span,
- &format!("this looks like {} but the `else` is missing", looks_like),
- None,
- &format!(
- "to remove this lint, add the missing `else` or add a new line before {}",
- next_thing,
- ),
- );
- }
++ // Proc-macros can give weird spans. Make sure this is actually an `if`.
++ if let Some(if_snip) = snippet_opt(cx, first.span.until(cond_expr.span));
++ if if_snip.starts_with("if");
+
++ // If there is a line break between the two expressions, don't lint.
++ // If there is a non-whitespace character, this span came from a proc-macro.
++ let else_span = first.span.between(second.span);
++ if let Some(else_snippet) = snippet_opt(cx, else_span);
++ if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace());
++ then {
++ let (looks_like, next_thing) = if is_if(second) {
++ ("an `else if`", "the second `if`")
++ } else {
++ ("an `else {..}`", "the next block")
++ };
++
++ span_lint_and_note(
++ cx,
++ SUSPICIOUS_ELSE_FORMATTING,
++ else_span,
++ &format!("this looks like {} but the `else` is missing", looks_like),
++ None,
++ &format!(
++ "to remove this lint, add the missing `else` or add a new line before {}",
++ next_thing,
++ ),
++ );
+ }
+ }
+}
+
+fn is_block(expr: &Expr) -> bool {
+ matches!(expr.kind, ExprKind::Block(..))
+}
+
+/// Check if the expression is an `if` or `if let`
+fn is_if(expr: &Expr) -> bool {
+ matches!(expr.kind, ExprKind::If(..))
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::higher::PanicExpn;
++use clippy_utils::is_expn_of;
++use clippy_utils::source::snippet_with_applicability;
++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);
++
++ 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()
++ } else {
++ format!("!{}", snippet_with_applicability(cx, cond.span, "..", &mut applicability))
++ };
++
++ 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
--- /dev/null
++use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait};
++use rustc_hir::{ImplItem, ImplItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::kw;
++use rustc_span::symbol::sym;
++
++declare_clippy_lint! {
++ /// ### What it does
++ /// Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`.
++ ///
++ /// ### Why is this bad?
++ /// Methods named `iter` or `iter_mut` conventionally return an `Iterator`.
++ ///
++ /// ### Example
++ /// ```rust
++ /// // `String` does not implement `Iterator`
++ /// struct Data {}
++ /// impl Data {
++ /// fn iter(&self) -> String {
++ /// todo!()
++ /// }
++ /// }
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// use std::str::Chars;
++ /// struct Data {}
++ /// impl Data {
++ /// fn iter(&self) -> Chars<'static> {
++ /// todo!()
++ /// }
++ /// }
++ /// ```
++ pub ITER_NOT_RETURNING_ITERATOR,
++ pedantic,
++ "methods named `iter` or `iter_mut` that do not return an `Iterator`"
++}
++
++declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
++
++impl LateLintPass<'_> for IterNotReturningIterator {
++ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
++ let name: &str = &impl_item.ident.name.as_str();
++ if_chain! {
++ if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind;
++ let ret_ty = return_ty(cx, impl_item.hir_id());
++ if matches!(name, "iter" | "iter_mut");
++ if let [param] = cx.tcx.fn_arg_names(impl_item.def_id);
++ if param.name == kw::SelfLower;
++ if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
++ if !implements_trait(cx, ret_ty, iter_trait_id, &[]);
++
++ then {
++ span_lint(
++ cx,
++ ITER_NOT_RETURNING_ITERATOR,
++ fn_sig.span,
++ &format!("this method is named `{}` but its return type does not implement `Iterator`", name),
++ );
++ }
++ }
++ }
++}
--- /dev/null
- mod if_let_some_result;
+// 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 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;
- if_let_some_result::IF_LET_SOME_RESULT,
+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 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 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`
+
+ // 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,
- types::BOX_VEC,
+ 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,
- LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
++ 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),
+ ]);
+
+ #[cfg(feature = "internal-lints")]
+ 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(misc::FLOAT_CMP),
++ 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(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
+ 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(types::BOX_VEC),
+ 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(if_let_some_result::IF_LET_SOME_RESULT),
++ 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(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
++ 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(misc::FLOAT_CMP),
+ 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(types::BOX_VEC),
+ 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),
- store.register_late_pass(|| Box::new(regex::Regex::default()));
++ 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),
+ ]);
+
+ 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),
+ ]);
+
+ #[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));
+ 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(if_let_some_result::OkIfLet));
++ 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));
- let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
- store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(&disallowed_methods)));
++ 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(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));
+}
+
+#[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
- pub(super) fn check<'tcx>(
- cx: &LateContext<'tcx>,
- pat: &'tcx Pat<'_>,
- arg: &'tcx Expr<'_>,
- body: &'tcx Expr<'_>,
- ) {
+use super::FOR_KV_MAP;
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::is_local_used;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+/// Checks for the `FOR_KV_MAP` lint.
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
+ let pat_span = pat.span;
+
+ if let PatKind::Tuple(pat, _) = pat.kind {
+ if pat.len() == 2 {
+ let arg_span = arg.span;
+ let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() {
+ ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
+ (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl),
+ (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not),
+ _ => return,
+ },
+ _ => return,
+ };
+ let mutbl = match mutbl {
+ Mutability::Not => "",
+ Mutability::Mut => "_mut",
+ };
+ let arg = match arg.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr,
+ _ => arg,
+ };
+
+ if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) {
+ span_lint_and_then(
+ cx,
+ FOR_KV_MAP,
+ arg_span,
+ &format!("you seem to want to iterate on a map's {}s", kind),
+ |diag| {
+ let map = sugg::Sugg::hir(cx, arg, "map");
+ multispan_sugg(
+ diag,
+ "use the corresponding method",
+ vec![
+ (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
+ (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
+ ],
+ );
+ },
+ );
+ }
+ }
+ }
+}
+
+/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
+fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
+ match *pat {
+ PatKind::Wild => true,
+ PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
+ _ => false,
+ }
+}
--- /dev/null
- ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
+use super::WHILE_LET_LOOP;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::Applicability;
+use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+
+pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
+ // extract the expression from the first statement (if any) in a block
+ let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
+ // or extract the first expression (if any) from the block
+ if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) {
+ if let Some(higher::IfLet {
+ let_pat,
+ let_expr,
+ if_else: Some(if_else),
+ ..
+ }) = higher::IfLet::hir(cx, inner)
+ {
+ if is_simple_break_expr(if_else) {
+ could_be_while_let(cx, expr, let_pat, let_expr);
+ }
+ }
+
+ if let ExprKind::Match(matchexpr, arms, MatchSource::Normal) = inner.kind {
+ if arms.len() == 2
+ && arms[0].guard.is_none()
+ && arms[1].guard.is_none()
+ && is_simple_break_expr(arms[1].body)
+ {
+ could_be_while_let(cx, expr, arms[0].pat, matchexpr);
+ }
+ }
+ }
+}
+
+/// If a block begins with a statement (possibly a `let` binding) and has an
+/// expression, return it.
+fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ if let Some(first_stmt) = block.stmts.get(0) {
+ if let StmtKind::Local(local) = first_stmt.kind {
+ return local.init;
+ }
+ }
+ None
+}
+
+/// If a block begins with an expression (with or without semicolon), return it.
+fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ match block.expr {
+ Some(expr) if block.stmts.is_empty() => Some(expr),
+ None if !block.stmts.is_empty() => match block.stmts[0].kind {
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some(expr),
+ StmtKind::Local(..) | StmtKind::Item(..) => None,
+ },
+ _ => None,
+ }
+}
+
+/// Returns `true` if expr contains a single break expr without destination label
+/// and
+/// passed expression. The expression may be within a block.
+fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
+ match expr.kind {
+ ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
++ ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, is_simple_break_expr),
+ _ => false,
+ }
+}
+
+fn could_be_while_let<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ let_pat: &'tcx Pat<'_>,
+ let_expr: &'tcx Expr<'_>,
+) {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+
+ // NOTE: we used to build a body here instead of using
+ // ellipsis, this was removed because:
+ // 1) it was ugly with big bodies;
+ // 2) it was not indented properly;
+ // 3) it wasn’t very smart (see #675).
+ let mut applicability = Applicability::HasPlaceholders;
+ span_lint_and_sugg(
+ cx,
+ WHILE_LET_LOOP,
+ expr.span,
+ "this loop could be written as a `while let` loop",
+ "try",
+ format!(
+ "while let {} = {} {{ .. }}",
+ snippet_with_applicability(cx, let_pat.span, "..", &mut applicability),
+ snippet_with_applicability(cx, let_expr.span, "..", &mut applicability),
+ ),
+ applicability,
+ );
+}
--- /dev/null
- use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
+use super::WHILE_LET_ON_ITERATOR;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{
+ get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
+};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
- let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
- if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) {
- // Reborrow for mutable references. It may not be possible to get a mutable reference here.
- "&mut *"
- } else {
- "&mut "
- }
++use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp};
+use rustc_lint::LateContext;
+use rustc_span::{symbol::sym, Span, Symbol};
+
+pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
+ if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
+ // check for `Some(..)` pattern
+ if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
+ if let Res::Def(_, pat_did) = pat_path.res;
+ if match_def_path(cx, pat_did, &paths::OPTION_SOME);
+ // check for call to `Iterator::next`
+ if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = let_expr.kind;
+ if method_name.ident.name == sym::next;
+ if is_trait_method(cx, let_expr, sym::Iterator);
+ if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr);
+ // get the loop containing the match expression
+ if !uses_iter(cx, &iter_expr_struct, if_then);
+ then {
+ (let_expr, iter_expr_struct, some_pat, expr)
+ } else {
+ return;
+ }
+ };
+
+ let mut applicability = Applicability::MachineApplicable;
+ let loop_var = if let Some(some_pat) = some_pat.first() {
+ if is_refutable(cx, some_pat) {
+ // Refutable patterns don't work with for loops.
+ return;
+ }
+ snippet_with_applicability(cx, some_pat.span, "..", &mut applicability)
+ } else {
+ "_".into()
+ };
+
+ // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
+ // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
+ // afterwards a mutable borrow of a field isn't necessary.
- format!("for {} in {}{}", loop_var, ref_mut, iterator),
++ let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
++ ".by_ref()"
+ } else {
+ ""
+ };
+
+ let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ WHILE_LET_ON_ITERATOR,
+ expr.span.with_hi(scrutinee_expr.span.hi()),
+ "this loop could be written as a `for` loop",
+ "try",
- /// The HIR id of the whole expression, not just the path and fields stored here.
- hir_id: HirId,
++ format!("for {} in {}{}", loop_var, iterator, by_ref),
+ applicability,
+ );
+}
+
+#[derive(Debug)]
+struct IterExpr {
+ /// The span of the whole expression, not just the path and fields stored here.
+ span: Span,
- let hir_id = e.hir_id;
+ /// The fields used, in order of child to parent.
+ fields: Vec<Symbol>,
+ /// The path being used.
+ path: Res,
+}
+
+/// Parses any expression to find out which field of which variable is used. Will return `None` if
+/// the expression might have side effects.
+fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
+ let span = e.span;
- hir_id,
+ let mut fields = Vec::new();
+ loop {
+ match e.kind {
+ ExprKind::Path(ref path) => {
+ break Some(IterExpr {
+ span,
+ fields,
+ path: cx.qpath_res(path, e.hir_id),
+ });
+ },
+ ExprKind::Field(base, name) => {
+ fields.push(name.name);
+ e = base;
+ },
+ // Dereferencing a pointer has no side effects and doesn't affect which field is being used.
+ ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base,
+
+ // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
+ // already been seen.
+ ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
+ fields.clear();
+ e = base;
+ },
+ ExprKind::Unary(UnOp::Deref, base) => {
+ fields.clear();
+ e = base;
+ },
+
+ // No effect and doesn't affect which field is being used.
+ ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base,
+ _ => break None,
+ }
+ }
+}
+
+fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool {
+ loop {
+ match (&e.kind, fields) {
+ (&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => {
+ e = base;
+ fields = tail_fields;
+ },
+ (ExprKind::Path(path), []) => {
+ break cx.qpath_res(path, e.hir_id) == path_res;
+ },
+ (&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base,
+ _ => break false,
+ }
+ }
+}
+
+/// Checks if the given expression is the same field as, is a child of, or is the parent of the
+/// given field. Used to check if the expression can be used while the given field is borrowed
+/// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but
+/// `x.z`, and `y` will return false.
+fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool {
+ match expr.kind {
+ ExprKind::Field(base, name) => {
+ if let Some((head_field, tail_fields)) = fields.split_first() {
+ if name.name == *head_field && is_expr_same_field(cx, base, tail_fields, path_res) {
+ return true;
+ }
+ // Check if the expression is a parent field
+ let mut fields_iter = tail_fields.iter();
+ while let Some(field) = fields_iter.next() {
+ if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) {
+ return true;
+ }
+ }
+ }
+
+ // Check if the expression is a child field.
+ let mut e = base;
+ loop {
+ match e.kind {
+ ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true,
+ ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
+ ExprKind::Path(ref path) if fields.is_empty() => {
+ break cx.qpath_res(path, e.hir_id) == path_res;
+ },
+ _ => break false,
+ }
+ }
+ },
+ // If the path matches, this is either an exact match, or the expression is a parent of the field.
+ ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res,
+ ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => {
+ is_expr_same_child_or_parent_field(cx, base, fields, path_res)
+ },
+ _ => false,
+ }
+}
+
+/// Strips off all field and path expressions. This will return true if a field or path has been
+/// skipped. Used to skip them after failing to check for equality.
+fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
+ let mut e = expr;
+ let e = loop {
+ match e.kind {
+ ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
+ ExprKind::Path(_) => return (None, true),
+ _ => break e,
+ }
+ };
+ (Some(e), e.hir_id != expr.hir_id)
+}
+
+/// Checks if the given expression uses the iterator.
+fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
+ struct V<'a, 'b, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ iter_expr: &'b IterExpr,
+ uses_iter: bool,
+ }
+ 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.uses_iter {
+ // return
+ } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
+ self.uses_iter = true;
+ } else if let (e, true) = skip_fields_and_path(e) {
+ if let Some(e) = e {
+ self.visit_expr(e);
+ }
+ } else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
+ if is_res_used(self.cx, self.iter_expr.path, id) {
+ self.uses_iter = true;
+ }
+ } else {
+ walk_expr(self, e);
+ }
+ }
+ }
+
+ let mut v = V {
+ cx,
+ iter_expr,
+ uses_iter: false,
+ };
+ v.visit_expr(container);
+ v.uses_iter
+}
+
+#[allow(clippy::too_many_lines)]
+fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool {
+ struct AfterLoopVisitor<'a, 'b, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ iter_expr: &'b IterExpr,
+ loop_id: HirId,
+ after_loop: bool,
+ used_iter: bool,
+ }
+ impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, '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.used_iter {
+ return;
+ }
+ if self.after_loop {
+ if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
+ self.used_iter = true;
+ } else if let (e, true) = skip_fields_and_path(e) {
+ if let Some(e) = e {
+ self.visit_expr(e);
+ }
+ } else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
+ self.used_iter = is_res_used(self.cx, self.iter_expr.path, id);
+ } else {
+ walk_expr(self, e);
+ }
+ } else if self.loop_id == e.hir_id {
+ self.after_loop = true;
+ } else {
+ walk_expr(self, e);
+ }
+ }
+ }
+
+ struct NestedLoopVisitor<'a, 'b, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ iter_expr: &'b IterExpr,
+ local_id: HirId,
+ loop_id: HirId,
+ after_loop: bool,
+ found_local: bool,
+ used_after: bool,
+ }
+ impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_local(&mut self, l: &'tcx Local<'_>) {
+ if !self.after_loop {
+ l.pat.each_binding_or_first(&mut |_, id, _, _| {
+ if id == self.local_id {
+ self.found_local = true;
+ }
+ });
+ }
+ if let Some(e) = l.init {
+ self.visit_expr(e);
+ }
+ }
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if self.used_after {
+ return;
+ }
+ if self.after_loop {
+ if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
+ self.used_after = true;
+ } else if let (e, true) = skip_fields_and_path(e) {
+ if let Some(e) = e {
+ self.visit_expr(e);
+ }
+ } else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
+ self.used_after = is_res_used(self.cx, self.iter_expr.path, id);
+ } else {
+ walk_expr(self, e);
+ }
+ } else if e.hir_id == self.loop_id {
+ self.after_loop = true;
+ } else {
+ walk_expr(self, e);
+ }
+ }
+ }
+
+ if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
+ // The iterator expression will be used on the next iteration (for loops), or on the next call (for
+ // closures) unless it is declared within the enclosing expression. TODO: Check for closures
+ // used where an `FnOnce` type is expected.
+ let local_id = match iter_expr.path {
+ Res::Local(id) => id,
+ _ => return true,
+ };
+ let mut v = NestedLoopVisitor {
+ cx,
+ iter_expr,
+ local_id,
+ loop_id: loop_expr.hir_id,
+ after_loop: false,
+ found_local: false,
+ used_after: false,
+ };
+ v.visit_expr(e);
+ v.used_after || !v.found_local
+ } else {
+ let mut v = AfterLoopVisitor {
+ cx,
+ iter_expr,
+ loop_id: loop_expr.hir_id,
+ after_loop: false,
+ used_iter: false,
+ };
+ v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
+ v.used_iter
+ }
+}
--- /dev/null
- if let Some(_callee) = span.source_callee() {
- if !self.collected.contains(&call_site) {
- let name = if name.contains("::") {
- name.split("::").last().unwrap().to_string()
- } else {
- name.to_string()
- };
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet;
+use hir::def::{DefKind, Res};
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{edition::Edition, sym, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `#[macro_use] use...`.
+ ///
+ /// ### Why is this bad?
+ /// Since the Rust 2018 edition you can import
+ /// macro's directly, this is considered idiomatic.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// #[macro_use]
+ /// use some_macro;
+ /// ```
+ pub MACRO_USE_IMPORTS,
+ pedantic,
+ "#[macro_use] is no longer needed"
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct PathAndSpan {
+ path: String,
+ span: Span,
+}
+
+/// `MacroRefData` includes the name of the macro.
+#[derive(Debug, Clone)]
+pub struct MacroRefData {
+ name: String,
+}
+
+impl MacroRefData {
+ pub fn new(name: String) -> Self {
+ Self { name }
+ }
+}
+
+#[derive(Default)]
+#[allow(clippy::module_name_repetitions)]
+pub struct MacroUseImports {
+ /// the actual import path used and the span of the attribute above it.
+ imports: Vec<(String, Span)>,
+ /// the span of the macro reference, kept to ensure only one reference is used per macro call.
+ collected: FxHashSet<Span>,
+ mac_refs: Vec<MacroRefData>,
+}
+
+impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
+
+impl MacroUseImports {
+ fn push_unique_macro(&mut self, cx: &LateContext<'_>, span: Span) {
+ let call_site = span.source_callsite();
+ let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
- self.mac_refs.push(MacroRefData::new(name));
- self.collected.insert(call_site);
- }
++ if span.source_callee().is_some() && !self.collected.contains(&call_site) {
++ let name = if name.contains("::") {
++ name.split("::").last().unwrap().to_string()
++ } else {
++ name.to_string()
++ };
+
- if let Some(_callee) = span.source_callee() {
- if !self.collected.contains(&call_site) {
- self.mac_refs
- .push(MacroRefData::new(name.to_string()));
- self.collected.insert(call_site);
- }
++ self.mac_refs.push(MacroRefData::new(name));
++ self.collected.insert(call_site);
+ }
+ }
+
+ fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_>, span: Span) {
+ let call_site = span.source_callsite();
+ let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
++ if span.source_callee().is_some() && !self.collected.contains(&call_site) {
++ self.mac_refs.push(MacroRefData::new(name.to_string()));
++ self.collected.insert(call_site);
+ }
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
+ if_chain! {
+ if cx.sess().opts.edition >= Edition::Edition2018;
+ if let hir::ItemKind::Use(path, _kind) = &item.kind;
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
+ if let Res::Def(DefKind::Mod, id) = path.res;
+ if !id.is_local();
+ then {
+ for kid in cx.tcx.item_children(id).iter() {
+ if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
+ let span = mac_attr.span;
+ let def_path = cx.tcx.def_path_str(mac_id);
+ self.imports.push((def_path, span));
+ }
+ }
+ } else {
+ if in_macro(item.span) {
+ self.push_unique_macro_pat_ty(cx, item.span);
+ }
+ }
+ }
+ }
+ fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
+ if in_macro(attr.span) {
+ self.push_unique_macro(cx, attr.span);
+ }
+ }
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
+ if in_macro(expr.span) {
+ self.push_unique_macro(cx, expr.span);
+ }
+ }
+ fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
+ if in_macro(stmt.span) {
+ self.push_unique_macro(cx, stmt.span);
+ }
+ }
+ fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {
+ if in_macro(pat.span) {
+ self.push_unique_macro_pat_ty(cx, pat.span);
+ }
+ }
+ fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) {
+ if in_macro(ty.span) {
+ self.push_unique_macro_pat_ty(cx, ty.span);
+ }
+ }
+ #[allow(clippy::too_many_lines)]
+ fn check_crate_post(&mut self, cx: &LateContext<'_>, _krate: &hir::Crate<'_>) {
+ let mut used = FxHashMap::default();
+ let mut check_dup = vec![];
+ for (import, span) in &self.imports {
+ let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
+
+ if let Some(idx) = found_idx {
+ self.mac_refs.remove(idx);
+ let seg = import.split("::").collect::<Vec<_>>();
+
+ match seg.as_slice() {
+ // an empty path is impossible
+ // a path should always consist of 2 or more segments
+ [] | [_] => return,
+ [root, item] => {
+ if !check_dup.contains(&(*item).to_string()) {
+ used.entry(((*root).to_string(), span))
+ .or_insert_with(Vec::new)
+ .push((*item).to_string());
+ check_dup.push((*item).to_string());
+ }
+ },
+ [root, rest @ ..] => {
+ if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) {
+ let filtered = rest
+ .iter()
+ .filter_map(|item| {
+ if check_dup.contains(&(*item).to_string()) {
+ None
+ } else {
+ Some((*item).to_string())
+ }
+ })
+ .collect::<Vec<_>>();
+ used.entry(((*root).to_string(), span))
+ .or_insert_with(Vec::new)
+ .push(filtered.join("::"));
+ check_dup.extend(filtered);
+ } else {
+ let rest = rest.to_vec();
+ used.entry(((*root).to_string(), span))
+ .or_insert_with(Vec::new)
+ .push(rest.join("::"));
+ check_dup.extend(rest.iter().map(ToString::to_string));
+ }
+ },
+ }
+ }
+ }
+
+ let mut suggestions = vec![];
+ for ((root, span), path) in used {
+ if path.len() == 1 {
+ suggestions.push((span, format!("{}::{}", root, path[0])));
+ } else {
+ suggestions.push((span, format!("{}::{{{}}}", root, path.join(", "))));
+ }
+ }
+
+ // If mac_refs is not empty we have encountered an import we could not handle
+ // such as `std::prelude::v1::foo` or some other macro that expands to an import.
+ if self.mac_refs.is_empty() {
+ for (span, import) in suggestions {
+ let help = format!("use {};", import);
+ span_lint_and_sugg(
+ cx,
+ MACRO_USE_IMPORTS,
+ *span,
+ "`macro_use` attributes are no longer needed in the Rust 2018 edition",
+ "remove the attribute and import the macro directly, try",
+ help,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::higher;
++use clippy_utils::method_chain_args;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::ty::is_type_diagnostic_item;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, PatKind, QPath};
++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 unnecessary `ok()` in `while let`.
++ ///
++ /// ### Why is this bad?
++ /// Calling `ok()` in `while let` is unnecessary, instead match
++ /// on `Ok(pat)`
++ ///
++ /// ### Example
++ /// ```ignore
++ /// while let Some(value) = iter.next().ok() {
++ /// vec.push(value)
++ /// }
++ ///
++ /// if let Some(valie) = iter.next().ok() {
++ /// vec.push(value)
++ /// }
++ /// ```
++ /// Use instead:
++ /// ```ignore
++ /// while let Ok(value) = iter.next() {
++ /// vec.push(value)
++ /// }
++ ///
++ /// if let Ok(value) = iter.next() {
++ /// vec.push_value)
++ /// }
++ /// ```
++ pub MATCH_RESULT_OK,
++ style,
++ "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
++}
++
++declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]);
++
++impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
++ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++ let (let_pat, let_expr, ifwhile) =
++ if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
++ (let_pat, let_expr, "if")
++ } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
++ (let_pat, let_expr, "while")
++ } else {
++ return;
++ };
++
++ if_chain! {
++ if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
++ if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
++ if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
++ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
++ if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
++
++ then {
++
++ let mut applicability = Applicability::MachineApplicable;
++ let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
++ let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
++ let sugg = format!(
++ "{} let Ok({}) = {}",
++ ifwhile,
++ some_expr_string,
++ trimmed_ok.trim().trim_end_matches('.'),
++ );
++ span_lint_and_sugg(
++ cx,
++ MATCH_RESULT_OK,
++ expr.span.with_hi(let_expr.span.hi()),
++ "matching on `Some` with `ok()` is redundant",
++ &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
++ sugg,
++ applicability,
++ );
++ }
++ }
++ }
++}
--- /dev/null
- /// 1...10 => println!("1 ... 10"),
- /// 5...15 => println!("5 ... 15"),
+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_type) {
+ 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_type)
+ || is_type_diagnostic_item(cx, ty, sym::result_type)) =>
+ {
+ 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_type)
+ || 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
- let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{is_diag_item_method, match_def_path, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, adjustment::Adjust};
+use rustc_span::{symbol::sym, Span, SyntaxContext};
+
+use super::MANUAL_SPLIT_ONCE;
+
+pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
+ if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+ return;
+ }
+
+ let ctxt = expr.span.ctxt();
- let (method_name, msg) = if method_name == "splitn" {
- ("split_once", "manual implementation of `split_once`")
- } else {
- ("rsplit_once", "manual implementation of `rsplit_once`")
- };
++ let (method_name, msg, reverse) = if method_name == "splitn" {
++ ("split_once", "manual implementation of `split_once`", false)
++ } else {
++ ("rsplit_once", "manual implementation of `rsplit_once`", true)
++ };
++ let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
+ Some(x) => x,
+ None => return,
+ };
- match usage.kind {
+
+ let mut app = Applicability::MachineApplicable;
+ let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+ let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
+
- span_lint_and_sugg(
- cx,
- MANUAL_SPLIT_ONCE,
- usage.span,
- msg,
- "try this",
- format!("{}.{}({})", self_snip, method_name, pat_snip),
- app,
- );
++ let sugg = match usage.kind {
+ IterUsageKind::NextTuple => {
- let sugg = if usage.unwrap_kind.is_some() {
++ format!("{}.{}({})", self_snip, method_name, pat_snip)
+ },
++ IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
+ IterUsageKind::Next => {
+ let self_deref = {
+ let adjust = cx.typeck_results().expr_adjustments(self_arg);
+ if adjust.is_empty() {
+ String::new()
+ } else if cx.typeck_results().expr_ty(self_arg).is_box()
+ || adjust
+ .iter()
+ .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
+ {
+ format!("&{}", "*".repeat(adjust.len() - 1))
+ } else {
+ "*".repeat(adjust.len() - 2)
+ }
+ };
- };
-
- span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
++ if usage.unwrap_kind.is_some() {
+ format!(
+ "{}.{}({}).map_or({}{}, |x| x.0)",
+ &self_snip, method_name, pat_snip, self_deref, &self_snip
+ )
+ } else {
+ format!(
+ "Some({}.{}({}).map_or({}{}, |x| x.0))",
+ &self_snip, method_name, pat_snip, self_deref, &self_snip
+ )
- span_lint_and_sugg(
- cx,
- MANUAL_SPLIT_ONCE,
- usage.span,
- msg,
- "try this",
- format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
- app,
- );
++ }
+ },
+ IterUsageKind::Second => {
+ let access_str = match usage.unwrap_kind {
+ Some(UnwrapKind::Unwrap) => ".unwrap().1",
+ Some(UnwrapKind::QuestionMark) => "?.1",
+ None => ".map(|x| x.1)",
+ };
- }
++ format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
+ },
- ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
++ };
++
++ span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
+}
+
+enum IterUsageKind {
+ Next,
+ Second,
+ NextTuple,
++ RNextTuple,
+}
+
+enum UnwrapKind {
+ Unwrap,
+ QuestionMark,
+}
+
+struct IterUsage {
+ kind: IterUsageKind,
+ unwrap_kind: Option<UnwrapKind>,
+ span: Span,
+}
+
++#[allow(clippy::too_many_lines)]
+fn parse_iter_usage(
+ cx: &LateContext<'tcx>,
+ ctxt: SyntaxContext,
+ mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
++ reverse: bool,
+) -> Option<IterUsage> {
+ let (kind, span) = match iter.next() {
+ Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
+ let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
+ (name, args)
+ } else {
+ return None;
+ };
+ let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
+ let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
+
+ match (&*name.ident.as_str(), args) {
- if_chain! {
++ ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
++ if reverse {
++ (IterUsageKind::Second, e.span)
++ } else {
++ (IterUsageKind::Next, e.span)
++ }
++ },
+ ("next_tuple", []) => {
- return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
++ return if_chain! {
+ if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
+ if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
+ if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
+ if let ty::Tuple(subs) = subs.type_at(0).kind();
+ if subs.len() == 2;
+ then {
- return None;
++ Some(IterUsage {
++ kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
++ span: e.span,
++ unwrap_kind: None
++ })
+ } else {
- }
++ None
+ }
- match idx {
++ };
+ },
+ ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+ if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
+ let span = if name.ident.as_str() == "nth" {
+ e.span
+ } else {
+ if_chain! {
+ if let Some((_, Node::Expr(next_expr))) = iter.next();
+ if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind;
+ if next_name.ident.name == sym::next;
+ if next_expr.span.ctxt() == ctxt;
+ if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
+ if cx.tcx.trait_of_item(next_id) == Some(iter_id);
+ then {
+ next_expr.span
+ } else {
+ return None;
+ }
+ }
+ };
++ match if reverse { idx ^ 1 } else { idx } {
+ 0 => (IterUsageKind::Next, span),
+ 1 => (IterUsageKind::Second, span),
+ _ => return None,
+ }
+ } else {
+ return None;
+ }
+ },
+ _ => return None,
+ }
+ },
+ _ => return None,
+ };
+
+ let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
+ match e.kind {
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
+ ..
+ },
+ _,
+ ) => {
+ let parent_span = e.span.parent_callsite().unwrap();
+ if parent_span.ctxt() == ctxt {
+ (Some(UnwrapKind::QuestionMark), parent_span)
+ } else {
+ (None, span)
+ }
+ },
+ _ if e.span.ctxt() != ctxt => (None, span),
+ ExprKind::MethodCall(name, _, [_], _)
+ if name.ident.name == sym::unwrap
+ && cx
+ .typeck_results()
+ .type_dependent_def_id(e.hir_id)
+ .map_or(false, |id| is_diag_item_method(cx, id, sym::option_type)) =>
+ {
+ (Some(UnwrapKind::Unwrap), e.span)
+ },
+ _ => (None, span),
+ }
+ } else {
+ (None, span)
+ };
+
+ Some(IterUsage {
+ kind,
+ unwrap_kind,
+ span,
+ })
+}
--- /dev/null
+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. 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
- correctness,
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::implements_trait;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{
+ self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
+ StmtKind, TyKind, UnOp,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene::DesugaringKind;
+use rustc_span::source_map::{ExpnKind, Span};
+use rustc_span::symbol::sym;
+
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{
+ expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const,
+ iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
+};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for function arguments and let bindings denoted as
+ /// `ref`.
+ ///
+ /// ### Why is this bad?
+ /// The `ref` declaration makes the function take an owned
+ /// value, but turns the argument into a reference (which means that the value
+ /// is destroyed when exiting the function). This adds not much value: either
+ /// take a reference type, or take an owned value and create references in the
+ /// body.
+ ///
+ /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
+ /// type of `x` is more obvious with the former.
+ ///
+ /// ### Known problems
+ /// If the argument is dereferenced within the function,
+ /// removing the `ref` will lead to errors. This can be fixed by removing the
+ /// dereferences, e.g., changing `*x` to `x` within the function.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// // Bad
+ /// fn foo(ref x: u8) -> bool {
+ /// true
+ /// }
+ ///
+ /// // Good
+ /// fn foo(x: &u8) -> bool {
+ /// true
+ /// }
+ /// ```
+ pub TOPLEVEL_REF_ARG,
+ style,
+ "an entire binding declared as `ref`, in a function argument or a `let` statement"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for comparisons to NaN.
+ ///
+ /// ### Why is this bad?
+ /// NaN does not compare meaningfully to anything – not
+ /// even itself – so those comparisons are simply wrong.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = 1.0;
+ ///
+ /// // Bad
+ /// if x == f32::NAN { }
+ ///
+ /// // Good
+ /// if x.is_nan() { }
+ /// ```
+ pub CMP_NAN,
+ correctness,
+ "comparisons to `NAN`, which will always return false, probably not intended"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for (in-)equality comparisons on floating-point
+ /// values (apart from zero), except in functions called `*eq*` (which probably
+ /// implement equality for a type involving floats).
+ ///
+ /// ### Why is this bad?
+ /// Floating point calculations are usually imprecise, so
+ /// asking if two values are *exactly* equal is asking for trouble. For a good
+ /// guide on what to do, see [the floating point
+ /// guide](http://www.floating-point-gui.de/errors/comparison).
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = 1.2331f64;
+ /// let y = 1.2332f64;
+ ///
+ /// // Bad
+ /// if y == 1.23f64 { }
+ /// if y != x {} // where both are floats
+ ///
+ /// // Good
+ /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
+ /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
+ /// // let error_margin = std::f64::EPSILON;
+ /// if (y - 1.23f64).abs() < error_margin { }
+ /// if (y - x).abs() > error_margin { }
+ /// ```
+ pub FLOAT_CMP,
++ pedantic,
+ "using `==` or `!=` on float values instead of comparing difference with an epsilon"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for conversions to owned values just for the sake
+ /// of a comparison.
+ ///
+ /// ### Why is this bad?
+ /// The comparison can operate on a reference, so creating
+ /// an owned value effectively throws it away directly afterwards, which is
+ /// needlessly consuming code and heap space.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = "foo";
+ /// # let y = String::from("foo");
+ /// if x.to_owned() == y {}
+ /// ```
+ /// Could be written as
+ /// ```rust
+ /// # let x = "foo";
+ /// # let y = String::from("foo");
+ /// if x == y {}
+ /// ```
+ pub CMP_OWNED,
+ perf,
+ "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for getting the remainder of a division by one or minus
+ /// one.
+ ///
+ /// ### Why is this bad?
+ /// The result for a divisor of one can only ever be zero; for
+ /// minus one it can cause panic/overflow (if the left operand is the minimal value of
+ /// the respective integer type) or results in zero. No one will write such code
+ /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
+ /// contest, it's probably a bad idea. Use something more underhanded.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = 1;
+ /// let a = x % 1;
+ /// let a = x % -1;
+ /// ```
+ pub MODULO_ONE,
+ correctness,
+ "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of bindings with a single leading
+ /// underscore.
+ ///
+ /// ### Why is this bad?
+ /// A single leading underscore is usually used to indicate
+ /// that a binding will not be used. Using such a binding breaks this
+ /// expectation.
+ ///
+ /// ### Known problems
+ /// The lint does not work properly with desugaring and
+ /// macro, it has been allowed in the mean time.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _x = 0;
+ /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
+ /// // underscore. We should rename `_x` to `x`
+ /// ```
+ pub USED_UNDERSCORE_BINDING,
+ pedantic,
+ "using a binding which is prefixed with an underscore"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of short circuit boolean conditions as
+ /// a
+ /// statement.
+ ///
+ /// ### Why is this bad?
+ /// Using a short circuit boolean condition as a statement
+ /// may hide the fact that the second part is executed or not depending on the
+ /// outcome of the first part.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// f() && g(); // We should write `if f() { g(); }`.
+ /// ```
+ pub SHORT_CIRCUIT_STATEMENT,
+ complexity,
+ "using a short circuit boolean condition as a statement"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Catch casts from `0` to some pointer type
+ ///
+ /// ### Why is this bad?
+ /// This generally means `null` and is better expressed as
+ /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let a = 0 as *const u32;
+ ///
+ /// // Good
+ /// let a = std::ptr::null::<u32>();
+ /// ```
+ pub ZERO_PTR,
+ style,
+ "using `0 as *{const, mut} T`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for (in-)equality comparisons on floating-point
+ /// value and constant, except in functions called `*eq*` (which probably
+ /// implement equality for a type involving floats).
+ ///
+ /// ### Why is this bad?
+ /// Floating point calculations are usually imprecise, so
+ /// asking if two values are *exactly* equal is asking for trouble. For a good
+ /// guide on what to do, see [the floating point
+ /// guide](http://www.floating-point-gui.de/errors/comparison).
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x: f64 = 1.0;
+ /// const ONE: f64 = 1.00;
+ ///
+ /// // Bad
+ /// if x == ONE { } // where both are floats
+ ///
+ /// // Good
+ /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
+ /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
+ /// // let error_margin = std::f64::EPSILON;
+ /// if (x - ONE).abs() < error_margin { }
+ /// ```
+ pub FLOAT_CMP_CONST,
+ restriction,
+ "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
+}
+
+declare_lint_pass!(MiscLints => [
+ TOPLEVEL_REF_ARG,
+ CMP_NAN,
+ FLOAT_CMP,
+ CMP_OWNED,
+ MODULO_ONE,
+ USED_UNDERSCORE_BINDING,
+ SHORT_CIRCUIT_STATEMENT,
+ ZERO_PTR,
+ FLOAT_CMP_CONST
+]);
+
+impl<'tcx> LateLintPass<'tcx> for MiscLints {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ k: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ span: Span,
+ _: HirId,
+ ) {
+ if let FnKind::Closure = k {
+ // Does not apply to closures
+ return;
+ }
+ if in_external_macro(cx.tcx.sess, span) {
+ return;
+ }
+ for arg in iter_input_pats(decl, body) {
+ if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
+ span_lint(
+ cx,
+ TOPLEVEL_REF_ARG,
+ arg.pat.span,
+ "`ref` directly on a function argument is ignored. \
+ Consider using a reference type instead",
+ );
+ }
+ }
+ }
+
+ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+ if_chain! {
+ if !in_external_macro(cx.tcx.sess, stmt.span);
+ if let StmtKind::Local(local) = stmt.kind;
+ if let PatKind::Binding(an, .., name, None) = local.pat.kind;
+ if let Some(init) = local.init;
+ if !higher::is_from_for_desugar(local);
+ if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut;
+ then {
+ // use the macro callsite when the init span (but not the whole local span)
+ // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
+ let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
+ Sugg::hir_with_macro_callsite(cx, init, "..")
+ } else {
+ Sugg::hir(cx, init, "..")
+ };
+ let (mutopt, initref) = if an == BindingAnnotation::RefMut {
+ ("mut ", sugg_init.mut_addr())
+ } else {
+ ("", sugg_init.addr())
+ };
+ let tyopt = if let Some(ty) = local.ty {
+ format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
+ } else {
+ String::new()
+ };
+ span_lint_hir_and_then(
+ cx,
+ TOPLEVEL_REF_ARG,
+ init.hir_id,
+ local.pat.span,
+ "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
+ |diag| {
+ diag.span_suggestion(
+ stmt.span,
+ "try",
+ format!(
+ "let {name}{tyopt} = {initref};",
+ name=snippet(cx, name.span, ".."),
+ tyopt=tyopt,
+ initref=initref,
+ ),
+ Applicability::MachineApplicable,
+ );
+ }
+ );
+ }
+ };
+ if_chain! {
+ if let StmtKind::Semi(expr) = stmt.kind;
+ if let ExprKind::Binary(ref binop, a, b) = expr.kind;
+ if binop.node == BinOpKind::And || binop.node == BinOpKind::Or;
+ if let Some(sugg) = Sugg::hir_opt(cx, a);
+ then {
+ span_lint_hir_and_then(
+ cx,
+ SHORT_CIRCUIT_STATEMENT,
+ expr.hir_id,
+ stmt.span,
+ "boolean short circuit operator in statement may be clearer using an explicit test",
+ |diag| {
+ let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
+ diag.span_suggestion(
+ stmt.span,
+ "replace it with",
+ format!(
+ "if {} {{ {}; }}",
+ sugg,
+ &snippet(cx, b.span, ".."),
+ ),
+ Applicability::MachineApplicable, // snippet
+ );
+ });
+ }
+ };
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ match expr.kind {
+ ExprKind::Cast(e, ty) => {
+ check_cast(cx, expr.span, e, ty);
+ return;
+ },
+ ExprKind::Binary(ref cmp, left, right) => {
+ check_binary(cx, expr, cmp, left, right);
+ return;
+ },
+ _ => {},
+ }
+ if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
+ // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
+ return;
+ }
+ let binding = match expr.kind {
+ ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => {
+ let binding = last_path_segment(qpath).ident.as_str();
+ if binding.starts_with('_') &&
+ !binding.starts_with("__") &&
+ binding != "_result" && // FIXME: #944
+ is_used(cx, expr) &&
+ // don't lint if the declaration is in a macro
+ non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id))
+ {
+ Some(binding)
+ } else {
+ None
+ }
+ },
+ ExprKind::Field(_, ident) => {
+ let name = ident.as_str();
+ if name.starts_with('_') && !name.starts_with("__") {
+ Some(name)
+ } else {
+ None
+ }
+ },
+ _ => None,
+ };
+ if let Some(binding) = binding {
+ span_lint(
+ cx,
+ USED_UNDERSCORE_BINDING,
+ expr.span,
+ &format!(
+ "used binding `{}` which is prefixed with an underscore. A leading \
+ underscore signals that a binding will not be used",
+ binding
+ ),
+ );
+ }
+ }
+}
+
+fn get_lint_and_message(
+ is_comparing_constants: bool,
+ is_comparing_arrays: bool,
+) -> (&'static rustc_lint::Lint, &'static str) {
+ if is_comparing_constants {
+ (
+ FLOAT_CMP_CONST,
+ if is_comparing_arrays {
+ "strict comparison of `f32` or `f64` constant arrays"
+ } else {
+ "strict comparison of `f32` or `f64` constant"
+ },
+ )
+ } else {
+ (
+ FLOAT_CMP,
+ if is_comparing_arrays {
+ "strict comparison of `f32` or `f64` arrays"
+ } else {
+ "strict comparison of `f32` or `f64`"
+ },
+ )
+ }
+}
+
+fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
+ if_chain! {
+ if !in_constant(cx, cmp_expr.hir_id);
+ if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
+ if match value {
+ Constant::F32(num) => num.is_nan(),
+ Constant::F64(num) => num.is_nan(),
+ _ => false,
+ };
+ then {
+ span_lint(
+ cx,
+ CMP_NAN,
+ cmp_expr.span,
+ "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
+ );
+ }
+ }
+}
+
+fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
+ res
+ } else {
+ false
+ }
+}
+
+fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ match constant(cx, cx.typeck_results(), expr) {
+ Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
+ Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
+ Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
+ Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
+ Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
+ _ => false,
+ }),
+ _ => false,
+ }
+}
+
+// Return true if `expr` is the result of `signum()` invoked on a float value.
+fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ // The negation of a signum is still a signum
+ if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
+ return is_signum(cx, child_expr);
+ }
+
+ if_chain! {
+ if let ExprKind::MethodCall(method_name, _, [ref self_arg, ..], _) = expr.kind;
+ if sym!(signum) == method_name.ident.name;
+ // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
+ // the method call)
+ then {
+ return is_float(cx, self_arg);
+ }
+ }
+ false
+}
+
+fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
+
+ if let ty::Array(arr_ty, _) = value {
+ return matches!(arr_ty.kind(), ty::Float(_));
+ };
+
+ matches!(value, ty::Float(_))
+}
+
+fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
+}
+
+fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
+ #[derive(Default)]
+ struct EqImpl {
+ ty_eq_other: bool,
+ other_eq_ty: bool,
+ }
+
+ impl EqImpl {
+ fn is_implemented(&self) -> bool {
+ self.ty_eq_other || self.other_eq_ty
+ }
+ }
+
+ fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
+ cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
+ ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
+ other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
+ })
+ }
+
+ let (arg_ty, snip) = match expr.kind {
+ ExprKind::MethodCall(.., args, _) if args.len() == 1 => {
+ if_chain!(
+ if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if is_diag_trait_item(cx, expr_def_id, sym::ToString)
+ || is_diag_trait_item(cx, expr_def_id, sym::ToOwned);
+ then {
+ (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
+ } else {
+ return;
+ }
+ )
+ },
+ ExprKind::Call(path, [arg]) => {
+ if expr_path_res(cx, path)
+ .opt_def_id()
+ .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
+ .is_some()
+ {
+ (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, ".."))
+ } else {
+ return;
+ }
+ },
+ _ => return,
+ };
+
+ let other_ty = cx.typeck_results().expr_ty(other);
+
+ let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
+ let with_deref = arg_ty
+ .builtin_deref(true)
+ .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
+ .unwrap_or_default();
+
+ if !with_deref.is_implemented() && !without_deref.is_implemented() {
+ return;
+ }
+
+ let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
+
+ let lint_span = if other_gets_derefed {
+ expr.span.to(other.span)
+ } else {
+ expr.span
+ };
+
+ span_lint_and_then(
+ cx,
+ CMP_OWNED,
+ lint_span,
+ "this creates an owned instance just for comparison",
+ |diag| {
+ // This also catches `PartialEq` implementations that call `to_owned`.
+ if other_gets_derefed {
+ diag.span_label(lint_span, "try implementing the comparison without allocating");
+ return;
+ }
+
+ let expr_snip;
+ let eq_impl;
+ if with_deref.is_implemented() {
+ expr_snip = format!("*{}", snip);
+ eq_impl = with_deref;
+ } else {
+ expr_snip = snip.to_string();
+ eq_impl = without_deref;
+ };
+
+ let span;
+ let hint;
+ if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
+ span = expr.span;
+ hint = expr_snip;
+ } else {
+ span = expr.span.to(other.span);
+ if eq_impl.ty_eq_other {
+ hint = format!("{} == {}", expr_snip, snippet(cx, other.span, ".."));
+ } else {
+ hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip);
+ }
+ }
+
+ diag.span_suggestion(
+ span,
+ "try",
+ hint,
+ Applicability::MachineApplicable, // snippet
+ );
+ },
+ );
+}
+
+/// Heuristic to see if an expression is used. Should be compatible with
+/// `unused_variables`'s idea
+/// of what it means for an expression to be "used".
+fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind {
+ ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
+ _ => is_used(cx, parent),
+ })
+}
+
+/// Tests whether an expression is in a macro expansion (e.g., something
+/// generated by `#[derive(...)]` or the like).
+fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
+ use rustc_span::hygiene::MacroKind;
+ if expr.span.from_expansion() {
+ let data = expr.span.ctxt().outer_expn_data();
+ matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _))
+ } else {
+ false
+ }
+}
+
+/// Tests whether `res` is a variable defined outside a macro.
+fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
+ if let def::Res::Local(id) = res {
+ !cx.tcx.hir().span(id).from_expansion()
+ } else {
+ false
+ }
+}
+
+fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
+ if_chain! {
+ if let TyKind::Ptr(ref mut_ty) = ty.kind;
+ if let ExprKind::Lit(ref lit) = e.kind;
+ if let LitKind::Int(0, _) = lit.node;
+ if !in_constant(cx, e.hir_id);
+ then {
+ let (msg, sugg_fn) = match mut_ty.mutbl {
+ Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"),
+ Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"),
+ };
+
+ let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
+ (format!("{}()", sugg_fn), Applicability::MachineApplicable)
+ } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
+ (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable)
+ } else {
+ // `MaybeIncorrect` as type inference may not work with the suggested code
+ (format!("{}()", sugg_fn), Applicability::MaybeIncorrect)
+ };
+ span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
+ }
+ }
+}
+
+fn check_binary(
+ cx: &LateContext<'a>,
+ expr: &Expr<'_>,
+ cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
+ left: &'a Expr<'_>,
+ right: &'a Expr<'_>,
+) {
+ let op = cmp.node;
+ if op.is_comparison() {
+ check_nan(cx, left, expr);
+ check_nan(cx, right, expr);
+ check_to_owned(cx, left, right, true);
+ check_to_owned(cx, right, left, false);
+ }
+ if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
+ if is_allowed(cx, left) || is_allowed(cx, right) {
+ return;
+ }
+
+ // Allow comparing the results of signum()
+ if is_signum(cx, left) && is_signum(cx, right) {
+ return;
+ }
+
+ if let Some(name) = get_item_name(cx, expr) {
+ let name = name.as_str();
+ if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
+ return;
+ }
+ }
+ let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
+ let (lint, msg) = get_lint_and_message(
+ is_named_constant(cx, left) || is_named_constant(cx, right),
+ is_comparing_arrays,
+ );
+ span_lint_and_then(cx, lint, expr.span, msg, |diag| {
+ let lhs = Sugg::hir(cx, left, "..");
+ let rhs = Sugg::hir(cx, right, "..");
+
+ if !is_comparing_arrays {
+ diag.span_suggestion(
+ expr.span,
+ "consider comparing them within some margin of error",
+ format!(
+ "({}).abs() {} error_margin",
+ lhs - rhs,
+ if op == BinOpKind::Eq { '<' } else { '>' }
+ ),
+ Applicability::HasPlaceholders, // snippet
+ );
+ }
+ diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
+ });
+ } else if op == BinOpKind::Rem {
+ if is_integer_const(cx, right, 1) {
+ span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
+ }
+
+ if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
+ if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
+ span_lint(
+ cx,
+ MODULO_ONE,
+ expr.span,
+ "any number modulo -1 will panic/overflow or result in 0",
+ );
+ }
+ };
+ }
+}
--- /dev/null
- use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::trait_ref_of_method;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::TypeFoldable;
- /// It's correct to use a struct, that contains interior mutability
- /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
- /// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
- /// The `bytes` crate is a great example of this.
++use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::symbol::sym;
+use std::iter;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for sets/maps with mutable key types.
+ ///
+ /// ### Why is this bad?
+ /// All of `HashMap`, `HashSet`, `BTreeMap` and
+ /// `BtreeSet` rely on either the hash or the order of keys be unchanging,
+ /// so having types with interior mutability is a bad idea.
+ ///
+ /// ### Known problems
- if [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeMap]
++ ///
++ /// #### False Positives
++ /// It's correct to use a struct that contains interior mutability as a key, when its
++ /// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
++ /// However, this lint is unable to recognize this, so it will often cause false positives in
++ /// theses cases. The `bytes` crate is a great example of this.
++ ///
++ /// #### False Negatives
++ /// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
++ /// indirection. For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
++ /// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
++ ///
++ /// This lint does check a few cases for indirection. Firstly, using some standard library
++ /// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
++ /// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
++ /// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
++ /// contained type.
++ ///
++ /// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
++ /// apply only to the **address** of the contained value. Therefore, interior mutability
++ /// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
++ /// or `Ord`, and therefore will not trigger this link. For more info, see issue
++ /// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::cmp::{PartialEq, Eq};
+ /// use std::collections::HashSet;
+ /// use std::hash::{Hash, Hasher};
+ /// use std::sync::atomic::AtomicUsize;
+ ///# #[allow(unused)]
+ ///
+ /// struct Bad(AtomicUsize);
+ /// impl PartialEq for Bad {
+ /// fn eq(&self, rhs: &Self) -> bool {
+ /// ..
+ /// ; unimplemented!();
+ /// }
+ /// }
+ ///
+ /// impl Eq for Bad {}
+ ///
+ /// impl Hash for Bad {
+ /// fn hash<H: Hasher>(&self, h: &mut H) {
+ /// ..
+ /// ; unimplemented!();
+ /// }
+ /// }
+ ///
+ /// fn main() {
+ /// let _: HashSet<Bad> = HashSet::new();
+ /// }
+ /// ```
+ pub MUTABLE_KEY_TYPE,
+ suspicious,
+ "Check for mutable `Map`/`Set` key type"
+}
+
+declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
+
+impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
+ if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
+ check_sig(cx, item.hir_id(), sig.decl);
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
+ if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind {
+ if trait_ref_of_method(cx, item.hir_id()).is_none() {
+ check_sig(cx, item.hir_id(), sig.decl);
+ }
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
+ if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
+ check_sig(cx, item.hir_id(), sig.decl);
+ }
+ }
+
+ fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
+ if let hir::PatKind::Wild = local.pat.kind {
+ return;
+ }
+ check_ty(cx, local.span, cx.typeck_results().pat_ty(&*local.pat));
+ }
+}
+
+fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) {
+ let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
+ let fn_sig = cx.tcx.fn_sig(fn_def_id);
+ for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
+ check_ty(cx, hir_ty.span, ty);
+ }
+ check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
+}
+
+// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
+// generics (because the compiler cannot ensure immutability for unknown types).
+fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
+ let ty = ty.peel_refs();
+ if let Adt(def, substs) = ty.kind() {
- .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did))
- && is_mutable_type(cx, substs.type_at(0), span)
- {
++ let is_keyed_type = [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeSet]
+ .iter()
- fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
++ .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
++ if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) {
+ span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
+ }
+ }
+}
+
- RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
- mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
- },
- Slice(inner_ty) => is_mutable_type(cx, inner_ty, span),
++/// Determines if a type contains interior mutability which would affect its implementation of
++/// [`Hash`] or [`Ord`].
++fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
+ match *ty.kind() {
- size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
++ Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span),
++ Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span),
+ Array(inner_ty, size) => {
- Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
- Adt(..) => {
- !ty.has_escaping_bound_vars()
- && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
- && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
++ size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
++ && is_interior_mutable_type(cx, inner_ty, span)
+ },
++ Tuple(..) => ty.tuple_fields().any(|ty| is_interior_mutable_type(cx, ty, span)),
++ Adt(def, substs) => {
++ // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
++ // that of their type parameters. Note: we don't include `HashSet` and `HashMap`
++ // because they have no impl for `Hash` or `Ord`.
++ let is_std_collection = [
++ sym::option_type,
++ sym::result_type,
++ sym::LinkedList,
++ sym::vec_type,
++ sym::vecdeque_type,
++ sym::BTreeMap,
++ sym::BTreeSet,
++ sym::Rc,
++ sym::Arc,
++ ]
++ .iter()
++ .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
++ let is_box = Some(def.did) == cx.tcx.lang_items().owned_box();
++ if is_std_collection || is_box {
++ // The type is mutable if any of its type parameters are
++ substs.types().any(|ty| is_interior_mutable_type(cx, ty, span))
++ } else {
++ !ty.has_escaping_bound_vars()
++ && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
++ && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
++ }
+ },
+ _ => false,
+ }
+}
--- /dev/null
- if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind {
+//! Checks for needless address of operations (`&`)
+//!
+//! This lint is **warn** by default
+
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability, snippet_with_context};
+use clippy_utils::{get_parent_expr, in_macro, path_to_local};
+use if_chain::if_chain;
+use rustc_ast::util::parser::PREC_POSTFIX;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_errors::Applicability;
+use rustc_hir::{BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for address of operations (`&`) that are going to
+ /// be dereferenced immediately by the compiler.
+ ///
+ /// ### Why is this bad?
+ /// Suggests that the receiver of the expression borrows
+ /// the expression.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn fun(_a: &i32) {}
+ ///
+ /// // Bad
+ /// let x: &i32 = &&&&&&5;
+ /// fun(&x);
+ ///
+ /// // Good
+ /// let x: &i32 = &5;
+ /// fun(x);
+ /// ```
+ pub NEEDLESS_BORROW,
+ style,
+ "taking a reference that is going to be automatically dereferenced"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `ref` bindings which create a reference to a reference.
+ ///
+ /// ### Why is this bad?
+ /// The address-of operator at the use site is clearer about the need for a reference.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let x = Some("");
+ /// if let Some(ref x) = x {
+ /// // use `x` here
+ /// }
+ ///
+ /// // Good
+ /// let x = Some("");
+ /// if let Some(x) = x {
+ /// // use `&x` here
+ /// }
+ /// ```
+ pub REF_BINDING_TO_REFERENCE,
+ pedantic,
+ "`ref` binding to a reference"
+}
+
+impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE]);
+#[derive(Default)]
+pub struct NeedlessBorrow {
+ /// The body the first local was found in. Used to emit lints when the traversal of the body has
+ /// been finished. Note we can't lint at the end of every body as they can be nested within each
+ /// other.
+ current_body: Option<BodyId>,
+ /// The list of locals currently being checked by the lint.
+ /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
+ /// This is needed for or patterns where one of the branches can be linted, but another can not
+ /// be.
+ ///
+ /// e.g. `m!(x) | Foo::Bar(ref x)`
+ ref_locals: FxIndexMap<HirId, Option<RefPat>>,
+}
+
+struct RefPat {
+ /// Whether every usage of the binding is dereferenced.
+ always_deref: bool,
+ /// The spans of all the ref bindings for this local.
+ spans: Vec<Span>,
+ /// The applicability of this suggestion.
+ app: Applicability,
+ /// All the replacements which need to be made.
+ replacements: Vec<(Span, String)>,
+}
+
+impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if let Some(local) = path_to_local(e) {
+ self.check_local_usage(cx, e, local);
+ }
+
+ if e.span.from_expansion() {
+ return;
+ }
- "this expression borrows a reference (`&{}`) that is immediately dereferenced \
++ if let ExprKind::AddrOf(BorrowKind::Ref, mutability, inner) = e.kind {
+ if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
+ for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
+ if let [Adjustment {
+ kind: Adjust::Deref(_), ..
+ }, Adjustment {
+ kind: Adjust::Deref(_), ..
+ }, Adjustment {
+ kind: Adjust::Borrow(_),
+ ..
+ }] = *adj3
+ {
++ let help_msg_ty = if matches!(mutability, Mutability::Not) {
++ format!("&{}", ty)
++ } else {
++ format!("&mut {}", ty)
++ };
++
+ span_lint_and_then(
+ cx,
+ NEEDLESS_BORROW,
+ e.span,
+ &format!(
- ty
++ "this expression borrows a reference (`{}`) that is immediately dereferenced \
+ by the compiler",
++ help_msg_ty
+ ),
+ |diag| {
+ if let Some(snippet) = snippet_opt(cx, inner.span) {
+ diag.span_suggestion(
+ e.span,
+ "change this to",
+ snippet,
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ );
+ }
+ }
+ }
+ }
+ }
+
+ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
+ if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
+ if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
+ // This binding id has been seen before. Add this pattern to the list of changes.
+ if let Some(prev_pat) = opt_prev_pat {
+ if in_macro(pat.span) {
+ // Doesn't match the context of the previous pattern. Can't lint here.
+ *opt_prev_pat = None;
+ } else {
+ prev_pat.spans.push(pat.span);
+ prev_pat.replacements.push((
+ pat.span,
+ snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
+ .0
+ .into(),
+ ));
+ }
+ }
+ return;
+ }
+
+ if_chain! {
+ if !in_macro(pat.span);
+ if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
+ // only lint immutable refs, because borrowed `&mut T` cannot be moved out
+ if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
+ then {
+ let mut app = Applicability::MachineApplicable;
+ let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
+ self.current_body = self.current_body.or(cx.enclosing_body);
+ self.ref_locals.insert(
+ id,
+ Some(RefPat {
+ always_deref: true,
+ spans: vec![pat.span],
+ app,
+ replacements: vec![(pat.span, snip.into())],
+ }),
+ );
+ }
+ }
+ }
+ }
+
+ fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+ if Some(body.id()) == self.current_body {
+ for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
+ let replacements = pat.replacements;
+ let app = pat.app;
+ span_lint_and_then(
+ cx,
+ if pat.always_deref {
+ NEEDLESS_BORROW
+ } else {
+ REF_BINDING_TO_REFERENCE
+ },
+ pat.spans,
+ "this pattern creates a reference to a reference",
+ |diag| {
+ diag.multipart_suggestion("try this", replacements, app);
+ },
+ );
+ }
+ self.current_body = None;
+ }
+ }
+}
+impl NeedlessBorrow {
+ fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
+ if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
+ if let Some(pat) = outer_pat {
+ // Check for auto-deref
+ if !matches!(
+ cx.typeck_results().expr_adjustments(e),
+ [
+ Adjustment {
+ kind: Adjust::Deref(_),
+ ..
+ },
+ Adjustment {
+ kind: Adjust::Deref(_),
+ ..
+ },
+ ..
+ ]
+ ) {
+ match get_parent_expr(cx, e) {
+ // Field accesses are the same no matter the number of references.
+ Some(Expr {
+ kind: ExprKind::Field(..),
+ ..
+ }) => (),
+ Some(&Expr {
+ span,
+ kind: ExprKind::Unary(UnOp::Deref, _),
+ ..
+ }) if !in_macro(span) => {
+ // Remove explicit deref.
+ let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
+ pat.replacements.push((span, snip.into()));
+ },
+ Some(parent) if !in_macro(parent.span) => {
+ // Double reference might be needed at this point.
+ if parent.precedence().order() == PREC_POSTFIX {
+ // Parentheses would be needed here, don't lint.
+ *outer_pat = None;
+ } else {
+ pat.always_deref = false;
+ let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
+ pat.replacements.push((e.span, format!("&{}", snip)));
+ }
+ },
+ _ if !in_macro(e.span) => {
+ // Double reference might be needed at this point.
+ pat.always_deref = false;
+ let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
+ pat.replacements.push((e.span, format!("&{}", snip)));
+ },
+ // Edge case for macros. The span of the identifier will usually match the context of the
+ // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
+ // macros
+ _ => *outer_pat = None,
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
- style,
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use rustc_ast::ast::{
+ Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind,
+};
+use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
+use rustc_span::symbol::{Ident, Symbol};
+use std::cmp::Ordering;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for names that are very similar and thus confusing.
+ ///
+ /// ### Why is this bad?
+ /// It's hard to distinguish between names that differ only
+ /// by a single character.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// let checked_exp = something;
+ /// let checked_expr = something_else;
+ /// ```
+ pub SIMILAR_NAMES,
+ pedantic,
+ "similarly named items and bindings"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for too many variables whose name consists of a
+ /// single character.
+ ///
+ /// ### Why is this bad?
+ /// It's hard to memorize what a variable means without a
+ /// descriptive name.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// let (a, b, c, d, e, f, g) = (...);
+ /// ```
+ pub MANY_SINGLE_CHAR_NAMES,
++ pedantic,
+ "too many single character bindings"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks if you have variables whose name consists of just
+ /// underscores and digits.
+ ///
+ /// ### Why is this bad?
+ /// It's hard to memorize what a variable means without a
+ /// descriptive name.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _1 = 1;
+ /// let ___1 = 1;
+ /// let __1___2 = 11;
+ /// ```
+ pub JUST_UNDERSCORES_AND_DIGITS,
+ style,
+ "unclear name"
+}
+
+#[derive(Copy, Clone)]
+pub struct NonExpressiveNames {
+ pub single_char_binding_names_threshold: u64,
+}
+
+impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);
+
+struct ExistingName {
+ interned: Symbol,
+ span: Span,
+ len: usize,
+ exemptions: &'static [&'static str],
+}
+
+struct SimilarNamesLocalVisitor<'a, 'tcx> {
+ names: Vec<ExistingName>,
+ cx: &'a EarlyContext<'tcx>,
+ lint: &'a NonExpressiveNames,
+
+ /// A stack of scopes containing the single-character bindings in each scope.
+ single_char_names: Vec<Vec<Ident>>,
+}
+
+impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
+ fn check_single_char_names(&self) {
+ let num_single_char_names = self.single_char_names.iter().flatten().count();
+ let threshold = self.lint.single_char_binding_names_threshold;
+ if num_single_char_names as u64 > threshold {
+ let span = self
+ .single_char_names
+ .iter()
+ .flatten()
+ .map(|ident| ident.span)
+ .collect::<Vec<_>>();
+ span_lint(
+ self.cx,
+ MANY_SINGLE_CHAR_NAMES,
+ span,
+ &format!(
+ "{} bindings with single-character names in scope",
+ num_single_char_names
+ ),
+ );
+ }
+ }
+}
+
+// this list contains lists of names that are allowed to be similar
+// the assumption is that no name is ever contained in multiple lists.
+#[rustfmt::skip]
+const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
+ &["parsed", "parser"],
+ &["lhs", "rhs"],
+ &["tx", "rx"],
+ &["set", "get"],
+ &["args", "arms"],
+ &["qpath", "path"],
+ &["lit", "lint"],
+ &["wparam", "lparam"],
+ &["iter", "item"],
+];
+
+struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);
+
+impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
+ fn visit_pat(&mut self, pat: &'tcx Pat) {
+ match pat.kind {
+ PatKind::Ident(_, ident, _) => {
+ if !pat.span.from_expansion() {
+ self.check_ident(ident);
+ }
+ },
+ PatKind::Struct(_, _, ref fields, _) => {
+ for field in fields {
+ if !field.is_shorthand {
+ self.visit_pat(&field.pat);
+ }
+ }
+ },
+ // just go through the first pattern, as either all patterns
+ // bind the same bindings or rustc would have errored much earlier
+ PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
+ _ => walk_pat(self, pat),
+ }
+ }
+}
+
+#[must_use]
+fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
+ for &list in ALLOWED_TO_BE_SIMILAR {
+ if allowed_to_be_similar(interned_name, list) {
+ return Some(list);
+ }
+ }
+ None
+}
+
+#[must_use]
+fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
+ list.iter()
+ .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
+}
+
+impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
+ fn check_short_ident(&mut self, ident: Ident) {
+ // Ignore shadowing
+ if self
+ .0
+ .single_char_names
+ .iter()
+ .flatten()
+ .any(|id| id.name == ident.name)
+ {
+ return;
+ }
+
+ if let Some(scope) = &mut self.0.single_char_names.last_mut() {
+ scope.push(ident);
+ }
+ }
+
+ #[allow(clippy::too_many_lines)]
+ fn check_ident(&mut self, ident: Ident) {
+ let interned_name = ident.name.as_str();
+ if interned_name.chars().any(char::is_uppercase) {
+ return;
+ }
+ if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
+ span_lint(
+ self.0.cx,
+ JUST_UNDERSCORES_AND_DIGITS,
+ ident.span,
+ "consider choosing a more descriptive name",
+ );
+ return;
+ }
+ if interned_name.starts_with('_') {
+ // these bindings are typically unused or represent an ignored portion of a destructuring pattern
+ return;
+ }
+ let count = interned_name.chars().count();
+ if count < 3 {
+ if count == 1 {
+ self.check_short_ident(ident);
+ }
+ return;
+ }
+ for existing_name in &self.0.names {
+ if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
+ continue;
+ }
+ match existing_name.len.cmp(&count) {
+ Ordering::Greater => {
+ if existing_name.len - count != 1
+ || levenstein_not_1(&interned_name, &existing_name.interned.as_str())
+ {
+ continue;
+ }
+ },
+ Ordering::Less => {
+ if count - existing_name.len != 1
+ || levenstein_not_1(&existing_name.interned.as_str(), &interned_name)
+ {
+ continue;
+ }
+ },
+ Ordering::Equal => {
+ let mut interned_chars = interned_name.chars();
+ let interned_str = existing_name.interned.as_str();
+ let mut existing_chars = interned_str.chars();
+ let first_i = interned_chars.next().expect("we know we have at least one char");
+ let first_e = existing_chars.next().expect("we know we have at least one char");
+ let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
+
+ if eq_or_numeric((first_i, first_e)) {
+ let last_i = interned_chars.next_back().expect("we know we have at least two chars");
+ let last_e = existing_chars.next_back().expect("we know we have at least two chars");
+ if eq_or_numeric((last_i, last_e)) {
+ if interned_chars
+ .zip(existing_chars)
+ .filter(|&ie| !eq_or_numeric(ie))
+ .count()
+ != 1
+ {
+ continue;
+ }
+ } else {
+ let second_last_i = interned_chars
+ .next_back()
+ .expect("we know we have at least three chars");
+ let second_last_e = existing_chars
+ .next_back()
+ .expect("we know we have at least three chars");
+ if !eq_or_numeric((second_last_i, second_last_e))
+ || second_last_i == '_'
+ || !interned_chars.zip(existing_chars).all(eq_or_numeric)
+ {
+ // allowed similarity foo_x, foo_y
+ // or too many chars differ (foo_x, boo_y) or (foox, booy)
+ continue;
+ }
+ }
+ } else {
+ let second_i = interned_chars.next().expect("we know we have at least two chars");
+ let second_e = existing_chars.next().expect("we know we have at least two chars");
+ if !eq_or_numeric((second_i, second_e))
+ || second_i == '_'
+ || !interned_chars.zip(existing_chars).all(eq_or_numeric)
+ {
+ // allowed similarity x_foo, y_foo
+ // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
+ continue;
+ }
+ }
+ },
+ }
+ span_lint_and_then(
+ self.0.cx,
+ SIMILAR_NAMES,
+ ident.span,
+ "binding's name is too similar to existing binding",
+ |diag| {
+ diag.span_note(existing_name.span, "existing binding defined here");
+ },
+ );
+ return;
+ }
+ self.0.names.push(ExistingName {
+ exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
+ interned: ident.name,
+ span: ident.span,
+ len: count,
+ });
+ }
+}
+
+impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
+ /// ensure scoping rules work
+ fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
+ let n = self.names.len();
+ let single_char_count = self.single_char_names.len();
+ f(self);
+ self.names.truncate(n);
+ self.single_char_names.truncate(single_char_count);
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
+ fn visit_local(&mut self, local: &'tcx Local) {
+ if let Some((init, els)) = &local.kind.init_else_opt() {
+ self.apply(|this| walk_expr(this, init));
+ if let Some(els) = els {
+ self.apply(|this| walk_block(this, els));
+ }
+ }
+ // add the pattern after the expression because the bindings aren't available
+ // yet in the init
+ // expression
+ SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
+ }
+ fn visit_block(&mut self, blk: &'tcx Block) {
+ self.single_char_names.push(vec![]);
+
+ self.apply(|this| walk_block(this, blk));
+
+ self.check_single_char_names();
+ self.single_char_names.pop();
+ }
+ fn visit_arm(&mut self, arm: &'tcx Arm) {
+ self.single_char_names.push(vec![]);
+
+ self.apply(|this| {
+ SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
+ this.apply(|this| walk_expr(this, &arm.body));
+ });
+
+ self.check_single_char_names();
+ self.single_char_names.pop();
+ }
+ fn visit_item(&mut self, _: &Item) {
+ // do not recurse into inner items
+ }
+}
+
+impl EarlyLintPass for NonExpressiveNames {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ if in_external_macro(cx.sess, item.span) {
+ return;
+ }
+
+ if let ItemKind::Fn(box FnKind(_, ref sig, _, Some(ref blk))) = item.kind {
+ do_check(self, cx, &item.attrs, &sig.decl, blk);
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
+ if in_external_macro(cx.sess, item.span) {
+ return;
+ }
+
+ if let AssocItemKind::Fn(box FnKind(_, ref sig, _, Some(ref blk))) = item.kind {
+ do_check(self, cx, &item.attrs, &sig.decl, blk);
+ }
+ }
+}
+
+fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
+ if !attrs.iter().any(|attr| attr.has_name(sym::test)) {
+ let mut visitor = SimilarNamesLocalVisitor {
+ names: Vec::new(),
+ cx,
+ lint,
+ single_char_names: vec![vec![]],
+ };
+
+ // initialize with function arguments
+ for arg in &decl.inputs {
+ SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
+ }
+ // walk all other bindings
+ walk_block(&mut visitor, blk);
+
+ visitor.check_single_char_names();
+ }
+}
+
+/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
+#[must_use]
+fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
+ debug_assert!(a_name.chars().count() < b_name.chars().count());
+ let mut a_chars = a_name.chars();
+ let mut b_chars = b_name.chars();
+ while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
+ if a == b {
+ continue;
+ }
+ if let Some(b2) = b_chars.next() {
+ // check if there's just one character inserted
+ return a != b2 || a_chars.ne(b_chars);
+ }
+ // tuple
+ // ntuple
+ return true;
+ }
+ // for item in items
+ true
+}
--- /dev/null
- .filter_map(|ty| get_rptr_lm(ty))
+//! Checks for usage of `&Vec[_]` and `&String`.
+
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::ptr::get_spans;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
+use clippy_utils::{expr_path_res, is_lint_allowed, match_any_diagnostic_items, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{
+ BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, Impl, ImplItem, ImplItemKind, Item,
+ ItemKind, Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
+};
+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 rustc_span::symbol::Symbol;
+use rustc_span::{sym, MultiSpan};
+use std::borrow::Cow;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// This lint checks for function arguments of type `&String`
+ /// or `&Vec` unless the references are mutable. It will also suggest you
+ /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
+ /// calls.
+ ///
+ /// ### Why is this bad?
+ /// Requiring the argument to be of the specific size
+ /// makes the function less useful for no benefit; slices in the form of `&[T]`
+ /// or `&str` usually suffice and can be obtained from other types, too.
+ ///
+ /// ### Known problems
+ /// The lint does not follow data. So if you have an
+ /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
+ /// changing that `.clone()` to `.to_owned()`.
+ ///
+ /// Other functions called from this function taking a `&String` or `&Vec`
+ /// argument may also fail to compile if you change the argument. Applying
+ /// this lint on them will fix the problem, but they may be in other crates.
+ ///
+ /// One notable example of a function that may cause issues, and which cannot
+ /// easily be changed due to being in the standard library is `Vec::contains`.
+ /// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then
+ /// it will compile, but if a `&[T]` is passed then it will not compile.
+ ///
+ /// ```ignore
+ /// fn cannot_take_a_slice(v: &Vec<u8>) -> bool {
+ /// let vec_of_vecs: Vec<Vec<u8>> = some_other_fn();
+ ///
+ /// vec_of_vecs.contains(v)
+ /// }
+ /// ```
+ ///
+ /// Also there may be `fn(&Vec)`-typed references pointing to your function.
+ /// If you have them, you will get a compiler error after applying this lint's
+ /// suggestions. You then have the choice to undo your changes or change the
+ /// type of the reference.
+ ///
+ /// Note that if the function is part of your public interface, there may be
+ /// other crates referencing it, of which you may not be aware. Carefully
+ /// deprecate the function before applying the lint suggestions in this case.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// // Bad
+ /// fn foo(&Vec<u32>) { .. }
+ ///
+ /// // Good
+ /// fn foo(&[u32]) { .. }
+ /// ```
+ pub PTR_ARG,
+ style,
+ "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// This lint checks for equality comparisons with `ptr::null`
+ ///
+ /// ### Why is this bad?
+ /// It's easier and more readable to use the inherent
+ /// `.is_null()`
+ /// method instead
+ ///
+ /// ### Example
+ /// ```ignore
+ /// // Bad
+ /// if x == ptr::null {
+ /// ..
+ /// }
+ ///
+ /// // Good
+ /// if x.is_null() {
+ /// ..
+ /// }
+ /// ```
+ pub CMP_NULL,
+ style,
+ "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// This lint checks for functions that take immutable
+ /// references and return mutable ones.
+ ///
+ /// ### Why is this bad?
+ /// This is trivially unsound, as one can create two
+ /// mutable references from the same (immutable!) source.
+ /// This [error](https://github.com/rust-lang/rust/issues/39465)
+ /// actually lead to an interim Rust release 1.15.1.
+ ///
+ /// ### Known problems
+ /// To be on the conservative side, if there's at least one
+ /// mutable reference with the output lifetime, this lint will not trigger.
+ /// In practice, this case is unlikely anyway.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// fn foo(&Foo) -> &mut Bar { .. }
+ /// ```
+ pub MUT_FROM_REF,
+ correctness,
+ "fns that create mutable refs from immutable ref args"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// This lint checks for invalid usages of `ptr::null`.
+ ///
+ /// ### Why is this bad?
+ /// This causes undefined behavior.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// // Bad. Undefined behavior
+ /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
+ /// ```
+ ///
+ /// // Good
+ /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
+ /// ```
+ pub INVALID_NULL_PTR_USAGE,
+ correctness,
+ "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
+}
+
+declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
+
+impl<'tcx> LateLintPass<'tcx> for Ptr {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
+ check_fn(cx, sig.decl, item.hir_id(), Some(body_id));
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
+ if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
+ let parent_item = cx.tcx.hir().get_parent_item(item.hir_id());
+ if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
+ if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = it.kind {
+ return; // ignore trait impls
+ }
+ }
+ check_fn(cx, sig.decl, item.hir_id(), Some(body_id));
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
+ let body_id = if let TraitFn::Provided(b) = *trait_method {
+ Some(b)
+ } else {
+ None
+ };
+ check_fn(cx, sig.decl, item.hir_id(), body_id);
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if let ExprKind::Binary(ref op, l, r) = expr.kind {
+ if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
+ span_lint(
+ cx,
+ CMP_NULL,
+ expr.span,
+ "comparing with null is better expressed by the `.is_null()` method",
+ );
+ }
+ } else {
+ check_invalid_ptr_usage(cx, expr);
+ }
+ }
+}
+
+fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
+ const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
+ (&paths::SLICE_FROM_RAW_PARTS, &[0]),
+ (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
+ (&paths::PTR_COPY, &[0, 1]),
+ (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
+ (&paths::PTR_READ, &[0]),
+ (&paths::PTR_READ_UNALIGNED, &[0]),
+ (&paths::PTR_READ_VOLATILE, &[0]),
+ (&paths::PTR_REPLACE, &[0]),
+ (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
+ (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
+ (&paths::PTR_SWAP, &[0, 1]),
+ (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
+ (&paths::PTR_WRITE, &[0]),
+ (&paths::PTR_WRITE_UNALIGNED, &[0]),
+ (&paths::PTR_WRITE_VOLATILE, &[0]),
+ (&paths::PTR_WRITE_BYTES, &[0]),
+ ];
+
+ 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();
+ let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
+ if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
+ .iter()
+ .find(|&&(fn_path, _)| fn_path == fun_def_path);
+ then {
+ for &arg_idx in arg_indices {
+ if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
+ span_lint_and_sugg(
+ cx,
+ INVALID_NULL_PTR_USAGE,
+ arg.span,
+ "pointer must be non-null",
+ "change this to",
+ "core::ptr::NonNull::dangling().as_ptr()".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option<BodyId>) {
+ let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
+ let sig = cx.tcx.fn_sig(fn_def_id);
+ let fn_ty = sig.skip_binder();
+ let body = opt_body_id.map(|id| cx.tcx.hir().body(id));
+
+ for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
+ // Honor the allow attribute on parameters. See issue 5644.
+ if let Some(body) = &body {
+ if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
+ continue;
+ }
+ }
+
+ if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
+ if is_type_diagnostic_item(cx, ty, sym::vec_type) {
+ if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
+ span_lint_and_then(
+ cx,
+ PTR_ARG,
+ arg.span,
+ "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
+ with non-Vec-based slices",
+ |diag| {
+ if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) {
+ diag.span_suggestion(
+ arg.span,
+ "change this to",
+ format!("&[{}]", snippet),
+ Applicability::Unspecified,
+ );
+ }
+ for (clonespan, suggestion) in spans {
+ diag.span_suggestion(
+ clonespan,
+ &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
+ Cow::Owned(format!("change `{}` to", x))
+ }),
+ suggestion.into(),
+ Applicability::Unspecified,
+ );
+ }
+ },
+ );
+ }
+ } else if is_type_diagnostic_item(cx, ty, sym::string_type) {
+ if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
+ span_lint_and_then(
+ cx,
+ PTR_ARG,
+ arg.span,
+ "writing `&String` instead of `&str` involves a new object where a slice will do",
+ |diag| {
+ diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
+ for (clonespan, suggestion) in spans {
+ diag.span_suggestion_short(
+ clonespan,
+ &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
+ Cow::Owned(format!("change `{}` to", x))
+ }),
+ suggestion.into(),
+ Applicability::Unspecified,
+ );
+ }
+ },
+ );
+ }
+ } else if is_type_diagnostic_item(cx, ty, sym::PathBuf) {
+ if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) {
+ span_lint_and_then(
+ cx,
+ PTR_ARG,
+ arg.span,
+ "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do",
+ |diag| {
+ diag.span_suggestion(
+ arg.span,
+ "change this to",
+ "&Path".into(),
+ Applicability::Unspecified,
+ );
+ for (clonespan, suggestion) in spans {
+ diag.span_suggestion_short(
+ clonespan,
+ &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
+ Cow::Owned(format!("change `{}` to", x))
+ }),
+ suggestion.into(),
+ Applicability::Unspecified,
+ );
+ }
+ },
+ );
+ }
+ } else if match_type(cx, ty, &paths::COW) {
+ if_chain! {
+ if let TyKind::Rptr(_, MutTy { ty, ..} ) = arg.kind;
+ if let TyKind::Path(QPath::Resolved(None, pp)) = ty.kind;
+ if let [ref bx] = *pp.segments;
+ if let Some(params) = bx.args;
+ if !params.parenthesized;
+ if let Some(inner) = params.args.iter().find_map(|arg| match arg {
+ GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ });
+ let replacement = snippet_opt(cx, inner.span);
+ if let Some(r) = replacement;
+ then {
+ span_lint_and_sugg(
+ cx,
+ PTR_ARG,
+ arg.span,
+ "using a reference to `Cow` is not recommended",
+ "change this to",
+ "&".to_owned() + &r,
+ Applicability::Unspecified,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ if let FnRetTy::Return(ty) = decl.output {
+ if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
+ let mut immutables = vec![];
+ for (_, ref mutbl, ref argspan) in decl
+ .inputs
+ .iter()
++ .filter_map(get_rptr_lm)
+ .filter(|&(lt, _, _)| lt.name == out.name)
+ {
+ if *mutbl == Mutability::Mut {
+ return;
+ }
+ immutables.push(*argspan);
+ }
+ if immutables.is_empty() {
+ return;
+ }
+ span_lint_and_then(
+ cx,
+ MUT_FROM_REF,
+ ty.span,
+ "mutable borrow from immutable input(s)",
+ |diag| {
+ let ms = MultiSpan::from_spans(immutables);
+ diag.span_note(ms, "immutable borrow here");
+ },
+ );
+ }
+ }
+}
+
+fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> {
+ if_chain! {
+ if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind;
+ if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last();
+ let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
+ GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ }).collect();
+ if types.len() == 1;
+ then {
+ snippet_opt(cx, types[0].span)
+ } else {
+ None
+ }
+ }
+}
+
+fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
+ if let TyKind::Rptr(ref lt, ref m) = ty.kind {
+ Some((lt, m.mutbl, ty.span))
+ } else {
+ None
+ }
+}
+
+fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let ExprKind::Call(pathexp, []) = expr.kind {
+ expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
+ match_any_diagnostic_items(cx, id, &[sym::ptr_null, sym::ptr_null_mut]).is_some()
+ })
+ } else {
+ false
+ }
+}
--- /dev/null
- use rustc_session::{declare_tool_lint, impl_lint_pass};
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::{match_def_path, paths};
+use if_chain::if_chain;
+use rustc_ast::ast::{LitKind, StrStyle};
+use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
- #[derive(Clone, Default)]
- pub struct Regex {}
-
- impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
++use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::{BytePos, Span};
+use std::convert::TryFrom;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks [regex](https://crates.io/crates/regex) creation
+ /// (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct
+ /// regex syntax.
+ ///
+ /// ### Why is this bad?
+ /// This will lead to a runtime panic.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// Regex::new("|")
+ /// ```
+ pub INVALID_REGEX,
+ correctness,
+ "invalid regular expressions"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for trivial [regex](https://crates.io/crates/regex)
+ /// creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).
+ ///
+ /// ### Why is this bad?
+ /// Matching the regex can likely be replaced by `==` or
+ /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str`
+ /// methods.
+ ///
+ /// ### Known problems
+ /// If the same regex is going to be applied to multiple
+ /// inputs, the precomputations done by `Regex` construction can give
+ /// significantly better performance than any of the `str`-based methods.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// Regex::new("^foobar")
+ /// ```
+ pub TRIVIAL_REGEX,
+ nursery,
+ "trivial regular expressions"
+}
+
++declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
+
+impl<'tcx> LateLintPass<'tcx> for Regex {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Call(fun, args) = expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if args.len() == 1;
+ if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ then {
+ if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
+ match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
+ check_regex(cx, &args[0], true);
+ } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) ||
+ match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
+ check_regex(cx, &args[0], false);
+ } else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) {
+ check_set(cx, &args[0], true);
+ } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) {
+ check_set(cx, &args[0], false);
+ }
+ }
+ }
+ }
+}
+
+#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here
+#[must_use]
+fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u16) -> Span {
+ let offset = u32::from(offset);
+ let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset);
+ let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset);
+ assert!(start <= end);
+ Span::new(start, end, base.ctxt(), base.parent())
+}
+
+fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
+ constant(cx, cx.typeck_results(), e).and_then(|(c, _)| match c {
+ Constant::Str(s) => Some(s),
+ _ => None,
+ })
+}
+
+fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> {
+ use regex_syntax::hir::Anchor::{EndText, StartText};
+ use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal};
+
+ let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_)));
+
+ match *s.kind() {
+ Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"),
+ Literal(_) => Some("consider using `str::contains`"),
+ Alternation(ref exprs) => {
+ if exprs.iter().all(|e| e.kind().is_empty()) {
+ Some("the regex is unlikely to be useful as it is")
+ } else {
+ None
+ }
+ },
+ Concat(ref exprs) => match (exprs[0].kind(), exprs[exprs.len() - 1].kind()) {
+ (&Anchor(StartText), &Anchor(EndText)) if exprs[1..(exprs.len() - 1)].is_empty() => {
+ Some("consider using `str::is_empty`")
+ },
+ (&Anchor(StartText), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
+ Some("consider using `==` on `str`s")
+ },
+ (&Anchor(StartText), &Literal(_)) if is_literal(&exprs[1..]) => Some("consider using `str::starts_with`"),
+ (&Literal(_), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
+ Some("consider using `str::ends_with`")
+ },
+ _ if is_literal(exprs) => Some("consider using `str::contains`"),
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
+ if_chain! {
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
+ if let ExprKind::Array(exprs) = expr.kind;
+ then {
+ for expr in exprs {
+ check_regex(cx, expr, utf8);
+ }
+ }
+ }
+}
+
+fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
+ let mut parser = regex_syntax::ParserBuilder::new()
+ .unicode(true)
+ .allow_invalid_utf8(!utf8)
+ .build();
+
+ if let ExprKind::Lit(ref lit) = expr.kind {
+ if let LitKind::Str(ref r, style) = lit.node {
+ let r = &r.as_str();
+ let offset = if let StrStyle::Raw(n) = style { 2 + n } else { 1 };
+ match parser.parse(r) {
+ Ok(r) => {
+ if let Some(repl) = is_trivial_regex(&r) {
+ span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
+ }
+ },
+ Err(regex_syntax::Error::Parse(e)) => {
+ span_lint(
+ cx,
+ INVALID_REGEX,
+ str_span(expr.span, *e.span(), offset),
+ &format!("regex syntax error: {}", e.kind()),
+ );
+ },
+ Err(regex_syntax::Error::Translate(e)) => {
+ span_lint(
+ cx,
+ INVALID_REGEX,
+ str_span(expr.span, *e.span(), offset),
+ &format!("regex syntax error: {}", e.kind()),
+ );
+ },
+ Err(e) => {
+ span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
+ },
+ }
+ }
+ } else if let Some(r) = const_str(cx, expr) {
+ match parser.parse(&r) {
+ Ok(r) => {
+ if let Some(repl) = is_trivial_regex(&r) {
+ span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
+ }
+ },
+ Err(regex_syntax::Error::Parse(e)) => {
+ span_lint(
+ cx,
+ INVALID_REGEX,
+ expr.span,
+ &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
+ );
+ },
+ Err(regex_syntax::Error::Translate(e)) => {
+ span_lint(
+ cx,
+ INVALID_REGEX,
+ expr.span,
+ &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
+ );
+ },
+ Err(e) => {
+ span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
+ },
+ }
+ }
+}
--- /dev/null
- check_final_expr(cx, else_clause, None, RetReplacement::Empty);
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
+use if_chain::if_chain;
+use rustc_ast::ast::Attribute;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::hygiene::DesugaringKind;
+use rustc_span::source_map::Span;
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `let`-bindings, which are subsequently
+ /// returned.
+ ///
+ /// ### Why is this bad?
+ /// It is just extraneous code. Remove it to make your code
+ /// more rusty.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn foo() -> String {
+ /// let x = String::new();
+ /// x
+ /// }
+ /// ```
+ /// instead, use
+ /// ```
+ /// fn foo() -> String {
+ /// String::new()
+ /// }
+ /// ```
+ pub LET_AND_RETURN,
+ style,
+ "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for return statements at the end of a block.
+ ///
+ /// ### Why is this bad?
+ /// Removing the `return` and semicolon will make the code
+ /// more rusty.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn foo(x: usize) -> usize {
+ /// return x;
+ /// }
+ /// ```
+ /// simplify to
+ /// ```rust
+ /// fn foo(x: usize) -> usize {
+ /// x
+ /// }
+ /// ```
+ pub NEEDLESS_RETURN,
+ style,
+ "using a return statement like `return expr;` where an expression would suffice"
+}
+
+#[derive(PartialEq, Eq, Copy, Clone)]
+enum RetReplacement {
+ Empty,
+ Block,
+}
+
+declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
+
+impl<'tcx> LateLintPass<'tcx> for Return {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
+ // we need both a let-binding stmt and an expr
+ if_chain! {
+ if let Some(retexpr) = block.expr;
+ if let Some(stmt) = block.stmts.iter().last();
+ if let StmtKind::Local(local) = &stmt.kind;
+ if local.ty.is_none();
+ if cx.tcx.hir().attrs(local.hir_id).is_empty();
+ if let Some(initexpr) = &local.init;
+ if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
+ if path_to_local_id(retexpr, local_id);
+ if !last_statement_borrows(cx, initexpr);
+ if !in_external_macro(cx.sess(), initexpr.span);
+ if !in_external_macro(cx.sess(), retexpr.span);
+ if !in_external_macro(cx.sess(), local.span);
+ if !in_macro(local.span);
+ then {
+ span_lint_and_then(
+ cx,
+ LET_AND_RETURN,
+ retexpr.span,
+ "returning the result of a `let` binding from a block",
+ |err| {
+ err.span_label(local.span, "unnecessary `let` binding");
+
+ if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
+ if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
+ snippet.push_str(" as _");
+ }
+ err.multipart_suggestion(
+ "return the expression directly",
+ vec![
+ (local.span, String::new()),
+ (retexpr.span, snippet),
+ ],
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_help(initexpr.span, "this expression can be directly returned");
+ }
+ },
+ );
+ }
+ }
+ }
+
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ kind: FnKind<'tcx>,
+ _: &'tcx FnDecl<'tcx>,
+ body: &'tcx Body<'tcx>,
+ _: Span,
+ _: HirId,
+ ) {
+ match kind {
+ FnKind::Closure => {
+ // when returning without value in closure, replace this `return`
+ // with an empty block to prevent invalid suggestion (see #6501)
+ let replacement = if let ExprKind::Ret(None) = &body.value.kind {
+ RetReplacement::Block
+ } else {
+ RetReplacement::Empty
+ };
+ check_final_expr(cx, &body.value, Some(body.value.span), replacement);
+ },
+ FnKind::ItemFn(..) | FnKind::Method(..) => {
+ if let ExprKind::Block(block, _) = body.value.kind {
+ check_block_return(cx, block);
+ }
+ },
+ }
+ }
+}
+
+fn attr_is_cfg(attr: &Attribute) -> bool {
+ attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
+}
+
+fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+ if let Some(expr) = block.expr {
+ check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
+ } else if let Some(stmt) = block.stmts.iter().last() {
+ match stmt.kind {
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
+ check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
+ },
+ _ => (),
+ }
+ }
+}
+
+fn check_final_expr<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ span: Option<Span>,
+ replacement: RetReplacement,
+) {
+ match expr.kind {
+ // simple return is always "bad"
+ ExprKind::Ret(ref inner) => {
+ // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
+ let attrs = cx.tcx.hir().attrs(expr.hir_id);
+ if !attrs.iter().any(attr_is_cfg) {
+ let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
+ if !borrows {
+ emit_return_lint(
+ cx,
+ span.expect("`else return` is not possible"),
+ inner.as_ref().map(|i| i.span),
+ replacement,
+ );
+ }
+ }
+ },
+ // a whole block? check it!
+ ExprKind::Block(block, _) => {
+ check_block_return(cx, block);
+ },
+ ExprKind::If(_, then, else_clause_opt) => {
+ if let ExprKind::Block(ifblock, _) = then.kind {
+ check_block_return(cx, ifblock);
+ }
+ if let Some(else_clause) = else_clause_opt {
++ if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) {
++ check_final_expr(cx, else_clause, None, RetReplacement::Empty);
++ }
+ }
+ },
+ // a match expr, check all arms
+ // an if/if let expr, check both exprs
+ // note, if without else is going to be a type checking error anyways
+ // (except for unit type functions) so we don't match it
+ ExprKind::Match(_, arms, MatchSource::Normal) => {
+ for arm in arms.iter() {
+ check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
+ }
+ },
+ ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
+ _ => (),
+ }
+}
+
+fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
+ if ret_span.from_expansion() {
+ return;
+ }
+ match inner_span {
+ Some(inner_span) => {
+ if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
+ return;
+ }
+
+ span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
+ if let Some(snippet) = snippet_opt(cx, inner_span) {
+ diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
+ }
+ });
+ },
+ None => match replacement {
+ RetReplacement::Empty => {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_RETURN,
+ ret_span,
+ "unneeded `return` statement",
+ "remove `return`",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ },
+ RetReplacement::Block => {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_RETURN,
+ ret_span,
+ "unneeded `return` statement",
+ "replace `return` with an empty block",
+ "{}".to_string(),
+ Applicability::MachineApplicable,
+ );
+ },
+ },
+ }
+}
+
+fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ let mut visitor = BorrowVisitor { cx, borrows: false };
+ walk_expr(&mut visitor, expr);
+ visitor.borrows
+}
+
+struct BorrowVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ borrows: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.borrows {
+ return;
+ }
+
+ if let Some(def_id) = fn_def_id(self.cx, expr) {
+ self.borrows = self
+ .cx
+ .tcx
+ .fn_sig(def_id)
+ .output()
+ .skip_binder()
+ .walk(self.cx.tcx)
+ .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
+ }
+
+ walk_expr(self, expr);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_then;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{Crate, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::AssocKind;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Symbol;
++use rustc_span::Span;
++use std::collections::{BTreeMap, BTreeSet};
++
++declare_clippy_lint! {
++ /// ### What it does
++ /// It lints if a struct has two method with same time:
++ /// one from a trait, another not from trait.
++ ///
++ /// ### Why is this bad?
++ /// Confusing.
++ ///
++ /// ### Example
++ /// ```rust
++ /// trait T {
++ /// fn foo(&self) {}
++ /// }
++ ///
++ /// struct S;
++ ///
++ /// impl T for S {
++ /// fn foo(&self) {}
++ /// }
++ ///
++ /// impl S {
++ /// fn foo(&self) {}
++ /// }
++ /// ```
++ pub SAME_NAME_METHOD,
++ restriction,
++ "two method with same name"
++}
++
++declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]);
++
++struct ExistingName {
++ impl_methods: BTreeMap<Symbol, Span>,
++ trait_methods: BTreeMap<Symbol, Vec<Span>>,
++}
++
++impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
++ fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'tcx>) {
++ let mut map = FxHashMap::<Res, ExistingName>::default();
++
++ for item in krate.items() {
++ if let ItemKind::Impl(Impl {
++ items,
++ of_trait,
++ self_ty,
++ ..
++ }) = &item.kind
++ {
++ if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
++ if !map.contains_key(res) {
++ map.insert(
++ *res,
++ ExistingName {
++ impl_methods: BTreeMap::new(),
++ trait_methods: BTreeMap::new(),
++ },
++ );
++ }
++ let existing_name = map.get_mut(res).unwrap();
++
++ match of_trait {
++ Some(trait_ref) => {
++ let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
++ if let Some(Node::TraitRef(TraitRef { path, .. })) =
++ cx.tcx.hir().find(trait_ref.hir_ref_id);
++ if let Res::Def(DefKind::Trait, did) = path.res;
++ then{
++ // FIXME: if
++ // `rustc_middle::ty::assoc::AssocItems::items` is public,
++ // we can iterate its keys instead of `in_definition_order`,
++ // which's more efficient
++ cx.tcx
++ .associated_items(did)
++ .in_definition_order()
++ .filter(|assoc_item| {
++ matches!(assoc_item.kind, AssocKind::Fn)
++ })
++ .map(|assoc_item| assoc_item.ident.name)
++ .collect()
++ }else{
++ BTreeSet::new()
++ }
++ };
++
++ let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
++ if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
++ span_lint_and_then(
++ cx,
++ SAME_NAME_METHOD,
++ *impl_span,
++ "method's name is same to an existing method in a trait",
++ |diag| {
++ diag.span_note(
++ trait_method_span,
++ &format!("existing `{}` defined here", method_name),
++ );
++ },
++ );
++ }
++ if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
++ v.push(trait_method_span);
++ } else {
++ existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
++ }
++ };
++
++ for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
++ matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
++ }) {
++ let method_name = impl_item_ref.ident.name;
++ methods_in_trait.remove(&method_name);
++ check_trait_method(method_name, impl_item_ref.span);
++ }
++
++ for method_name in methods_in_trait {
++ check_trait_method(method_name, item.span);
++ }
++ },
++ None => {
++ for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
++ matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
++ }) {
++ let method_name = impl_item_ref.ident.name;
++ let impl_span = impl_item_ref.span;
++ if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
++ span_lint_and_then(
++ cx,
++ SAME_NAME_METHOD,
++ impl_span,
++ "method's name is same to an existing method in a trait",
++ |diag| {
++ // TODO should we `span_note` on every trait?
++ // iterate on trait_spans?
++ diag.span_note(
++ trait_spans[0],
++ &format!("existing `{}` defined here", method_name),
++ );
++ },
++ );
++ }
++ existing_name.impl_methods.insert(method_name, impl_span);
++ }
++ },
++ }
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_help;
++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::BOX_COLLECTION;
++
++pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
++ if_chain! {
++ if Some(def_id) == cx.tcx.lang_items().owned_box();
++ if let Some(item_type) = get_std_collection(cx, qpath);
++ then {
++ let generic = if item_type == "String" {
++ ""
++ } else {
++ "<..>"
++ };
++ span_lint_and_help(
++ cx,
++ BOX_COLLECTION,
++ hir_ty.span,
++ &format!(
++ "you seem to be trying to use `Box<{outer}{generic}>`. Consider using just `{outer}{generic}`",
++ outer=item_type,
++ generic = generic),
++ None,
++ &format!(
++ "`{outer}{generic}` is already on the heap, `Box<{outer}{generic}>` makes an extra allocation",
++ outer=item_type,
++ generic = generic)
++ );
++ true
++ } else {
++ false
++ }
++ }
++}
++
++fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
++ if is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some() {
++ Some("Vec")
++ } else if is_ty_param_diagnostic_item(cx, qpath, sym::string_type).is_some() {
++ Some("String")
++ } else if is_ty_param_diagnostic_item(cx, qpath, sym::hashmap_type).is_some() {
++ Some("HashMap")
++ } else {
++ None
++ }
++}
--- /dev/null
- mod box_vec;
+mod borrowed_box;
- /// Checks for use of `Box<Vec<_>>` anywhere in the code.
++mod box_collection;
+mod linked_list;
+mod option_option;
+mod rc_buffer;
+mod rc_mutex;
+mod redundant_allocation;
+mod type_complexity;
+mod utils;
+mod vec_box;
+
+use rustc_hir as hir;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{
+ Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
+ TraitItemKind, TyKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
- /// `Vec` already keeps its contents in a separate area on
- /// the heap. So if you `Box` it, you just add another level of indirection
++ /// Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code.
+ /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
+ ///
+ /// ### Why is this bad?
- pub BOX_VEC,
++ /// Collections already keeps their contents in a separate area on
++ /// the heap. So if you `Box` them, you just add another level of indirection
+ /// without any benefit whatsoever.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// struct X {
+ /// values: Box<Vec<Foo>>,
+ /// }
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust,ignore
+ /// struct X {
+ /// values: Vec<Foo>,
+ /// }
+ /// ```
- impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
++ pub BOX_COLLECTION,
+ perf,
+ "usage of `Box<Vec<T>>`, vector elements are already on the heap"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
+ /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
+ ///
+ /// ### Why is this bad?
+ /// `Vec` already keeps its contents in a separate area on
+ /// the heap. So if you `Box` its contents, you just add another level of indirection.
+ ///
+ /// ### Known problems
+ /// Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),
+ /// 1st comment).
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct X {
+ /// values: Vec<Box<i32>>,
+ /// }
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust
+ /// struct X {
+ /// values: Vec<i32>,
+ /// }
+ /// ```
+ pub VEC_BOX,
+ complexity,
+ "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `Option<Option<_>>` in function signatures and type
+ /// definitions
+ ///
+ /// ### Why is this bad?
+ /// `Option<_>` represents an optional value. `Option<Option<_>>`
+ /// represents an optional optional value which is logically the same thing as an optional
+ /// value but has an unneeded extra level of wrapping.
+ ///
+ /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
+ /// consider a custom `enum` instead, with clear names for each case.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn get_data() -> Option<Option<u32>> {
+ /// None
+ /// }
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust
+ /// pub enum Contents {
+ /// Data(Vec<u8>), // Was Some(Some(Vec<u8>))
+ /// NotYetFetched, // Was Some(None)
+ /// None, // Was None
+ /// }
+ ///
+ /// fn get_data() -> Contents {
+ /// Contents::None
+ /// }
+ /// ```
+ pub OPTION_OPTION,
+ pedantic,
+ "usage of `Option<Option<T>>`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of any `LinkedList`, suggesting to use a
+ /// `Vec` or a `VecDeque` (formerly called `RingBuf`).
+ ///
+ /// ### Why is this bad?
+ /// Gankro says:
+ ///
+ /// > The TL;DR of `LinkedList` is that it's built on a massive amount of
+ /// pointers and indirection.
+ /// > It wastes memory, it has terrible cache locality, and is all-around slow.
+ /// `RingBuf`, while
+ /// > "only" amortized for push/pop, should be faster in the general case for
+ /// almost every possible
+ /// > workload, and isn't even amortized at all if you can predict the capacity
+ /// you need.
+ /// >
+ /// > `LinkedList`s are only really good if you're doing a lot of merging or
+ /// splitting of lists.
+ /// > This is because they can just mangle some pointers instead of actually
+ /// copying the data. Even
+ /// > if you're doing a lot of insertion in the middle of the list, `RingBuf`
+ /// can still be better
+ /// > because of how expensive it is to seek to the middle of a `LinkedList`.
+ ///
+ /// ### Known problems
+ /// False positives – the instances where using a
+ /// `LinkedList` makes sense are few and far between, but they can still happen.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::collections::LinkedList;
+ /// let x: LinkedList<usize> = LinkedList::new();
+ /// ```
+ pub LINKEDLIST,
+ pedantic,
+ "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `&Box<T>` anywhere in the code.
+ /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
+ ///
+ /// ### Why is this bad?
+ /// Any `&Box<T>` can also be a `&T`, which is more
+ /// general.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// fn foo(bar: &Box<T>) { ... }
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust,ignore
+ /// fn foo(bar: &T) { ... }
+ /// ```
+ pub BORROWED_BOX,
+ complexity,
+ "a borrow of a boxed type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of redundant allocations anywhere in the code.
+ ///
+ /// ### Why is this bad?
+ /// Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, `Arc<&T>`, `Arc<Rc<T>>`,
+ /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::rc::Rc;
+ /// fn foo(bar: Rc<&usize>) {}
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust
+ /// fn foo(bar: &usize) {}
+ /// ```
+ pub REDUNDANT_ALLOCATION,
+ perf,
+ "redundant allocation"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
+ ///
+ /// ### Why is this bad?
+ /// Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
+ /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
+ ///
+ /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only
+ /// works if there are no additional references yet, which usually defeats the purpose of
+ /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
+ /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally
+ /// be used.
+ ///
+ /// ### Known problems
+ /// This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
+ /// cases where mutation only happens before there are any additional references.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// # use std::rc::Rc;
+ /// fn foo(interned: Rc<String>) { ... }
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust,ignore
+ /// fn foo(interned: Rc<str>) { ... }
+ /// ```
+ pub RC_BUFFER,
+ restriction,
+ "shared ownership of a buffer type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for types used in structs, parameters and `let`
+ /// declarations above a certain complexity threshold.
+ ///
+ /// ### Why is this bad?
+ /// Too complex types make the code less readable. Consider
+ /// using a `type` definition to simplify them.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::rc::Rc;
+ /// struct Foo {
+ /// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
+ /// }
+ /// ```
+ pub TYPE_COMPLEXITY,
+ complexity,
+ "usage of very complex types that might be better factored into `type` definitions"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `Rc<Mutex<T>>`.
+ ///
+ /// ### Why is this bad?
+ /// `Rc` is used in single thread and `Mutex` is used in multi thread.
+ /// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
+ ///
+ /// ### Known problems
+ /// Sometimes combining generic types can lead to the requirement that a
+ /// type use Rc in conjunction with Mutex. We must consider those cases false positives, but
+ /// alas they are quite hard to rule out. Luckily they are also rare.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// use std::rc::Rc;
+ /// use std::sync::Mutex;
+ /// fn foo(interned: Rc<Mutex<i32>>) { ... }
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust,ignore
+ /// use std::rc::Rc;
+ /// use std::cell::RefCell
+ /// fn foo(interned: Rc<RefCell<i32>>) { ... }
+ /// ```
+ pub RC_MUTEX,
+ restriction,
+ "usage of `Rc<Mutex<T>>`"
+}
+
+pub struct Types {
+ vec_box_size_threshold: u64,
+ type_complexity_threshold: u64,
+ avoid_breaking_exported_api: bool,
+}
+
- triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
++impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
+
+impl<'tcx> LateLintPass<'tcx> for Types {
+ fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
+ let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id))
+ {
+ matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
+ } else {
+ false
+ };
+
+ let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id));
+
+ self.check_fn_decl(
+ cx,
+ decl,
+ CheckTyContext {
+ is_in_trait_impl,
+ is_exported,
+ ..CheckTyContext::default()
+ },
+ );
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ let is_exported = cx.access_levels.is_exported(item.def_id);
+
+ match item.kind {
+ ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
+ cx,
+ ty,
+ CheckTyContext {
+ is_exported,
+ ..CheckTyContext::default()
+ },
+ ),
+ // functions, enums, structs, impls and traits are covered
+ _ => (),
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
+ match item.kind {
+ ImplItemKind::Const(ty, _) | ImplItemKind::TyAlias(ty) => self.check_ty(
+ cx,
+ ty,
+ CheckTyContext {
+ is_in_trait_impl: true,
+ ..CheckTyContext::default()
+ },
+ ),
+ // methods are covered by check_fn
+ ImplItemKind::Fn(..) => (),
+ }
+ }
+
+ fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
+ let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id));
+
+ self.check_ty(
+ cx,
+ field.ty,
+ CheckTyContext {
+ is_exported,
+ ..CheckTyContext::default()
+ },
+ );
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) {
+ let is_exported = cx.access_levels.is_exported(item.def_id);
+
+ let context = CheckTyContext {
+ is_exported,
+ ..CheckTyContext::default()
+ };
+
+ match item.kind {
+ TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
+ self.check_ty(cx, ty, context);
+ },
+ TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context),
+ TraitItemKind::Type(..) => (),
+ }
+ }
+
+ fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
+ if let Some(ty) = local.ty {
+ self.check_ty(
+ cx,
+ ty,
+ CheckTyContext {
+ is_local: true,
+ ..CheckTyContext::default()
+ },
+ );
+ }
+ }
+}
+
+impl Types {
+ pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self {
+ Self {
+ vec_box_size_threshold,
+ type_complexity_threshold,
+ avoid_breaking_exported_api,
+ }
+ }
+
+ fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
+ for input in decl.inputs {
+ self.check_ty(cx, input, context);
+ }
+
+ if let FnRetTy::Return(ty) = decl.output {
+ self.check_ty(cx, ty, context);
+ }
+ }
+
+ /// Recursively check for `TypePass` lints in the given type. Stop at the first
+ /// lint found.
+ ///
+ /// The parameter `is_local` distinguishes the context of the type.
+ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context: CheckTyContext) {
+ if hir_ty.span.from_expansion() {
+ return;
+ }
+
+ if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) {
+ return;
+ }
+
+ // Skip trait implementations; see issue #605.
+ if context.is_in_trait_impl {
+ return;
+ }
+
+ match hir_ty.kind {
+ TyKind::Path(ref qpath) if !context.is_local => {
+ let hir_id = hir_ty.hir_id;
+ let res = cx.qpath_res(qpath, hir_id);
+ if let Some(def_id) = res.opt_def_id() {
+ if self.is_type_change_allowed(context) {
+ // All lints that are being checked in this block are guarded by
+ // the `avoid_breaking_exported_api` configuration. When adding a
+ // new lint, please also add the name to the configuration documentation
+ // in `clippy_lints::utils::conf.rs`
+
+ let mut triggered = false;
++ triggered |= box_collection::check(cx, hir_ty, qpath, def_id);
+ triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
+ triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
+ triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
+ triggered |= option_option::check(cx, hir_ty, qpath, def_id);
+ triggered |= linked_list::check(cx, hir_ty, def_id);
+ triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
+
+ if triggered {
+ return;
+ }
+ }
+ }
+ match *qpath {
+ QPath::Resolved(Some(ty), p) => {
+ context.is_nested_call = true;
+ self.check_ty(cx, ty, context);
+ for ty in p.segments.iter().flat_map(|seg| {
+ seg.args
+ .as_ref()
+ .map_or_else(|| [].iter(), |params| params.args.iter())
+ .filter_map(|arg| match arg {
+ GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ })
+ }) {
+ self.check_ty(cx, ty, context);
+ }
+ },
+ QPath::Resolved(None, p) => {
+ context.is_nested_call = true;
+ for ty in p.segments.iter().flat_map(|seg| {
+ seg.args
+ .as_ref()
+ .map_or_else(|| [].iter(), |params| params.args.iter())
+ .filter_map(|arg| match arg {
+ GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ })
+ }) {
+ self.check_ty(cx, ty, context);
+ }
+ },
+ QPath::TypeRelative(ty, seg) => {
+ context.is_nested_call = true;
+ self.check_ty(cx, ty, context);
+ if let Some(params) = seg.args {
+ for ty in params.args.iter().filter_map(|arg| match arg {
+ GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ }) {
+ self.check_ty(cx, ty, context);
+ }
+ }
+ },
+ QPath::LangItem(..) => {},
+ }
+ },
+ TyKind::Rptr(ref lt, ref mut_ty) => {
+ context.is_nested_call = true;
+ if !borrowed_box::check(cx, hir_ty, lt, mut_ty) {
+ self.check_ty(cx, mut_ty.ty, context);
+ }
+ },
+ TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => {
+ context.is_nested_call = true;
+ self.check_ty(cx, ty, context);
+ },
+ TyKind::Tup(tys) => {
+ context.is_nested_call = true;
+ for ty in tys {
+ self.check_ty(cx, ty, context);
+ }
+ },
+ _ => {},
+ }
+ }
+
+ /// This function checks if the type is allowed to change in the current context
+ /// based on the `avoid_breaking_exported_api` configuration
+ fn is_type_change_allowed(&self, context: CheckTyContext) -> bool {
+ !(context.is_exported && self.avoid_breaking_exported_api)
+ }
+}
+
+#[allow(clippy::struct_excessive_bools)]
+#[derive(Clone, Copy, Default)]
+struct CheckTyContext {
+ is_in_trait_impl: bool,
+ /// `true` for types on local variables.
+ is_local: bool,
+ /// `true` for types that are part of the public API.
+ is_exported: bool,
+ is_nested_call: bool,
+}
--- /dev/null
- /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+//! 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! {
- (disallowed_methods: Vec<String> = Vec::new()),
++ /// 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()]),
+}
+
+/// 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
- if is_trigger_fn(fn_kind) {
- panic!("Would you like some help with that?");
- }
+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;
+use rustc_ast::ast::{Crate as AstCrate, 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::{
+ BinOpKind, Block, Crate, 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_type)
+ /// ```
+ 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(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
+ 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_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
+ 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) {
- span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
++ 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::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt())
++ 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<'_>, _: &Crate<'_>) {
+ 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
+//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
+//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
+//!
+//! This module and therefor the entire lint is guarded by a feature flag called
+//! `metadata-collector-lint`
+//!
+//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
+//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
+//! a simple mistake)
+
+use if_chain::if_chain;
+use rustc_ast as ast;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::{
+ self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
+};
+use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{sym, Loc, Span, Symbol};
+use serde::{ser::SerializeStruct, Serialize, Serializer};
+use std::collections::BinaryHeap;
+use std::fmt;
+use std::fs::{self, OpenOptions};
+use std::io::prelude::*;
+use std::path::Path;
+
+use crate::utils::internal_lints::is_lint_ref_type;
+use clippy_utils::{
+ diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
+ ty::walk_ptrs_ty_depth,
+};
+
+/// This is the output file of the lint collector.
+const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
+/// These lints are excluded from the export.
+const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"];
+/// These groups will be ignored by the lint group matcher. This is useful for collections like
+/// `clippy::all`
+const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
+/// Lints within this group will be excluded from the collection. These groups
+/// have to be defined without the `clippy::` prefix.
+const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
+/// Collected deprecated lint will be assigned to this group in the JSON output
+const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
+/// This is the lint level for deprecated lints that will be displayed in the lint list
+const DEPRECATED_LINT_LEVEL: &str = "none";
+/// This array holds Clippy's lint groups with their corresponding default lint level. The
+/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
+const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
+ ("correctness", "deny"),
+ ("suspicious", "warn"),
+ ("restriction", "allow"),
+ ("style", "warn"),
+ ("pedantic", "allow"),
+ ("complexity", "warn"),
+ ("perf", "warn"),
+ ("cargo", "allow"),
+ ("nursery", "allow"),
+];
+/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
+/// to only keep the actual lint group in the output.
+const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
+
+/// This template will be used to format the configuration section in the lint documentation.
+/// The `configurations` parameter will be replaced with one or multiple formatted
+/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations
+macro_rules! CONFIGURATION_SECTION_TEMPLATE {
+ () => {
+ r#"
+### Configuration
+This lint has the following configuration variables:
+
+{configurations}
+"#
+ };
+}
+/// This template will be used to format an individual `ClippyConfiguration` instance in the
+/// lint documentation.
+///
+/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and
+/// `default`
+macro_rules! CONFIGURATION_VALUE_TEMPLATE {
+ () => {
+ "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n"
+ };
+}
+
+const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
+ &["clippy_utils", "diagnostics", "span_lint"],
+ &["clippy_utils", "diagnostics", "span_lint_and_help"],
+ &["clippy_utils", "diagnostics", "span_lint_and_note"],
+ &["clippy_utils", "diagnostics", "span_lint_hir"],
+ &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
+ &["clippy_utils", "diagnostics", "span_lint_and_then"],
+ &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
+];
+const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
+ ("span_suggestion", false),
+ ("span_suggestion_short", false),
+ ("span_suggestion_verbose", false),
+ ("span_suggestion_hidden", false),
+ ("tool_only_span_suggestion", false),
+ ("multipart_suggestion", true),
+ ("multipart_suggestions", true),
+ ("tool_only_multipart_suggestion", true),
+ ("span_suggestions", true),
+];
+const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
+ &["clippy_utils", "diagnostics", "multispan_sugg"],
+ &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
+];
+const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
+
+/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
+const APPLICABILITY_NAME_INDEX: usize = 2;
+/// This applicability will be set for unresolved applicability values.
+const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Collects metadata about clippy lints for the website.
+ ///
+ /// This lint will be used to report problems of syntax parsing. You should hopefully never
+ /// see this but never say never I guess ^^
+ ///
+ /// ### Why is this bad?
+ /// This is not a bad thing but definitely a hacky way to do it. See
+ /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
+ /// about the implementation.
+ ///
+ /// ### Known problems
+ /// Hopefully none. It would be pretty uncool to have a problem here :)
+ ///
+ /// ### Example output
+ /// ```json,ignore
+ /// {
+ /// "id": "internal_metadata_collector",
+ /// "id_span": {
+ /// "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
+ /// "line": 1
+ /// },
+ /// "group": "clippy::internal",
+ /// "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
+ /// }
+ /// ```
+ pub INTERNAL_METADATA_COLLECTOR,
+ internal_warn,
+ "A busy bee collection metadata about lints"
+}
+
+impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
+
+#[allow(clippy::module_name_repetitions)]
+#[derive(Debug, Clone)]
+pub struct MetadataCollector {
+ /// All collected lints
+ ///
+ /// We use a Heap here to have the lints added in alphabetic order in the export
+ lints: BinaryHeap<LintMetadata>,
+ applicability_info: FxHashMap<String, ApplicabilityInfo>,
+ config: Vec<ClippyConfiguration>,
+}
+
+impl MetadataCollector {
+ pub fn new() -> Self {
+ Self {
+ lints: BinaryHeap::<LintMetadata>::default(),
+ applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
+ config: collect_configs(),
+ }
+ }
+
+ fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
+ self.config
+ .iter()
+ .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
+ .map(ToString::to_string)
+ .reduce(|acc, x| acc + &x)
+ .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
+ }
+}
+
+impl Drop for MetadataCollector {
+ /// You might ask: How hacky is this?
+ /// My answer: YES
+ fn drop(&mut self) {
+ // The metadata collector gets dropped twice, this makes sure that we only write
+ // when the list is full
+ if self.lints.is_empty() {
+ return;
+ }
+
+ let mut applicability_info = std::mem::take(&mut self.applicability_info);
+
+ // Mapping the final data
+ let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
+ lints
+ .iter_mut()
+ .for_each(|x| x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()));
+
+ // Outputting
+ if Path::new(OUTPUT_FILE).exists() {
+ fs::remove_file(OUTPUT_FILE).unwrap();
+ }
+ let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
+ writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
+ }
+}
+
+#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+struct LintMetadata {
+ id: String,
+ id_span: SerializableSpan,
+ group: String,
+ level: String,
+ docs: String,
+ /// This field is only used in the output and will only be
+ /// mapped shortly before the actual output.
+ applicability: Option<ApplicabilityInfo>,
+}
+
+impl LintMetadata {
+ fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self {
+ Self {
+ id,
+ id_span,
+ group,
+ level: level.to_string(),
+ docs,
+ applicability: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+struct SerializableSpan {
+ path: String,
+ line: usize,
+}
+
+impl std::fmt::Display for SerializableSpan {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
+ }
+}
+
+impl SerializableSpan {
+ fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
+ Self::from_span(cx, item.ident.span)
+ }
+
+ fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
+ let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
+
+ Self {
+ path: format!("{}", loc.file.name.prefer_remapped()),
+ line: loc.line,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
+struct ApplicabilityInfo {
+ /// Indicates if any of the lint emissions uses multiple spans. This is related to
+ /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
+ /// currently not be applied automatically.
+ is_multi_part_suggestion: bool,
+ applicability: Option<usize>,
+}
+
+impl Serialize for ApplicabilityInfo {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
+ s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
+ if let Some(index) = self.applicability {
+ s.serialize_field(
+ "applicability",
+ &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
+ )?;
+ } else {
+ s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
+ }
+ s.end()
+ }
+}
+
+// ==================================================================
+// Configuration
+// ==================================================================
+#[derive(Debug, Clone, Default)]
+pub struct ClippyConfiguration {
+ name: String,
+ config_type: &'static str,
+ default: String,
+ lints: Vec<String>,
+ doc: String,
++ #[allow(dead_code)]
+ deprecation_reason: Option<&'static str>,
+}
+
+impl ClippyConfiguration {
+ pub fn new(
+ name: &'static str,
+ config_type: &'static str,
+ default: String,
+ doc_comment: &'static str,
+ deprecation_reason: Option<&'static str>,
+ ) -> Self {
+ let (lints, doc) = parse_config_field_doc(doc_comment)
+ .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
+
+ Self {
+ name: to_kebab(name),
+ lints,
+ doc,
+ config_type,
+ default,
+ deprecation_reason,
+ }
+ }
+}
+
+fn collect_configs() -> Vec<ClippyConfiguration> {
+ crate::utils::conf::metadata::get_configuration_metadata()
+}
+
+/// This parses the field documentation of the config struct.
+///
+/// ```rust, ignore
+/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
+/// ```
+///
+/// Would yield:
+/// ```rust, ignore
+/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
+/// ```
+fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
+ const DOC_START: &str = " Lint: ";
+ if_chain! {
+ if doc_comment.starts_with(DOC_START);
+ if let Some(split_pos) = doc_comment.find('.');
+ then {
+ let mut doc_comment = doc_comment.to_string();
+ let mut documentation = doc_comment.split_off(split_pos);
+
+ // Extract lints
+ doc_comment.make_ascii_lowercase();
+ let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
+
+ // Format documentation correctly
+ // split off leading `.` from lint name list and indent for correct formatting
+ documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
+
+ Some((lints, documentation))
+ } else {
+ None
+ }
+ }
+}
+
+/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
+fn to_kebab(config_name: &str) -> String {
+ config_name.replace('_', "-")
+}
+
+impl fmt::Display for ClippyConfiguration {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ CONFIGURATION_VALUE_TEMPLATE!(),
+ name = self.name,
+ ty = self.config_type,
+ doc = self.doc,
+ default = self.default
+ )
+ }
+}
+
+// ==================================================================
+// Lint pass
+// ==================================================================
+impl<'hir> LateLintPass<'hir> for MetadataCollector {
+ /// Collecting lint declarations like:
+ /// ```rust, ignore
+ /// declare_clippy_lint! {
+ /// /// ### What it does
+ /// /// Something IDK.
+ /// pub SOME_LINT,
+ /// internal,
+ /// "Who am I?"
+ /// }
+ /// ```
+ fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
+ if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
+ // Normal lint
+ if_chain! {
+ // item validation
+ if is_lint_ref_type(cx, ty);
+ // blacklist check
+ let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
+ if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
+ // metadata extraction
+ if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
+ if let Some(mut docs) = extract_attr_docs_or_lint(cx, item);
+ then {
+ if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
+ docs.push_str(&configuration_section);
+ }
+
+ self.lints.push(LintMetadata::new(
+ lint_name,
+ SerializableSpan::from_item(cx, item),
+ group,
+ level,
+ docs,
+ ));
+ }
+ }
+
+ if_chain! {
+ if is_deprecated_lint(cx, ty);
+ // blacklist check
+ let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
+ if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
+ // Metadata the little we can get from a deprecated lint
+ if let Some(docs) = extract_attr_docs_or_lint(cx, item);
+ then {
+ self.lints.push(LintMetadata::new(
+ lint_name,
+ SerializableSpan::from_item(cx, item),
+ DEPRECATED_LINT_GROUP_STR.to_string(),
+ DEPRECATED_LINT_LEVEL,
+ docs,
+ ));
+ }
+ }
+ }
+ }
+
+ /// Collecting constant applicability from the actual lint emissions
+ ///
+ /// Example:
+ /// ```rust, ignore
+ /// span_lint_and_sugg(
+ /// cx,
+ /// SOME_LINT,
+ /// item.span,
+ /// "Le lint message",
+ /// "Here comes help:",
+ /// "#![allow(clippy::all)]",
+ /// Applicability::MachineApplicable, // <-- Extracts this constant value
+ /// );
+ /// ```
+ fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
+ if let Some(args) = match_lint_emission(cx, expr) {
+ let mut emission_info = extract_emission_info(cx, args);
+ if emission_info.is_empty() {
+ // See:
+ // - src/misc.rs:734:9
+ // - src/methods/mod.rs:3545:13
+ // - src/methods/mod.rs:3496:13
+ // We are basically unable to resolve the lint name itself.
+ return;
+ }
+
+ for (lint_name, applicability, is_multi_part) in emission_info.drain(..) {
+ let app_info = self.applicability_info.entry(lint_name).or_default();
+ app_info.applicability = applicability;
+ app_info.is_multi_part_suggestion = is_multi_part;
+ }
+ }
+ }
+}
+
+// ==================================================================
+// Lint definition extraction
+// ==================================================================
+fn sym_to_string(sym: Symbol) -> String {
+ sym.as_str().to_string()
+}
+
+fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
+ extract_attr_docs(cx, item).or_else(|| {
+ lint_collection_error_item(cx, item, "could not collect the lint documentation");
+ None
+ })
+}
+
+/// This function collects all documentation that has been added to an item using
+/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
+///
+/// ```ignore
+/// #[doc = r"Hello world!"]
+/// #[doc = r"=^.^="]
+/// struct SomeItem {}
+/// ```
+///
+/// Would result in `Hello world!\n=^.^=\n`
+///
+/// ---
+///
+/// This function may modify the doc comment to ensure that the string can be displayed using a
+/// markdown viewer in Clippy's lint list. The following modifications could be applied:
+/// * Removal of leading space after a new line. (Important to display tables)
+/// * Ensures that code blocks only contain language information
+fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
+ let mut docs = String::from(&*lines.next()?.as_str());
+ let mut in_code_block = false;
+ for line in lines {
+ docs.push('\n');
+ let line = line.as_str();
+ let line = &*line;
+ if let Some(info) = line.trim_start().strip_prefix("```") {
+ in_code_block = !in_code_block;
+ if in_code_block {
+ let lang = info
+ .trim()
+ .split(',')
+ // remove rustdoc directives
+ .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
+ // if no language is present, fill in "rust"
+ .unwrap_or("rust");
+ docs.push_str("```");
+ docs.push_str(lang);
+ continue;
+ }
+ }
+ // This removes the leading space that the macro translation introduces
+ if let Some(stripped_doc) = line.strip_prefix(' ') {
+ docs.push_str(stripped_doc);
+ } else if !line.is_empty() {
+ docs.push_str(line);
+ }
+ }
+ Some(docs)
+}
+
+fn get_lint_group_and_level_or_lint(
+ cx: &LateContext<'_>,
+ lint_name: &str,
+ item: &'hir Item<'_>,
+) -> Option<(String, &'static str)> {
+ let result = cx
+ .lint_store
+ .check_lint_name(cx.sess(), lint_name, Some(sym::clippy), &[]);
+ if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
+ if let Some(group) = get_lint_group(cx, lint_lst[0]) {
+ if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
+ return None;
+ }
+
+ if let Some(level) = get_lint_level_from_group(&group) {
+ Some((group, level))
+ } else {
+ lint_collection_error_item(
+ cx,
+ item,
+ &format!("Unable to determine lint level for found group `{}`", group),
+ );
+ None
+ }
+ } else {
+ lint_collection_error_item(cx, item, "Unable to determine lint group");
+ None
+ }
+ } else {
+ lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
+ None
+ }
+}
+
+fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
+ for (group_name, lints, _) in &cx.lint_store.get_lint_groups() {
+ if IGNORED_LINT_GROUPS.contains(group_name) {
+ continue;
+ }
+
+ if lints.iter().any(|group_lint| *group_lint == lint_id) {
+ let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
+ return Some((*group).to_string());
+ }
+ }
+
+ None
+}
+
+fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
+ DEFAULT_LINT_LEVELS
+ .iter()
+ .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
+}
+
+fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+ if let hir::TyKind::Path(ref path) = ty.kind {
+ if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
+ return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
+ }
+ }
+
+ false
+}
+
+// ==================================================================
+// Lint emission
+// ==================================================================
+fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
+ span_lint(
+ cx,
+ INTERNAL_METADATA_COLLECTOR,
+ item.ident.span,
+ &format!("metadata collection error for `{}`: {}", item.ident.name, message),
+ );
+}
+
+// ==================================================================
+// Applicability
+// ==================================================================
+/// This function checks if a given expression is equal to a simple lint emission function call.
+/// It will return the function arguments if the emission matched any function.
+fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
+ LINT_EMISSION_FUNCTIONS
+ .iter()
+ .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
+}
+
+fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
+ a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
+}
+
+fn extract_emission_info<'hir>(
+ cx: &LateContext<'hir>,
+ args: &'hir [hir::Expr<'hir>],
+) -> Vec<(String, Option<usize>, bool)> {
+ let mut lints = Vec::new();
+ let mut applicability = None;
+ let mut multi_part = false;
+
+ for arg in args {
+ let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
+
+ if match_type(cx, arg_ty, &paths::LINT) {
+ // If we found the lint arg, extract the lint name
+ let mut resolved_lints = resolve_lints(cx, arg);
+ lints.append(&mut resolved_lints);
+ } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
+ applicability = resolve_applicability(cx, arg);
+ } else if arg_ty.is_closure() {
+ multi_part |= check_is_multi_part(cx, arg);
+ // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
+ applicability = applicability.or_else(|| resolve_applicability(cx, arg));
+ }
+ }
+
+ lints
+ .drain(..)
+ .map(|lint_name| (lint_name, applicability, multi_part))
+ .collect()
+}
+
+/// Resolves the possible lints that this expression could reference
+fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
+ let mut resolver = LintResolver::new(cx);
+ resolver.visit_expr(expr);
+ resolver.lints
+}
+
+/// This function tries to resolve the linked applicability to the given expression.
+fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
+ let mut resolver = ApplicabilityResolver::new(cx);
+ resolver.visit_expr(expr);
+ resolver.complete()
+}
+
+fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
+ if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
+ let mut scanner = IsMultiSpanScanner::new(cx);
+ intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
+ return scanner.is_multi_part();
+ } else if let Some(local) = get_parent_local(cx, closure_expr) {
+ if let Some(local_init) = local.init {
+ return check_is_multi_part(cx, local_init);
+ }
+ }
+
+ false
+}
+
+struct LintResolver<'a, 'hir> {
+ cx: &'a LateContext<'hir>,
+ lints: Vec<String>,
+}
+
+impl<'a, 'hir> LintResolver<'a, 'hir> {
+ fn new(cx: &'a LateContext<'hir>) -> Self {
+ Self {
+ cx,
+ lints: Vec::<String>::default(),
+ }
+ }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
+ type Map = Map<'hir>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+ intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
+ }
+
+ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+ if_chain! {
+ if let ExprKind::Path(qpath) = &expr.kind;
+ if let QPath::Resolved(_, path) = qpath;
+
+ let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
+ if match_type(self.cx, expr_ty, &paths::LINT);
+ then {
+ if let hir::def::Res::Def(DefKind::Static, _) = path.res {
+ let lint_name = last_path_segment(qpath).ident.name;
+ self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
+ } else if let Some(local) = get_parent_local(self.cx, expr) {
+ if let Some(local_init) = local.init {
+ intravisit::walk_expr(self, local_init);
+ }
+ }
+ }
+ }
+
+ intravisit::walk_expr(self, expr);
+ }
+}
+
+/// This visitor finds the highest applicability value in the visited expressions
+struct ApplicabilityResolver<'a, 'hir> {
+ cx: &'a LateContext<'hir>,
+ /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
+ applicability_index: Option<usize>,
+}
+
+impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
+ fn new(cx: &'a LateContext<'hir>) -> Self {
+ Self {
+ cx,
+ applicability_index: None,
+ }
+ }
+
+ fn add_new_index(&mut self, new_index: usize) {
+ self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
+ }
+
+ fn complete(self) -> Option<usize> {
+ self.applicability_index
+ }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
+ type Map = Map<'hir>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+ intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
+ }
+
+ fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
+ for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
+ if match_path(path, enum_value) {
+ self.add_new_index(index);
+ return;
+ }
+ }
+ }
+
+ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+ let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
+
+ if_chain! {
+ if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
+ if let Some(local) = get_parent_local(self.cx, expr);
+ if let Some(local_init) = local.init;
+ then {
+ intravisit::walk_expr(self, local_init);
+ }
+ };
+
+ intravisit::walk_expr(self, expr);
+ }
+}
+
+/// This returns the parent local node if the expression is a reference one
+fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
+ if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
+ if let hir::def::Res::Local(local_hir) = path.res {
+ return get_parent_local_hir_id(cx, local_hir);
+ }
+ }
+
+ None
+}
+
+fn get_parent_local_hir_id(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
+ let map = cx.tcx.hir();
+
+ match map.find(map.get_parent_node(hir_id)) {
+ Some(hir::Node::Local(local)) => Some(local),
+ Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
+ _ => None,
+ }
+}
+
+/// This visitor finds the highest applicability value in the visited expressions
+struct IsMultiSpanScanner<'a, 'hir> {
+ cx: &'a LateContext<'hir>,
+ suggestion_count: usize,
+}
+
+impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
+ fn new(cx: &'a LateContext<'hir>) -> Self {
+ Self {
+ cx,
+ suggestion_count: 0,
+ }
+ }
+
+ /// Add a new single expression suggestion to the counter
+ fn add_single_span_suggestion(&mut self) {
+ self.suggestion_count += 1;
+ }
+
+ /// Signals that a suggestion with possible multiple spans was found
+ fn add_multi_part_suggestion(&mut self) {
+ self.suggestion_count += 2;
+ }
+
+ /// Checks if the suggestions include multiple spanns
+ fn is_multi_part(&self) -> bool {
+ self.suggestion_count > 1
+ }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
+ type Map = Map<'hir>;
+
+ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+ intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
+ }
+
+ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+ // Early return if the lint is already multi span
+ if self.is_multi_part() {
+ return;
+ }
+
+ match &expr.kind {
+ ExprKind::Call(fn_expr, _args) => {
+ let found_function = SUGGESTION_FUNCTIONS
+ .iter()
+ .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
+ if found_function {
+ // These functions are all multi part suggestions
+ self.add_single_span_suggestion();
+ }
+ },
+ ExprKind::MethodCall(path, _path_span, arg, _arg_span) => {
+ let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
+ if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
+ let called_method = path.ident.name.as_str().to_string();
+ for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
+ if *method_name == called_method {
+ if *is_multi_part {
+ self.add_multi_part_suggestion();
+ } else {
+ self.add_single_span_suggestion();
+ }
+ break;
+ }
+ }
+ }
+ },
+ _ => {},
+ }
+
+ intravisit::walk_expr(self, expr);
+ }
+}
--- /dev/null
- edition = "2018"
+[package]
+name = "clippy_utils"
+version = "0.1.57"
++edition = "2021"
+publish = false
+
+[dependencies]
+if_chain = "1.0"
+rustc-semver = "1.1"
+
+[features]
+deny-warnings = []
+internal-lints = []
+metadata-collector-lint = []
+
+[package.metadata.rust-analyzer]
+# This crate uses #[feature(rustc_private)]
+rustc_private = true
--- /dev/null
- (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
+//!
+//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
+
+#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
+
+use crate::{both, over};
+use if_chain::if_chain;
+use rustc_ast::ptr::P;
+use rustc_ast::{self as ast, *};
+use rustc_span::symbol::Ident;
+use std::mem;
+
+pub mod ident_iter;
+pub use ident_iter::IdentIter;
+
+pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
+ use BinOpKind::*;
+ matches!(
+ kind,
+ Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
+ )
+}
+
+/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
+pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+ left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
+}
+
+pub fn eq_id(l: Ident, r: Ident) -> bool {
+ l.name == r.name
+}
+
+pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
+ use PatKind::*;
+ match (&l.kind, &r.kind) {
+ (Paren(l), _) => eq_pat(l, r),
+ (_, Paren(r)) => eq_pat(l, r),
+ (Wild, Wild) | (Rest, Rest) => true,
+ (Lit(l), Lit(r)) => eq_expr(l, r),
+ (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
+ (Range(lf, lt, le), Range(rf, rt, re)) => {
+ eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
+ },
+ (Box(l), Box(r))
+ | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
+ | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
+ (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
- lr == rr
- && eq_maybe_qself(lqself, rqself)
- && eq_path(lp, rp)
- && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
++ (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
+ (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
+ eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
+ },
+ (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat)
+ },
+ (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
+ (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
+ match (l, r) {
+ (RangeEnd::Excluded, RangeEnd::Excluded) => true,
+ (RangeEnd::Included(l), RangeEnd::Included(r)) => {
+ matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
+ },
+ _ => false,
+ }
+}
+
+pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
+ l.is_placeholder == r.is_placeholder
+ && eq_id(l.ident, r.ident)
+ && eq_pat(&l.pat, &r.pat)
- over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
++ && over(&l.attrs, &r.attrs, eq_attr)
+}
+
+pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
+ l.position == r.position && eq_ty(&l.ty, &r.ty)
+}
+
+pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
+ match (l, r) {
+ (Some(l), Some(r)) => eq_qself(l, r),
+ (None, None) => true,
+ _ => false,
+ }
+}
+
+pub fn eq_path(l: &Path, r: &Path) -> bool {
- (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
- over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
- },
++ over(&l.segments, &r.segments, eq_path_seg)
+}
+
+pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
+ eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
+}
+
+pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
+ match (l, r) {
- if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
++ (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg),
+ (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
+ over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
+ },
+ _ => false,
+ }
+}
+
+pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
+ match (l, r) {
+ (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
+ (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
+ match (l, r) {
+ (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
+ (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
+ (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
+ _ => false,
+ }
+}
+
+pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
+ both(l, r, |l, r| eq_expr(l, r))
+}
+
+pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
+ match (l, r) {
+ (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb),
+ (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true,
+ _ => false,
+ }
+}
+
+pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
+ use ExprKind::*;
- (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
++ if !over(&l.attrs, &r.attrs, eq_attr) {
+ return false;
+ }
+ match (&l.kind, &r.kind) {
+ (Paren(l), _) => eq_expr(l, r),
+ (_, Paren(r)) => eq_expr(l, r),
+ (Err, Err) => true,
+ (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
+ (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
+ (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
+ (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
+ (MethodCall(lc, la, _), MethodCall(rc, ra, _)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
+ (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
+ (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
+ (Lit(l), Lit(r)) => l.kind == r.kind,
+ (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
+ (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
+ (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
+ (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
+ (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
+ eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
+ },
+ (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
+ (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
+ (TryBlock(l), TryBlock(r)) => eq_block(l, r),
+ (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
+ (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
+ (Continue(ll), Continue(rl)) => eq_label(ll, rl),
+ (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
+ (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
+ (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
- (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
++ (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
+ (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
+ lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
+ },
+ (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
+ (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
+ (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
- && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
++ (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
+ (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+ (Struct(lse), Struct(rse)) => {
+ eq_maybe_qself(&lse.qself, &rse.qself)
+ && eq_path(&lse.path, &rse.path)
+ && eq_struct_rest(&lse.rest, &rse.rest)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ && unordered_over(&lse.fields, &rse.fields, eq_field)
+ },
+ _ => false,
+ }
+}
+
+pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
+ l.is_placeholder == r.is_placeholder
+ && eq_id(l.ident, r.ident)
+ && eq_expr(&l.expr, &r.expr)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ && over(&l.attrs, &r.attrs, eq_attr)
+}
+
+pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
+ l.is_placeholder == r.is_placeholder
+ && eq_pat(&l.pat, &r.pat)
+ && eq_expr(&l.body, &r.body)
+ && eq_expr_opt(&l.guard, &r.guard)
- l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
++ && over(&l.attrs, &r.attrs, eq_attr)
+}
+
+pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
+ both(l, r, |l, r| eq_id(l.ident, r.ident))
+}
+
+pub fn eq_block(l: &Block, r: &Block) -> bool {
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt)
+}
+
+pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
+ use StmtKind::*;
+ match (&l.kind, &r.kind) {
+ (Local(l), Local(r)) => {
+ eq_pat(&l.pat, &r.pat)
+ && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
+ && eq_local_kind(&l.kind, &r.kind)
- l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ && over(&l.attrs, &r.attrs, eq_attr)
+ },
+ (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
+ (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
+ (Empty, Empty) => true,
+ (MacCall(l), MacCall(r)) => {
- eq_id(l.ident, r.ident)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
- && eq_vis(&l.vis, &r.vis)
- && eq_kind(&l.kind, &r.kind)
++ l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr)
+ },
+ _ => false,
+ }
+}
+
+pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
+ use LocalKind::*;
+ match (l, r) {
+ (Decl, Decl) => true,
+ (Init(l), Init(r)) => eq_expr(l, r),
+ (InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re),
+ _ => false,
+ }
+}
+
+pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
- both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
- && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
++ eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
+}
+
+pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
+ use ItemKind::*;
+ match (l, r) {
+ (ExternCrate(l), ExternCrate(r)) => l == r,
+ (Use(l), Use(r)) => eq_use_tree(l, r),
+ (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
+ (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+ (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
+ eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
+ },
+ (Mod(lu, lmk), Mod(ru, rmk)) => {
+ lu == ru
+ && match (lmk, rmk) {
+ (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
+ linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
+ },
+ (ModKind::Unloaded, ModKind::Unloaded) => true,
+ _ => false,
+ }
+ },
+ (ForeignMod(l), ForeignMod(r)) => {
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
++ both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
+ },
+ (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
+ eq_defaultness(*ld, *rd)
+ && eq_generics(lg, rg)
- (Enum(le, lg), Enum(re, rg)) => {
- over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
- },
++ && over(lb, rb, eq_generic_bound)
+ && both(lt, rt, |l, r| eq_ty(l, r))
+ },
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
++ (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg),
+ (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
+ eq_variant_data(lv, rv) && eq_generics(lg, rg)
+ },
+ (Trait(box TraitKind(la, lu, lg, lb, li)), Trait(box TraitKind(ra, ru, rg, rb, ri))) => {
+ la == ra
+ && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
+ && eq_generics(lg, rg)
- (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
++ && over(lb, rb, eq_generic_bound)
+ && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
+ },
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
++ (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound),
+ (
+ Impl(box ImplKind {
+ unsafety: lu,
+ polarity: lp,
+ defaultness: ld,
+ constness: lc,
+ generics: lg,
+ of_trait: lot,
+ self_ty: lst,
+ items: li,
+ }),
+ Impl(box ImplKind {
+ unsafety: ru,
+ polarity: rp,
+ defaultness: rd,
+ constness: rc,
+ generics: rg,
+ of_trait: rot,
+ self_ty: rst,
+ items: ri,
+ }),
+ ) => {
+ matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
+ && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
+ && eq_defaultness(*ld, *rd)
+ && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
+ && eq_generics(lg, rg)
+ && both(lot, rot, |l, r| eq_path(&l.path, &r.path))
+ && eq_ty(lst, rst)
+ && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
+ },
+ (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+ (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
+ _ => false,
+ }
+}
+
+pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
+ use ForeignItemKind::*;
+ match (l, r) {
+ (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
+ (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
+ eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
+ },
+ (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
+ eq_defaultness(*ld, *rd)
+ && eq_generics(lg, rg)
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
++ && over(lb, rb, eq_generic_bound)
+ && both(lt, rt, |l, r| eq_ty(l, r))
+ },
+ (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
+ use AssocItemKind::*;
+ match (l, r) {
+ (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+ (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
+ eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
+ },
+ (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
+ eq_defaultness(*ld, *rd)
+ && eq_generics(lg, rg)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ && over(lb, rb, eq_generic_bound)
+ && both(lt, rt, |l, r| eq_ty(l, r))
+ },
+ (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
+ l.is_placeholder == r.is_placeholder
- (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
++ && over(&l.attrs, &r.attrs, eq_attr)
+ && eq_vis(&l.vis, &r.vis)
+ && eq_id(l.ident, r.ident)
+ && eq_variant_data(&l.data, &r.data)
+ && both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
+}
+
+pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
+ use VariantData::*;
+ match (l, r) {
+ (Unit(_), Unit(_)) => true,
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, eq_struct_field),
+ _ => false,
+ }
+}
+
+pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
+ l.is_placeholder == r.is_placeholder
- over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
++ && over(&l.attrs, &r.attrs, eq_attr)
+ && eq_vis(&l.vis, &r.vis)
+ && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
+ && eq_ty(&l.ty, &r.ty)
+}
+
+pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
+ eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
+}
+
+pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
+ matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
+ && l.asyncness.is_async() == r.asyncness.is_async()
+ && matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
+ && eq_ext(&l.ext, &r.ext)
+}
+
+pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
- && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
++ over(&l.params, &r.params, eq_generic_param)
+ && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
+ eq_where_predicate(l, r)
+ })
+}
+
+pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
+ use WherePredicate::*;
+ match (l, r) {
+ (BoundPredicate(l), BoundPredicate(r)) => {
+ over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
+ eq_generic_param(l, r)
+ }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
- eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
++ && over(&l.bounds, &r.bounds, eq_generic_bound)
+ },
+ (RegionPredicate(l), RegionPredicate(r)) => {
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound)
+ },
+ (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
+ _ => false,
+ }
+}
+
+pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
+ eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
+}
+
+pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
+ eq_expr(&l.value, &r.value)
+}
+
+pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
+ use UseTreeKind::*;
+ match (l, r) {
+ (Glob, Glob) => true,
+ (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
+ (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
+ _ => false,
+ }
+}
+
+pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
+ matches!(
+ (l, r),
+ (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
+ )
+}
+
+pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
+ use VisibilityKind::*;
+ match (&l.kind, &r.kind) {
+ (Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true,
+ (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
+ eq_fn_ret_ty(&l.output, &r.output)
+ && over(&l.inputs, &r.inputs, |l, r| {
+ l.is_placeholder == r.is_placeholder
+ && eq_pat(&l.pat, &r.pat)
+ && eq_ty(&l.ty, &r.ty)
- && over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
++ && over(&l.attrs, &r.attrs, eq_attr)
+ })
+}
+
+pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
+ match (l, r) {
+ (FnRetTy::Default(_), FnRetTy::Default(_)) => true,
+ (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
+ use TyKind::*;
+ match (&l.kind, &r.kind) {
+ (Paren(l), _) => eq_ty(l, r),
+ (_, Paren(r)) => eq_ty(l, r),
+ (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
+ (Slice(l), Slice(r)) => eq_ty(l, r),
+ (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
+ (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
+ (Rptr(ll, l), Rptr(rl, r)) => {
+ both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
+ },
+ (BareFn(l), BareFn(r)) => {
+ l.unsafety == r.unsafety
+ && eq_ext(&l.ext, &r.ext)
- (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
- (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
- (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
++ && over(&l.generic_params, &r.generic_params, eq_generic_param)
+ && eq_fn_decl(&l.decl, &r.decl)
+ },
+ (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
- && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
++ (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
++ (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
++ (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
+ (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
+ (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
+ use Extern::*;
+ match (l, r) {
+ (None, None) | (Implicit, Implicit) => true,
+ (Explicit(l), Explicit(r)) => eq_str_lit(l, r),
+ _ => false,
+ }
+}
+
+pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
+ l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
+}
+
+pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
+ eq_path(&l.trait_ref.path, &r.trait_ref.path)
+ && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
+ eq_generic_param(l, r)
+ })
+}
+
+pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
+ use GenericParamKind::*;
+ l.is_placeholder == r.is_placeholder
+ && eq_id(l.ident, r.ident)
- ) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
++ && over(&l.bounds, &r.bounds, eq_generic_bound)
+ && match (&l.kind, &r.kind) {
+ (Lifetime, Lifetime) => true,
+ (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
+ (
+ Const {
+ ty: lt,
+ kw_span: _,
+ default: ld,
+ },
+ Const {
+ ty: rt,
+ kw_span: _,
+ default: rd,
+ },
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
++ ) => eq_ty(lt, rt) && both(ld, rd, eq_anon_const),
+ _ => false,
+ }
- (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
++ && over(&l.attrs, &r.attrs, eq_attr)
+}
+
+pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
+ use GenericBound::*;
+ match (l, r) {
+ (Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
+ (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
+ _ => false,
+ }
+}
+
+pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool {
+ use AssocTyConstraintKind::*;
+ eq_id(l.ident, r.ident)
+ && match (&l.kind, &r.kind) {
+ (Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
++ (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound),
+ _ => false,
+ }
+}
+
+pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
+ eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
+}
+
+pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
+ use AttrKind::*;
+ l.style == r.style
+ && match (&l.kind, &r.kind) {
+ (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
+ (Normal(l, _), Normal(r, _)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
+ _ => false,
+ }
+}
+
+pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
+ use MacArgs::*;
+ match (l, r) {
+ (Empty, Empty) => true,
+ (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
+ (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind,
+ _ => false,
+ }
+}
+
+/// Extract args from an assert-like macro.
+///
+/// Currently working with:
+/// - `assert_eq!` and `assert_ne!`
+/// - `debug_assert_eq!` and `debug_assert_ne!`
+///
+/// For example:
+///
+/// `debug_assert_eq!(a, b)` will return Some([a, b])
+pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
+ if_chain! {
+ if let ExprKind::If(_, ref block, _) = expr.kind;
+ if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
+ then {
+ expr = e;
+ }
+ }
+ if_chain! {
+ if let ExprKind::Block(ref block, _) = expr.kind;
+ if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
+ if let ExprKind::Match(ref match_expr, _) = expr.kind;
+ if let ExprKind::Tup(ref tup) = match_expr.kind;
+ if let [a, b, ..] = tup.as_slice();
+ if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
+ then {
+ return Some([&*a, &*b]);
+ }
+ }
+ None
+}
--- /dev/null
- ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
+//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
+//!
+//! Things to consider:
+//! - has the expression side-effects?
+//! - is the expression computationally expensive?
+//!
+//! See lints:
+//! - unnecessary-lazy-evaluations
+//! - or-fun-call
+//! - option-if-let-else
+
+use crate::is_ctor_or_promotable_const_function;
+use crate::ty::is_type_diagnostic_item;
+use rustc_hir::def::{DefKind, Res};
+
+use rustc_hir::intravisit;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+
+use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_span::sym;
+
+/// Is the expr pure (is it free from side-effects)?
+/// This function is named so to stress that it isn't exhaustive and returns FNs.
+fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
+ match expr.kind {
+ ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
+ ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
- fields.iter().all(|f| identify_some_pure_patterns(f.expr))
- && expr.map_or(true, |e| identify_some_pure_patterns(e))
++ ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(identify_some_pure_patterns),
+ ExprKind::Struct(_, fields, expr) => {
- ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
++ fields.iter().all(|f| identify_some_pure_patterns(f.expr)) && expr.map_or(true, identify_some_pure_patterns)
+ },
+ ExprKind::Call(
+ &Expr {
+ kind:
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
+ ..
+ },
+ )),
+ ..
+ },
+ args,
++ ) => args.iter().all(identify_some_pure_patterns),
+ ExprKind::Block(
+ &Block {
+ stmts,
+ expr: Some(expr),
+ ..
+ },
+ _,
+ ) => stmts.is_empty() && identify_some_pure_patterns(expr),
+ ExprKind::Box(..)
+ | ExprKind::Array(..)
+ | ExprKind::Call(..)
+ | ExprKind::MethodCall(..)
+ | ExprKind::Binary(..)
+ | ExprKind::Unary(..)
+ | ExprKind::Let(..)
+ | ExprKind::Cast(..)
+ | ExprKind::Type(..)
+ | ExprKind::DropTemps(..)
+ | ExprKind::Loop(..)
+ | ExprKind::If(..)
+ | ExprKind::Match(..)
+ | ExprKind::Closure(..)
+ | ExprKind::Block(..)
+ | ExprKind::Assign(..)
+ | ExprKind::AssignOp(..)
+ | ExprKind::Index(..)
+ | ExprKind::Break(..)
+ | ExprKind::Continue(..)
+ | ExprKind::Ret(..)
+ | ExprKind::InlineAsm(..)
+ | ExprKind::LlvmInlineAsm(..)
+ | ExprKind::Repeat(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Err => false,
+ }
+}
+
+/// Identify some potentially computationally expensive patterns.
+/// This function is named so to stress that its implementation is non-exhaustive.
+/// It returns FNs and FPs.
+fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ // Searches an expression for method calls or function calls that aren't ctors
+ struct FunCallFinder<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ found: bool,
+ }
+
+ impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ let call_found = match &expr.kind {
+ // ignore enum and struct constructors
+ ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
+ ExprKind::Index(obj, _) => {
+ let ty = self.cx.typeck_results().expr_ty(obj);
+ is_type_diagnostic_item(self.cx, ty, sym::hashmap_type)
+ || is_type_diagnostic_item(self.cx, ty, sym::BTreeMap)
+ },
+ ExprKind::MethodCall(..) => true,
+ _ => false,
+ };
+
+ if call_found {
+ self.found |= true;
+ }
+
+ if !self.found {
+ intravisit::walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+ }
+
+ let mut finder = FunCallFinder { cx, found: false };
+ finder.visit_expr(expr);
+ finder.found
+}
+
+pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
+}
+
+pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ identify_some_potentially_expensive_patterns(cx, expr)
+}
--- /dev/null
+//! This module contains functions that retrieves specifiec elements.
+
+#![deny(clippy::missing_docs_in_private_items)]
+
+use crate::{is_expn_of, match_def_path, paths};
+use if_chain::if_chain;
+use rustc_ast::ast::{self, LitKind};
+use rustc_hir as hir;
+use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp};
+use rustc_lint::LateContext;
+use rustc_span::{sym, ExpnKind, Span, Symbol};
+
+/// The essential nodes of a desugared for loop as well as the entire span:
+/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
+pub struct ForLoop<'tcx> {
+ /// `for` loop item
+ pub pat: &'tcx hir::Pat<'tcx>,
+ /// `IntoIterator` argument
+ pub arg: &'tcx hir::Expr<'tcx>,
+ /// `for` loop body
+ pub body: &'tcx hir::Expr<'tcx>,
+ /// entire `for` loop span
+ pub span: Span,
+}
+
+impl<'tcx> ForLoop<'tcx> {
+ #[inline]
+ /// Parses a desugared `for` loop
+ pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
+ if let Some(first_arm) = arms.get(0);
+ if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind;
+ if let Some(first_arg) = iterargs.get(0);
+ if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none();
+ if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind;
+ if block.expr.is_none();
+ if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
+ if let hir::StmtKind::Local(local) = let_stmt.kind;
+ if let hir::StmtKind::Expr(body_expr) = body.kind;
+ then {
+ return Some(Self {
+ pat: &*local.pat,
+ arg: first_arg,
+ body: body_expr,
+ span: first_arm.span
+ });
+ }
+ }
+ None
+ }
+}
+
+/// An `if` expression without `DropTemps`
+pub struct If<'hir> {
+ /// `if` condition
+ pub cond: &'hir Expr<'hir>,
+ /// `if` then expression
+ pub then: &'hir Expr<'hir>,
+ /// `else` expression
+ pub r#else: Option<&'hir Expr<'hir>>,
+}
+
+impl<'hir> If<'hir> {
+ #[inline]
+ /// Parses an `if` expression
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::If(
+ Expr {
+ kind: ExprKind::DropTemps(cond),
+ ..
+ },
+ then,
+ r#else,
+ ) = expr.kind
+ {
+ Some(Self { cond, then, r#else })
+ } else {
+ None
+ }
+ }
+}
+
+/// An `if let` expression
+pub struct IfLet<'hir> {
+ /// `if let` pattern
+ pub let_pat: &'hir Pat<'hir>,
+ /// `if let` scrutinee
+ pub let_expr: &'hir Expr<'hir>,
+ /// `if let` then expression
+ pub if_then: &'hir Expr<'hir>,
+ /// `if let` else expression
+ pub if_else: Option<&'hir Expr<'hir>>,
+}
+
+impl<'hir> IfLet<'hir> {
+ /// Parses an `if let` expression
+ pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::If(
+ Expr {
+ kind: ExprKind::Let(let_pat, let_expr, _),
+ ..
+ },
+ if_then,
+ if_else,
+ ) = expr.kind
+ {
+ let mut iter = cx.tcx.hir().parent_iter(expr.hir_id);
+ if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
+ if let Some((
+ _,
+ Node::Expr(Expr {
+ kind: ExprKind::Loop(_, _, LoopSource::While, _),
+ ..
+ }),
+ )) = iter.next()
+ {
+ // while loop desugar
+ return None;
+ }
+ }
+ return Some(Self {
+ let_pat,
+ let_expr,
+ if_then,
+ if_else,
+ });
+ }
+ None
+ }
+}
+
+/// An `if let` or `match` expression. Useful for lints that trigger on one or the other.
+pub enum IfLetOrMatch<'hir> {
+ /// Any `match` expression
+ Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
+ /// scrutinee, pattern, then block, else block
+ IfLet(
+ &'hir Expr<'hir>,
+ &'hir Pat<'hir>,
+ &'hir Expr<'hir>,
+ Option<&'hir Expr<'hir>>,
+ ),
+}
+
+impl<'hir> IfLetOrMatch<'hir> {
+ /// Parses an `if let` or `match` expression
+ pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
+ match expr.kind {
+ ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
+ _ => IfLet::hir(cx, expr).map(
+ |IfLet {
+ let_expr,
+ let_pat,
+ if_then,
+ if_else,
+ }| { Self::IfLet(let_expr, let_pat, if_then, if_else) },
+ ),
+ }
+ }
+}
+
+/// An `if` or `if let` expression
+pub struct IfOrIfLet<'hir> {
+ /// `if` condition that is maybe a `let` expression
+ pub cond: &'hir Expr<'hir>,
+ /// `if` then expression
+ pub then: &'hir Expr<'hir>,
+ /// `else` expression
+ pub r#else: Option<&'hir Expr<'hir>>,
+}
+
+impl<'hir> IfOrIfLet<'hir> {
+ #[inline]
+ /// Parses an `if` or `if let` expression
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::If(cond, then, r#else) = expr.kind {
+ if let ExprKind::DropTemps(new_cond) = cond.kind {
+ return Some(Self {
+ cond: new_cond,
+ r#else,
+ then,
+ });
+ }
+ if let ExprKind::Let(..) = cond.kind {
+ return Some(Self { cond, then, r#else });
+ }
+ }
+ None
+ }
+}
+
+/// Represent a range akin to `ast::ExprKind::Range`.
+#[derive(Debug, Copy, Clone)]
+pub struct Range<'a> {
+ /// The lower bound of the range, or `None` for ranges such as `..X`.
+ pub start: Option<&'a hir::Expr<'a>>,
+ /// The upper bound of the range, or `None` for ranges such as `X..`.
+ pub end: Option<&'a hir::Expr<'a>>,
+ /// Whether the interval is open or closed.
+ pub limits: ast::RangeLimits,
+}
+
+impl<'a> Range<'a> {
+ /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
+ pub fn hir(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
+ /// Finds the field named `name` in the field. Always return `Some` for
+ /// convenience.
+ fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
+ let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
+ Some(expr)
+ }
+
+ match expr.kind {
+ hir::ExprKind::Call(path, args)
+ if matches!(
+ path.kind,
+ hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
+ ) =>
+ {
+ Some(Range {
+ start: Some(&args[0]),
+ end: Some(&args[1]),
+ limits: ast::RangeLimits::Closed,
+ })
+ },
+ hir::ExprKind::Struct(path, fields, None) => match &path {
+ hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
+ start: None,
+ end: None,
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
+ start: Some(get_field("start", fields)?),
+ end: None,
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
+ start: Some(get_field("start", fields)?),
+ end: Some(get_field("end", fields)?),
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
+ start: None,
+ end: Some(get_field("end", fields)?),
+ limits: ast::RangeLimits::Closed,
+ }),
+ hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
+ start: None,
+ end: Some(get_field("end", fields)?),
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+}
+
+/// Represent the pre-expansion arguments of a `vec!` invocation.
+pub enum VecArgs<'a> {
+ /// `vec![elem; len]`
+ Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
+ /// `vec![a, b, c]`
+ Vec(&'a [hir::Expr<'a>]),
+}
+
+impl<'a> VecArgs<'a> {
+ /// Returns the arguments of the `vec!` macro if this expression was expanded
+ /// from `vec!`.
+ pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
+ if_chain! {
+ if let hir::ExprKind::Call(fun, args) = expr.kind;
+ if let hir::ExprKind::Path(ref qpath) = fun.kind;
+ if is_expn_of(fun.span, "vec").is_some();
+ if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ then {
+ return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
+ // `vec![elem; size]` case
+ Some(VecArgs::Repeat(&args[0], &args[1]))
+ }
+ else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
+ // `vec![a, b, c]` case
+ if_chain! {
+ if let hir::ExprKind::Box(boxed) = args[0].kind;
+ if let hir::ExprKind::Array(args) = boxed.kind;
+ then {
+ return Some(VecArgs::Vec(args));
+ }
+ }
+
+ None
+ }
+ else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
+ Some(VecArgs::Vec(&[]))
+ }
+ else {
+ None
+ };
+ }
+ }
+
+ None
+ }
+}
+
+/// A desugared `while` loop
+pub struct While<'hir> {
+ /// `while` loop condition
+ pub condition: &'hir Expr<'hir>,
+ /// `while` loop body
+ pub body: &'hir Expr<'hir>,
+}
+
+impl<'hir> While<'hir> {
+ #[inline]
+ /// Parses a desugared `while` loop
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::Loop(
+ Block {
+ expr:
+ Some(Expr {
+ kind:
+ ExprKind::If(
+ Expr {
+ kind: ExprKind::DropTemps(condition),
+ ..
+ },
+ body,
+ _,
+ ),
+ ..
+ }),
+ ..
+ },
+ _,
+ LoopSource::While,
+ _,
+ ) = expr.kind
+ {
+ return Some(Self { condition, body });
+ }
+ None
+ }
+}
+
+/// A desugared `while let` loop
+pub struct WhileLet<'hir> {
+ /// `while let` loop item pattern
+ pub let_pat: &'hir Pat<'hir>,
+ /// `while let` loop scrutinee
+ pub let_expr: &'hir Expr<'hir>,
+ /// `while let` loop body
+ pub if_then: &'hir Expr<'hir>,
+}
+
+impl<'hir> WhileLet<'hir> {
+ #[inline]
+ /// Parses a desugared `while let` loop
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::Loop(
+ Block {
+ expr:
+ Some(Expr {
+ kind:
+ ExprKind::If(
+ Expr {
+ kind: ExprKind::Let(let_pat, let_expr, _),
+ ..
+ },
+ if_then,
+ _,
+ ),
+ ..
+ }),
+ ..
+ },
+ _,
+ LoopSource::While,
+ _,
+ ) = expr.kind
+ {
+ return Some(Self {
+ let_pat,
+ let_expr,
+ if_then,
+ });
+ }
+ None
+ }
+}
+
+/// Converts a hir binary operator to the corresponding `ast` type.
+#[must_use]
+pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
+ match op {
+ hir::BinOpKind::Eq => ast::BinOpKind::Eq,
+ hir::BinOpKind::Ge => ast::BinOpKind::Ge,
+ hir::BinOpKind::Gt => ast::BinOpKind::Gt,
+ hir::BinOpKind::Le => ast::BinOpKind::Le,
+ hir::BinOpKind::Lt => ast::BinOpKind::Lt,
+ hir::BinOpKind::Ne => ast::BinOpKind::Ne,
+ hir::BinOpKind::Or => ast::BinOpKind::Or,
+ hir::BinOpKind::Add => ast::BinOpKind::Add,
+ hir::BinOpKind::And => ast::BinOpKind::And,
+ hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
+ hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
+ hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
+ hir::BinOpKind::Div => ast::BinOpKind::Div,
+ hir::BinOpKind::Mul => ast::BinOpKind::Mul,
+ hir::BinOpKind::Rem => ast::BinOpKind::Rem,
+ hir::BinOpKind::Shl => ast::BinOpKind::Shl,
+ hir::BinOpKind::Shr => ast::BinOpKind::Shr,
+ hir::BinOpKind::Sub => ast::BinOpKind::Sub,
+ }
+}
+
+/// Extract args from an assert-like macro.
+/// Currently working with:
+/// - `assert!`, `assert_eq!` and `assert_ne!`
+/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
+/// For example:
+/// `assert!(expr)` will return `Some([expr])`
+/// `debug_assert_eq!(a, b)` will return `Some([a, b])`
+pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
+ /// Try to match the AST for a pattern that contains a match, for example when two args are
+ /// compared
+ fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
+ if_chain! {
+ if let ExprKind::Match(headerexpr, _, _) = &matchblock_expr.kind;
+ if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
+ then {
+ return Some(vec![lhs, rhs]);
+ }
+ }
+ None
+ }
+
+ if let ExprKind::Block(block, _) = e.kind {
+ if block.stmts.len() == 1 {
+ if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind {
+ // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
+ if_chain! {
+ if let Some(If { cond, .. }) = If::hir(matchexpr);
+ if let ExprKind::Unary(UnOp::Not, condition) = cond.kind;
+ then {
+ return Some(vec![condition]);
+ }
+ }
+
+ // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
+ if_chain! {
+ if let ExprKind::Block(matchblock,_) = matchexpr.kind;
+ if let Some(matchblock_expr) = matchblock.expr;
+ then {
+ return ast_matchblock(matchblock_expr);
+ }
+ }
+ }
+ } else if let Some(matchblock_expr) = block.expr {
+ // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
+ return ast_matchblock(matchblock_expr);
+ }
+ }
+ None
+}
+
+/// A parsed `format!` expansion
+pub struct FormatExpn<'tcx> {
+ /// Span of `format!(..)`
+ pub call_site: Span,
+ /// Inner `format_args!` expansion
+ pub format_args: FormatArgsExpn<'tcx>,
+}
+
+impl FormatExpn<'tcx> {
+ /// Parses an expanded `format!` invocation
+ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let ExprKind::Block(block, _) = expr.kind;
+ if let [stmt] = block.stmts;
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(_, [format_args]) = init.kind;
+ let expn_data = expr.span.ctxt().outer_expn_data();
+ if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
+ if let Some(format_args) = FormatArgsExpn::parse(format_args);
+ then {
+ Some(FormatExpn {
+ call_site: expn_data.call_site,
+ format_args,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
+
+/// A parsed `format_args!` expansion
+pub struct FormatArgsExpn<'tcx> {
+ /// Span of the first argument, the format string
+ pub format_string_span: Span,
+ /// Values passed after the format string
+ pub value_args: Vec<&'tcx Expr<'tcx>>,
+
+ /// String literal expressions which represent the format string split by "{}"
+ pub format_string_parts: &'tcx [Expr<'tcx>],
+ /// Symbols corresponding to [`Self::format_string_parts`]
+ pub format_string_symbols: Vec<Symbol>,
+ /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
+ pub args: &'tcx [Expr<'tcx>],
+ /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
+ pub fmt_expr: Option<&'tcx Expr<'tcx>>,
+}
+
+impl FormatArgsExpn<'tcx> {
+ /// Parses an expanded `format_args!` or `format_args_nl!` invocation
+ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
+ let name = name.as_str();
+ if name.ends_with("format_args") || name.ends_with("format_args_nl");
+ if let ExprKind::Call(_, args) = expr.kind;
+ if let Some((strs_ref, args, fmt_expr)) = match args {
+ // Arguments::new_v1
+ [strs_ref, args] => Some((strs_ref, args, None)),
+ // Arguments::new_v1_formatted
+ [strs_ref, args, fmt_expr, _unsafe_arg] => Some((strs_ref, args, Some(fmt_expr))),
+ _ => None,
+ };
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
+ if let ExprKind::Array(format_string_parts) = strs_arr.kind;
+ if let Some(format_string_symbols) = format_string_parts
+ .iter()
+ .map(|e| {
+ if let ExprKind::Lit(lit) = &e.kind {
+ if let LitKind::Str(symbol, _style) = lit.node {
+ return Some(symbol);
+ }
+ }
+ None
+ })
+ .collect();
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
+ if let ExprKind::Match(args, [arm], _) = args.kind;
+ if let ExprKind::Tup(value_args) = args.kind;
+ if let Some(value_args) = value_args
+ .iter()
+ .map(|e| match e.kind {
+ ExprKind::AddrOf(_, _, e) => Some(e),
+ _ => None,
+ })
+ .collect();
+ if let ExprKind::Array(args) = arm.body.kind;
+ then {
+ Some(FormatArgsExpn {
+ format_string_span: strs_ref.span,
+ value_args,
+ format_string_parts,
+ format_string_symbols,
+ args,
+ fmt_expr,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
+
+/// Checks if a `let` statement is from a `for` loop desugaring.
+pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
+ // This will detect plain for-loops without an actual variable binding:
+ //
+ // ```
+ // for x in some_vec {
+ // // do stuff
+ // }
+ // ```
+ if_chain! {
+ if let Some(expr) = local.init;
+ if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
+ then {
+ return true;
+ }
+ }
+
+ // This detects a variable binding in for loop to avoid `let_unit_value`
+ // lint (see issue #1964).
+ //
+ // ```
+ // for _ in vec![()] {
+ // // anything
+ // }
+ // ```
+ if let hir::LocalSource::ForLoopDesugar = local.source {
+ return true;
+ }
+
+ false
+}
++
++/// A parsed `panic!` expansion
++pub struct PanicExpn<'tcx> {
++ /// Span of `panic!(..)`
++ pub call_site: Span,
++ /// Inner `format_args!` expansion
++ pub format_args: FormatArgsExpn<'tcx>,
++}
++
++impl PanicExpn<'tcx> {
++ /// Parses an expanded `panic!` invocation
++ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
++ if_chain! {
++ if let ExprKind::Block(block, _) = expr.kind;
++ if let Some(init) = block.expr;
++ if let ExprKind::Call(_, [format_args]) = init.kind;
++ let expn_data = expr.span.ctxt().outer_expn_data();
++ if let ExprKind::AddrOf(_, _, format_args) = format_args.kind;
++ if let Some(format_args) = FormatArgsExpn::parse(format_args);
++ then {
++ Some(PanicExpn {
++ call_site: expn_data.call_site,
++ format_args,
++ })
++ } else {
++ None
++ }
++ }
++ }
++}
--- /dev/null
- #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
+use crate::consts::{constant_context, constant_simple};
+use crate::differing_macro_contexts;
+use crate::source::snippet_opt;
+use rustc_ast::ast::InlineAsmTemplatePiece;
+use rustc_data_structures::fx::FxHasher;
+use rustc_hir::def::Res;
+use rustc_hir::HirIdMap;
+use rustc_hir::{
+ BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
+ InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
+ StmtKind, Ty, TyKind, TypeBinding,
+};
+use rustc_lexer::{tokenize, TokenKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::TypeckResults;
+use rustc_span::Symbol;
+use std::hash::{Hash, Hasher};
+
+/// Type used to check whether two ast are the same. This is different from the
+/// operator
+/// `==` on ast types as this operator would compare true equality with ID and
+/// span.
+///
+/// Note that some expressions kinds are not considered but could be added.
+pub struct SpanlessEq<'a, 'tcx> {
+ /// Context used to evaluate constant expressions.
+ cx: &'a LateContext<'tcx>,
+ maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+ allow_side_effects: bool,
+ expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
+}
+
+impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
+ pub fn new(cx: &'a LateContext<'tcx>) -> Self {
+ Self {
+ cx,
+ maybe_typeck_results: cx.maybe_typeck_results(),
+ allow_side_effects: true,
+ expr_fallback: None,
+ }
+ }
+
+ /// Consider expressions containing potential side effects as not equal.
+ pub fn deny_side_effects(self) -> Self {
+ Self {
+ allow_side_effects: false,
+ ..self
+ }
+ }
+
+ pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
+ Self {
+ expr_fallback: Some(Box::new(expr_fallback)),
+ ..self
+ }
+ }
+
+ /// Use this method to wrap comparisons that may involve inter-expression context.
+ /// See `self.locals`.
+ pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
+ HirEqInterExpr {
+ inner: self,
+ locals: HirIdMap::default(),
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
+ self.inter_expr().eq_block(left, right)
+ }
+
+ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+ self.inter_expr().eq_expr(left, right)
+ }
+
+ pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
+ self.inter_expr().eq_path_segment(left, right)
+ }
+
+ pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
+ self.inter_expr().eq_path_segments(left, right)
+ }
+}
+
+pub struct HirEqInterExpr<'a, 'b, 'tcx> {
+ inner: &'a mut SpanlessEq<'b, 'tcx>,
+
+ // When binding are declared, the binding ID in the left expression is mapped to the one on the
+ // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
+ // these blocks are considered equal since `x` is mapped to `y`.
+ locals: HirIdMap<HirId>,
+}
+
+impl HirEqInterExpr<'_, '_, '_> {
+ pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
+ match (&left.kind, &right.kind) {
+ (&StmtKind::Local(l), &StmtKind::Local(r)) => {
+ // This additional check ensures that the type of the locals are equivalent even if the init
+ // expression or type have some inferred parts.
+ if let Some(typeck) = self.inner.maybe_typeck_results {
+ let l_ty = typeck.pat_ty(l.pat);
+ let r_ty = typeck.pat_ty(r.pat);
+ if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) {
+ return false;
+ }
+ }
+
+ // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
+ // these only get added if the init and type is equal.
+ both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
+ && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
+ && self.eq_pat(l.pat, r.pat)
+ },
+ (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
+ _ => false,
+ }
+ }
+
+ /// Checks whether two blocks are the same.
+ fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
+ match (left.stmts, left.expr, right.stmts, right.expr) {
+ ([], None, [], None) => {
+ // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
+ // expanded to nothing, or the cfg attribute was used.
+ let (left, right) = match (
+ snippet_opt(self.inner.cx, left.span),
+ snippet_opt(self.inner.cx, right.span),
+ ) {
+ (Some(left), Some(right)) => (left, right),
+ _ => return true,
+ };
+ let mut left_pos = 0;
+ let left = tokenize(&left)
+ .map(|t| {
+ let end = left_pos + t.len;
+ let s = &left[left_pos..end];
+ left_pos = end;
+ (t, s)
+ })
+ .filter(|(t, _)| {
+ !matches!(
+ t.kind,
+ TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
+ )
+ })
+ .map(|(_, s)| s);
+ let mut right_pos = 0;
+ let right = tokenize(&right)
+ .map(|t| {
+ let end = right_pos + t.len;
+ let s = &right[right_pos..end];
+ right_pos = end;
+ (t, s)
+ })
+ .filter(|(t, _)| {
+ !matches!(
+ t.kind,
+ TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
+ )
+ })
+ .map(|(_, s)| s);
+ left.eq(right)
+ },
+ _ => {
+ over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r))
+ && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
+ },
+ }
+ }
+
+ pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
+ let cx = self.inner.cx;
+ let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
+ eval_const(left) == eval_const(right)
+ }
+
+ #[allow(clippy::similar_names)]
+ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+ if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) {
+ return false;
+ }
+
+ if let Some(typeck_results) = self.inner.maybe_typeck_results {
+ if let (Some(l), Some(r)) = (
+ constant_simple(self.inner.cx, typeck_results, left),
+ constant_simple(self.inner.cx, typeck_results, right),
+ ) {
+ if l == r {
+ return true;
+ }
+ }
+ }
+
+ let is_eq = match (
+ &reduce_exprkind(self.inner.cx, &left.kind),
+ &reduce_exprkind(self.inner.cx, &right.kind),
+ ) {
+ (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => {
+ lb == rb && l_mut == r_mut && self.eq_expr(le, re)
+ },
+ (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
+ both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
+ },
+ (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => {
+ self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+ },
+ (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => {
+ self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+ },
+ (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
+ (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
+ l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+ || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
+ l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+ })
+ },
+ (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
+ both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
+ && both(le, re, |l, r| self.eq_expr(l, r))
+ },
+ (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r),
+ (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
+ self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
+ },
+ (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => {
+ self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
+ },
+ (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => {
+ l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
+ },
+ (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
+ (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
+ self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
+ },
+ (&ExprKind::Let(lp, le, _), &ExprKind::Let(rp, re, _)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
+ (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
+ (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
+ lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
+ },
+ (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
+ ls == rs
+ && self.eq_expr(le, re)
+ && over(la, ra, |l, r| {
+ self.eq_pat(l.pat, r.pat)
+ && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
+ && self.eq_expr(l.body, r.body)
+ })
+ },
+ (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => {
+ self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
+ },
+ (&ExprKind::Repeat(le, ref ll_id), &ExprKind::Repeat(re, ref rl_id)) => {
+ self.eq_expr(le, re) && self.eq_body(ll_id.body, rl_id.body)
+ },
+ (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
+ (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
+ (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
+ self.eq_qpath(l_path, r_path)
+ && both(lo, ro, |l, r| self.eq_expr(l, r))
+ && over(lf, rf, |l, r| self.eq_expr_field(l, r))
+ },
+ (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
+ (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
+ (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
+ (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
+ _ => false,
+ };
+ is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
+ }
+
+ fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
+ over(left, right, |l, r| self.eq_expr(l, r))
+ }
+
+ fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
+ left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
+ }
+
+ fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
+ match (left, right) {
+ (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
+ (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
+ _ => false,
+ }
+ }
+
+ fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
+ match (left, right) {
+ (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
+ (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
+ (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
+ (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
+ _ => false,
+ }
+ }
+
+ fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
+ left.name == right.name
+ }
+
+ fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
+ let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
+ li.name == ri.name && self.eq_pat(lp, rp)
+ }
+
+ /// Checks whether two patterns are the same.
+ fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
+ match (&left.kind, &right.kind) {
+ (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
+ (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
+ self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
+ },
+ (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
+ self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
+ },
+ (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
+ let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
+ if eq {
+ self.locals.insert(li, ri);
+ }
+ eq
+ },
+ (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
+ (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
+ (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
+ (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
+ both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
+ },
+ (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
+ (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
+ over(ls, rs, |l, r| self.eq_pat(l, r))
+ && over(le, re, |l, r| self.eq_pat(l, r))
+ && both(li, ri, |l, r| self.eq_pat(l, r))
+ },
+ (&PatKind::Wild, &PatKind::Wild) => true,
+ _ => false,
+ }
+ }
+
+ #[allow(clippy::similar_names)]
+ fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
+ match (left, right) {
+ (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
+ both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
+ },
+ (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
+ self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
+ },
+ (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item,
+ _ => false,
+ }
+ }
+
+ fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
+ match (left.res, right.res) {
+ (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
+ (Res::Local(_), _) | (_, Res::Local(_)) => false,
+ _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
+ }
+ }
+
+ fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
+ if !(left.parenthesized || right.parenthesized) {
+ over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
+ && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r))
+ } else if left.parenthesized && right.parenthesized {
+ over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
+ && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
+ self.eq_ty(l, r)
+ })
+ } else {
+ false
+ }
+ }
+
+ pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
+ left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
+ }
+
+ pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
+ // The == of idents doesn't work with different contexts,
+ // we have to be explicit about hygiene
+ left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
+ }
+
+ #[allow(clippy::similar_names)]
+ fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
+ match (&left.kind, &right.kind) {
+ (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
+ (&TyKind::Array(lt, ref ll_id), &TyKind::Array(rt, ref rl_id)) => {
+ self.eq_ty(lt, rt) && self.eq_body(ll_id.body, rl_id.body)
+ },
+ (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
+ l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
+ },
+ (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
+ l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
+ },
+ (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
+ (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
+ (&TyKind::Infer, &TyKind::Infer) => true,
+ _ => false,
+ }
+ }
+
+ fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
+ left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
+ }
+}
+
+/// Some simple reductions like `{ return }` => `return`
+fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
+ if let ExprKind::Block(block, _) = kind {
+ match (block.stmts, block.expr) {
+ // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
+ // block with an empty span.
+ ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
+ // `{}` => `()`
+ ([], None) => match snippet_opt(cx, block.span) {
+ // Don't reduce if there are any tokens contained in the braces
+ Some(snip)
+ if tokenize(&snip)
+ .map(|t| t.kind)
+ .filter(|t| {
+ !matches!(
+ t,
+ TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
+ )
+ })
+ .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
+ {
+ kind
+ },
+ _ => &ExprKind::Tup(&[]),
+ },
+ ([], Some(expr)) => match expr.kind {
+ // `{ return .. }` => `return ..`
+ ExprKind::Ret(..) => &expr.kind,
+ _ => kind,
+ },
+ ([stmt], None) => match stmt.kind {
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
+ // `{ return ..; }` => `return ..`
+ ExprKind::Ret(..) => &expr.kind,
+ _ => kind,
+ },
+ _ => kind,
+ },
+ _ => kind,
+ }
+ } else {
+ kind
+ }
+}
+
+fn swap_binop<'a>(
+ binop: BinOpKind,
+ lhs: &'a Expr<'a>,
+ rhs: &'a Expr<'a>,
+) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
+ match binop {
+ BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
+ Some((binop, rhs, lhs))
+ },
+ BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
+ BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
+ BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
+ BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
+ BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
+ | BinOpKind::Shl
+ | BinOpKind::Shr
+ | BinOpKind::Rem
+ | BinOpKind::Sub
+ | BinOpKind::Div
+ | BinOpKind::And
+ | BinOpKind::Or => None,
+ }
+}
+
+/// Checks if the two `Option`s are both `None` or some equal values as per
+/// `eq_fn`.
+pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+ l.as_ref()
+ .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
+}
+
+/// Checks if two slices are equal as per `eq_fn`.
+pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+ left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
+}
+
+/// Counts how many elements of the slices are equal as per `eq_fn`.
+pub fn count_eq<X: Sized>(
+ left: &mut dyn Iterator<Item = X>,
+ right: &mut dyn Iterator<Item = X>,
+ mut eq_fn: impl FnMut(&X, &X) -> bool,
+) -> usize {
+ left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
+}
+
+/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
+pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+ SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
+}
+
+/// Type used to hash an ast element. This is different from the `Hash` trait
+/// on ast types as this
+/// trait would consider IDs and spans.
+///
+/// All expressions kind are hashed, but some might have a weaker hash.
+pub struct SpanlessHash<'a, 'tcx> {
+ /// Context used to evaluate constant expressions.
+ cx: &'a LateContext<'tcx>,
+ maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+ s: FxHasher,
+}
+
+impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
+ pub fn new(cx: &'a LateContext<'tcx>) -> Self {
+ Self {
+ cx,
+ maybe_typeck_results: cx.maybe_typeck_results(),
+ s: FxHasher::default(),
+ }
+ }
+
+ pub fn finish(self) -> u64 {
+ self.s.finish()
+ }
+
+ pub fn hash_block(&mut self, b: &Block<'_>) {
+ for s in b.stmts {
+ self.hash_stmt(s);
+ }
+
+ if let Some(e) = b.expr {
+ self.hash_expr(e);
+ }
+
+ std::mem::discriminant(&b.rules).hash(&mut self.s);
+ }
+
++ #[allow(clippy::too_many_lines)]
+ pub fn hash_expr(&mut self, e: &Expr<'_>) {
+ let simple_const = self
+ .maybe_typeck_results
+ .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
+
+ // const hashing may result in the same hash as some unrelated node, so add a sort of
+ // discriminant depending on which path we're choosing next
+ simple_const.hash(&mut self.s);
+ if simple_const.is_some() {
+ return;
+ }
+
+ std::mem::discriminant(&e.kind).hash(&mut self.s);
+
+ match e.kind {
+ ExprKind::AddrOf(kind, m, e) => {
+ std::mem::discriminant(&kind).hash(&mut self.s);
+ m.hash(&mut self.s);
+ self.hash_expr(e);
+ },
+ ExprKind::Continue(i) => {
+ if let Some(i) = i.label {
+ self.hash_name(i.ident.name);
+ }
+ },
+ ExprKind::Assign(l, r, _) => {
+ self.hash_expr(l);
+ self.hash_expr(r);
+ },
+ ExprKind::AssignOp(ref o, l, r) => {
+ std::mem::discriminant(&o.node).hash(&mut self.s);
+ self.hash_expr(l);
+ self.hash_expr(r);
+ },
+ ExprKind::Block(b, _) => {
+ self.hash_block(b);
+ },
+ ExprKind::Binary(op, l, r) => {
+ std::mem::discriminant(&op.node).hash(&mut self.s);
+ self.hash_expr(l);
+ self.hash_expr(r);
+ },
+ ExprKind::Break(i, ref j) => {
+ if let Some(i) = i.label {
+ self.hash_name(i.ident.name);
+ }
+ if let Some(j) = *j {
+ self.hash_expr(&*j);
+ }
+ },
+ ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
+ self.hash_expr(e);
+ },
+ ExprKind::Call(fun, args) => {
+ self.hash_expr(fun);
+ self.hash_exprs(args);
+ },
+ ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
+ self.hash_expr(e);
+ self.hash_ty(ty);
+ },
+ ExprKind::Closure(cap, _, eid, _, _) => {
+ std::mem::discriminant(&cap).hash(&mut self.s);
+ // closures inherit TypeckResults
+ self.hash_expr(&self.cx.tcx.hir().body(eid).value);
+ },
+ ExprKind::Field(e, ref f) => {
+ self.hash_expr(e);
+ self.hash_name(f.name);
+ },
+ ExprKind::Index(a, i) => {
+ self.hash_expr(a);
+ self.hash_expr(i);
+ },
+ ExprKind::InlineAsm(asm) => {
+ for piece in asm.template {
+ match piece {
+ InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
+ InlineAsmTemplatePiece::Placeholder {
+ operand_idx,
+ modifier,
+ span: _,
+ } => {
+ operand_idx.hash(&mut self.s);
+ modifier.hash(&mut self.s);
+ },
+ }
+ }
+ asm.options.hash(&mut self.s);
+ for (op, _op_sp) in asm.operands {
+ match op {
+ InlineAsmOperand::In { reg, expr } => {
+ reg.hash(&mut self.s);
+ self.hash_expr(expr);
+ },
+ InlineAsmOperand::Out { reg, late, expr } => {
+ reg.hash(&mut self.s);
+ late.hash(&mut self.s);
+ if let Some(expr) = expr {
+ self.hash_expr(expr);
+ }
+ },
+ InlineAsmOperand::InOut { reg, late, expr } => {
+ reg.hash(&mut self.s);
+ late.hash(&mut self.s);
+ self.hash_expr(expr);
+ },
+ InlineAsmOperand::SplitInOut {
+ reg,
+ late,
+ in_expr,
+ out_expr,
+ } => {
+ reg.hash(&mut self.s);
+ late.hash(&mut self.s);
+ self.hash_expr(in_expr);
+ if let Some(out_expr) = out_expr {
+ self.hash_expr(out_expr);
+ }
+ },
+ InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body),
+ InlineAsmOperand::Sym { expr } => self.hash_expr(expr),
+ }
+ }
+ },
+ ExprKind::Let(pat, expr, _) => {
+ self.hash_expr(expr);
+ self.hash_pat(pat);
+ },
+ ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {},
+ ExprKind::Lit(ref l) => {
+ l.node.hash(&mut self.s);
+ },
+ ExprKind::Loop(b, ref i, ..) => {
+ self.hash_block(b);
+ if let Some(i) = *i {
+ self.hash_name(i.ident.name);
+ }
+ },
+ ExprKind::If(cond, then, ref else_opt) => {
+ self.hash_expr(cond);
+ self.hash_expr(then);
+ if let Some(e) = *else_opt {
+ self.hash_expr(e);
+ }
+ },
+ ExprKind::Match(e, arms, ref s) => {
+ self.hash_expr(e);
+
+ for arm in arms {
+ self.hash_pat(arm.pat);
+ if let Some(ref e) = arm.guard {
+ self.hash_guard(e);
+ }
+ self.hash_expr(arm.body);
+ }
+
+ s.hash(&mut self.s);
+ },
+ ExprKind::MethodCall(path, ref _tys, args, ref _fn_span) => {
+ self.hash_name(path.ident.name);
+ self.hash_exprs(args);
+ },
+ ExprKind::ConstBlock(ref l_id) => {
+ self.hash_body(l_id.body);
+ },
+ ExprKind::Repeat(e, ref l_id) => {
+ self.hash_expr(e);
+ self.hash_body(l_id.body);
+ },
+ ExprKind::Ret(ref e) => {
+ if let Some(e) = *e {
+ self.hash_expr(e);
+ }
+ },
+ ExprKind::Path(ref qpath) => {
+ self.hash_qpath(qpath);
+ },
+ ExprKind::Struct(path, fields, ref expr) => {
+ self.hash_qpath(path);
+
+ for f in fields {
+ self.hash_name(f.ident.name);
+ self.hash_expr(f.expr);
+ }
+
+ if let Some(e) = *expr {
+ self.hash_expr(e);
+ }
+ },
+ ExprKind::Tup(tup) => {
+ self.hash_exprs(tup);
+ },
+ ExprKind::Array(v) => {
+ self.hash_exprs(v);
+ },
+ ExprKind::Unary(lop, le) => {
+ std::mem::discriminant(&lop).hash(&mut self.s);
+ self.hash_expr(le);
+ },
+ }
+ }
+
+ pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
+ for e in e {
+ self.hash_expr(e);
+ }
+ }
+
+ pub fn hash_name(&mut self, n: Symbol) {
+ n.hash(&mut self.s);
+ }
+
+ pub fn hash_qpath(&mut self, p: &QPath<'_>) {
+ match *p {
+ QPath::Resolved(_, path) => {
+ self.hash_path(path);
+ },
+ QPath::TypeRelative(_, path) => {
+ self.hash_name(path.ident.name);
+ },
+ QPath::LangItem(lang_item, ..) => {
+ std::mem::discriminant(&lang_item).hash(&mut self.s);
+ },
+ }
+ // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
+ }
+
+ pub fn hash_pat(&mut self, pat: &Pat<'_>) {
+ std::mem::discriminant(&pat.kind).hash(&mut self.s);
+ match pat.kind {
+ PatKind::Binding(ann, _, _, pat) => {
+ std::mem::discriminant(&ann).hash(&mut self.s);
+ if let Some(pat) = pat {
+ self.hash_pat(pat);
+ }
+ },
+ PatKind::Box(pat) => self.hash_pat(pat),
+ PatKind::Lit(expr) => self.hash_expr(expr),
+ PatKind::Or(pats) => {
+ for pat in pats {
+ self.hash_pat(pat);
+ }
+ },
+ PatKind::Path(ref qpath) => self.hash_qpath(qpath),
+ PatKind::Range(s, e, i) => {
+ if let Some(s) = s {
+ self.hash_expr(s);
+ }
+ if let Some(e) = e {
+ self.hash_expr(e);
+ }
+ std::mem::discriminant(&i).hash(&mut self.s);
+ },
+ PatKind::Ref(pat, mu) => {
+ self.hash_pat(pat);
+ std::mem::discriminant(&mu).hash(&mut self.s);
+ },
+ PatKind::Slice(l, m, r) => {
+ for pat in l {
+ self.hash_pat(pat);
+ }
+ if let Some(pat) = m {
+ self.hash_pat(pat);
+ }
+ for pat in r {
+ self.hash_pat(pat);
+ }
+ },
+ PatKind::Struct(ref qpath, fields, e) => {
+ self.hash_qpath(qpath);
+ for f in fields {
+ self.hash_name(f.ident.name);
+ self.hash_pat(f.pat);
+ }
+ e.hash(&mut self.s);
+ },
+ PatKind::Tuple(pats, e) => {
+ for pat in pats {
+ self.hash_pat(pat);
+ }
+ e.hash(&mut self.s);
+ },
+ PatKind::TupleStruct(ref qpath, pats, e) => {
+ self.hash_qpath(qpath);
+ for pat in pats {
+ self.hash_pat(pat);
+ }
+ e.hash(&mut self.s);
+ },
+ PatKind::Wild => {},
+ }
+ }
+
+ pub fn hash_path(&mut self, path: &Path<'_>) {
+ match path.res {
+ // constant hash since equality is dependant on inter-expression context
+ Res::Local(_) => 1_usize.hash(&mut self.s),
+ _ => {
+ for seg in path.segments {
+ self.hash_name(seg.ident.name);
+ self.hash_generic_args(seg.args().args);
+ }
+ },
+ }
+ }
+
+ pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
+ std::mem::discriminant(&b.kind).hash(&mut self.s);
+
+ match &b.kind {
+ StmtKind::Local(local) => {
+ self.hash_pat(local.pat);
+ if let Some(init) = local.init {
+ self.hash_expr(init);
+ }
+ },
+ StmtKind::Item(..) => {},
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
+ self.hash_expr(expr);
+ },
+ }
+ }
+
+ pub fn hash_guard(&mut self, g: &Guard<'_>) {
+ match g {
+ Guard::If(expr) | Guard::IfLet(_, expr) => {
+ self.hash_expr(expr);
+ },
+ }
+ }
+
+ pub fn hash_lifetime(&mut self, lifetime: Lifetime) {
+ std::mem::discriminant(&lifetime.name).hash(&mut self.s);
+ if let LifetimeName::Param(ref name) = lifetime.name {
+ std::mem::discriminant(name).hash(&mut self.s);
+ match name {
+ ParamName::Plain(ref ident) => {
+ ident.name.hash(&mut self.s);
+ },
+ ParamName::Fresh(ref size) => {
+ size.hash(&mut self.s);
+ },
+ ParamName::Error => {},
+ }
+ }
+ }
+
+ pub fn hash_ty(&mut self, ty: &Ty<'_>) {
+ std::mem::discriminant(&ty.kind).hash(&mut self.s);
+ self.hash_tykind(&ty.kind);
+ }
+
+ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
+ match ty {
+ TyKind::Slice(ty) => {
+ self.hash_ty(ty);
+ },
+ TyKind::Array(ty, anon_const) => {
+ self.hash_ty(ty);
+ self.hash_body(anon_const.body);
+ },
+ TyKind::Ptr(ref mut_ty) => {
+ self.hash_ty(mut_ty.ty);
+ mut_ty.mutbl.hash(&mut self.s);
+ },
+ TyKind::Rptr(lifetime, ref mut_ty) => {
+ self.hash_lifetime(*lifetime);
+ self.hash_ty(mut_ty.ty);
+ mut_ty.mutbl.hash(&mut self.s);
+ },
+ TyKind::BareFn(bfn) => {
+ bfn.unsafety.hash(&mut self.s);
+ bfn.abi.hash(&mut self.s);
+ for arg in bfn.decl.inputs {
+ self.hash_ty(arg);
+ }
+ std::mem::discriminant(&bfn.decl.output).hash(&mut self.s);
+ match bfn.decl.output {
+ FnRetTy::DefaultReturn(_) => {},
+ FnRetTy::Return(ty) => {
+ self.hash_ty(ty);
+ },
+ }
+ bfn.decl.c_variadic.hash(&mut self.s);
+ },
+ TyKind::Tup(ty_list) => {
+ for ty in *ty_list {
+ self.hash_ty(ty);
+ }
+ },
+ TyKind::Path(ref qpath) => self.hash_qpath(qpath),
+ TyKind::OpaqueDef(_, arg_list) => {
+ self.hash_generic_args(arg_list);
+ },
+ TyKind::TraitObject(_, lifetime, _) => {
+ self.hash_lifetime(*lifetime);
+ },
+ TyKind::Typeof(anon_const) => {
+ self.hash_body(anon_const.body);
+ },
+ TyKind::Err | TyKind::Infer | TyKind::Never => {},
+ }
+ }
+
+ pub fn hash_body(&mut self, body_id: BodyId) {
+ // swap out TypeckResults when hashing a body
+ let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
+ self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
+ self.maybe_typeck_results = old_maybe_typeck_results;
+ }
+
+ fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
+ for arg in arg_list {
+ match *arg {
+ GenericArg::Lifetime(l) => self.hash_lifetime(l),
+ GenericArg::Type(ref ty) => self.hash_ty(ty),
+ GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
+ GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
+ }
+ }
+ }
+}
--- /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_type` 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"];
+pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
+pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
+pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
+pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
+pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
+pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
+/// Preferably use the diagnostic item `sym::result_type` 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
- Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => Ok(()),
+// This code used to be a part of `rustc` but moved to Clippy as a result of
+// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
+// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
+// differ from the time of `rustc` even if the name stays the same.
+
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::mir::{
+ Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
+ TerminatorKind,
+};
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_semver::RustcVersion;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+use rustc_target::spec::abi::Abi::RustIntrinsic;
+use std::borrow::Cow;
+
+type McfResult = Result<(), (Span, Cow<'static, str>)>;
+
+pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
+ let def_id = body.source.def_id();
+ let mut current = def_id;
+ loop {
+ let predicates = tcx.predicates_of(current);
+ for (predicate, _) in predicates.predicates {
+ match predicate.kind().skip_binder() {
+ ty::PredicateKind::RegionOutlives(_)
+ | ty::PredicateKind::TypeOutlives(_)
+ | ty::PredicateKind::WellFormed(_)
+ | ty::PredicateKind::Projection(_)
+ | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::ConstEquate(..)
+ | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
+ ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
+ ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
+ ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
+ ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate),
+ ty::PredicateKind::Trait(pred) => {
+ if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
+ continue;
+ }
+ match pred.self_ty().kind() {
+ ty::Param(ref p) => {
+ let generics = tcx.generics_of(current);
+ let def = generics.type_param(p, tcx);
+ let span = tcx.def_span(def.def_id);
+ return Err((
+ span,
+ "trait bounds other than `Sized` \
+ on const fn parameters are unstable"
+ .into(),
+ ));
+ },
+ // other kinds of bounds are either tautologies
+ // or cause errors in other passes
+ _ => continue,
+ }
+ },
+ }
+ }
+ match predicates.parent {
+ Some(parent) => current = parent,
+ None => break,
+ }
+ }
+
+ for local in &body.local_decls {
+ check_ty(tcx, local.ty, local.source_info.span)?;
+ }
+ // impl trait is gone in MIR, so check the return type manually
+ check_ty(
+ tcx,
+ tcx.fn_sig(def_id).output().skip_binder(),
+ body.local_decls.iter().next().unwrap().source_info.span,
+ )?;
+
+ for bb in body.basic_blocks() {
+ check_terminator(tcx, body, bb.terminator(), msrv)?;
+ for stmt in &bb.statements {
+ check_statement(tcx, body, def_id, stmt)?;
+ }
+ }
+ Ok(())
+}
+
+fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
+ for arg in ty.walk(tcx) {
+ let ty = match arg.unpack() {
+ GenericArgKind::Type(ty) => ty,
+
+ // No constraints on lifetimes or constants, except potentially
+ // constants' types, but `walk` will get to them as well.
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
+ };
+
+ match ty.kind() {
+ ty::Ref(_, _, hir::Mutability::Mut) => {
+ return Err((span, "mutable references in const fn are unstable".into()));
+ },
+ ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
+ ty::FnPtr(..) => {
+ return Err((span, "function pointers in const fn are unstable".into()));
+ },
+ ty::Dynamic(preds, _) => {
+ for pred in preds.iter() {
+ match pred.skip_binder() {
+ ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
+ return Err((
+ span,
+ "trait bounds other than `Sized` \
+ on const fn parameters are unstable"
+ .into(),
+ ));
+ },
+ ty::ExistentialPredicate::Trait(trait_ref) => {
+ if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
+ return Err((
+ span,
+ "trait bounds other than `Sized` \
+ on const fn parameters are unstable"
+ .into(),
+ ));
+ }
+ },
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+ Ok(())
+}
+
+fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
+ match rvalue {
+ Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
+ Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
+ Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
+ check_place(tcx, *place, span, body)
+ },
+ Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
+ use rustc_middle::ty::cast::CastTy;
+ let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
+ let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
+ match (cast_in, cast_out) {
+ (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
+ Err((span, "casting pointers to ints is unstable in const fn".into()))
+ },
+ _ => check_operand(tcx, operand, span, body),
+ }
+ },
+ Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
+ check_operand(tcx, operand, span, body)
+ },
+ Rvalue::Cast(
+ CastKind::Pointer(
+ PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
+ ),
+ _,
+ _,
+ ) => Err((span, "function pointer casts are not allowed in const fn".into())),
+ Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
+ let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
+ deref_ty.ty
+ } else {
+ // We cannot allow this for now.
+ return Err((span, "unsizing casts are only allowed for references right now".into()));
+ };
+ let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
+ if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
+ check_operand(tcx, op, span, body)?;
+ // Casting/coercing things to slices is fine.
+ Ok(())
+ } else {
+ // We just can't allow trait objects until we have figured out trait method calls.
+ Err((span, "unsizing casts are not allowed in const fn".into()))
+ }
+ },
+ // binops are fine on integers
+ Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
+ check_operand(tcx, lhs, span, body)?;
+ check_operand(tcx, rhs, span, body)?;
+ let ty = lhs.ty(body, tcx);
+ if ty.is_integral() || ty.is_bool() || ty.is_char() {
+ Ok(())
+ } else {
+ Err((
+ span,
+ "only int, `bool` and `char` operations are stable in const fn".into(),
+ ))
+ }
+ },
- Rvalue::ShallowInitBox(_, _) => Ok(()),
++ Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
+ Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
+ Rvalue::UnaryOp(_, operand) => {
+ let ty = operand.ty(body, tcx);
+ if ty.is_integral() || ty.is_bool() {
+ check_operand(tcx, operand, span, body)
+ } else {
+ Err((span, "only int and `bool` operations are stable in const fn".into()))
+ }
+ },
+ Rvalue::Aggregate(_, operands) => {
+ for operand in operands {
+ check_operand(tcx, operand, span, body)?;
+ }
+ Ok(())
+ },
+ }
+}
+
+fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
+ let span = statement.source_info.span;
+ match &statement.kind {
+ StatementKind::Assign(box (place, rval)) => {
+ check_place(tcx, *place, span, body)?;
+ check_rvalue(tcx, body, def_id, rval, span)
+ },
+
+ StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
+ // just an assignment
+ StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
+
+ StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
+
+ StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
+ check_operand(tcx, dst, span, body)?;
+ check_operand(tcx, src, span, body)?;
+ check_operand(tcx, count, span, body)
+ },
+ // These are all NOPs
+ StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Retag { .. }
+ | StatementKind::AscribeUserType(..)
+ | StatementKind::Coverage(..)
+ | StatementKind::Nop => Ok(()),
+ }
+}
+
+fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+ match operand {
+ Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
+ Operand::Constant(c) => match c.check_static_ptr(tcx) {
+ Some(_) => Err((span, "cannot access `static` items in const fn".into())),
+ None => Ok(()),
+ },
+ }
+}
+
+fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+ let mut cursor = place.projection.as_ref();
+ while let [ref proj_base @ .., elem] = *cursor {
+ cursor = proj_base;
+ match elem {
+ ProjectionElem::Field(..) => {
+ let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
+ if let Some(def) = base_ty.ty_adt_def() {
+ // No union field accesses in `const fn`
+ if def.is_union() {
+ return Err((span, "accessing union fields is unstable".into()));
+ }
+ }
+ },
+ ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Downcast(..)
+ | ProjectionElem::Subslice { .. }
+ | ProjectionElem::Deref
+ | ProjectionElem::Index(_) => {},
+ }
+ }
+
+ Ok(())
+}
+
+fn check_terminator(
+ tcx: TyCtxt<'tcx>,
+ body: &'a Body<'tcx>,
+ terminator: &Terminator<'tcx>,
+ msrv: Option<&RustcVersion>,
+) -> McfResult {
+ let span = terminator.source_info.span;
+ match &terminator.kind {
+ TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::Goto { .. }
+ | TerminatorKind::Return
+ | TerminatorKind::Resume
+ | TerminatorKind::Unreachable => Ok(()),
+
+ TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
+ TerminatorKind::DropAndReplace { place, value, .. } => {
+ check_place(tcx, *place, span, body)?;
+ check_operand(tcx, value, span, body)
+ },
+
+ TerminatorKind::SwitchInt {
+ discr,
+ switch_ty: _,
+ targets: _,
+ } => check_operand(tcx, discr, span, body),
+
+ TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
+ TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
+ Err((span, "const fn generators are unstable".into()))
+ },
+
+ TerminatorKind::Call {
+ func,
+ args,
+ from_hir_call: _,
+ destination: _,
+ cleanup: _,
+ fn_span: _,
+ } => {
+ let fn_ty = func.ty(body, tcx);
+ if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
+ if !is_const_fn(tcx, fn_def_id, msrv) {
+ return Err((
+ span,
+ format!(
+ "can only call other `const fn` within a `const fn`, \
+ but `{:?}` is not stable as `const fn`",
+ func,
+ )
+ .into(),
+ ));
+ }
+
+ // HACK: This is to "unstabilize" the `transmute` intrinsic
+ // within const fns. `transmute` is allowed in all other const contexts.
+ // This won't really scale to more intrinsics or functions. Let's allow const
+ // transmutes in const fn before we add more hacks to this.
+ if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
+ return Err((
+ span,
+ "can only call `transmute` from const items, not `const fn`".into(),
+ ));
+ }
+
+ check_operand(tcx, func, span, body)?;
+
+ for arg in args {
+ check_operand(tcx, arg, span, body)?;
+ }
+ Ok(())
+ } else {
+ Err((span, "can only call other const fns within const fn".into()))
+ }
+ },
+
+ TerminatorKind::Assert {
+ cond,
+ expected: _,
+ msg: _,
+ target: _,
+ cleanup: _,
+ } => check_operand(tcx, cond, span, body),
+
+ TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
+ }
+}
+
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
+ rustc_const_eval::const_eval::is_const_fn(tcx, def_id)
+ && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
+ if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
+ // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
+ // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
+ // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
+ crate::meets_msrv(
+ msrv,
+ &RustcVersion::parse(&since.as_str())
+ .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
+ )
+ } else {
+ // Unstable const fn with the feature enabled.
+ msrv.is_none()
+ }
+ })
+}
--- /dev/null
- let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
+// Run clippy on a fixed set of crates and collect the warnings.
+// This helps observing the impact clippy changes have on a set of real-world code (and not just our
+// testsuite).
+//
+// When a new lint is introduced, we can search the results for new warnings and check for false
+// positives.
+
+#![allow(clippy::collapsible_else_if)]
+
+use std::ffi::OsStr;
+use std::process::Command;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::{collections::HashMap, io::ErrorKind};
+use std::{
+ env, fmt,
+ fs::write,
+ path::{Path, PathBuf},
++ thread,
++ time::Duration,
+};
+
+use clap::{App, Arg, ArgMatches};
+use rayon::prelude::*;
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+use walkdir::{DirEntry, WalkDir};
+
+#[cfg(not(windows))]
+const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
+#[cfg(not(windows))]
+const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
+
+#[cfg(windows)]
+const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
+#[cfg(windows)]
+const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
+
+const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
+const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
+
+/// List of sources to check, loaded from a .toml file
+#[derive(Debug, Serialize, Deserialize)]
+struct SourceList {
+ crates: HashMap<String, TomlCrate>,
+}
+
+/// A crate source stored inside the .toml
+/// will be translated into on one of the `CrateSource` variants
+#[derive(Debug, Serialize, Deserialize)]
+struct TomlCrate {
+ name: String,
+ versions: Option<Vec<String>>,
+ git_url: Option<String>,
+ git_hash: Option<String>,
+ path: Option<String>,
+ options: Option<Vec<String>>,
+}
+
+/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
+/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
+#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
+enum CrateSource {
+ CratesIo {
+ name: String,
+ version: String,
+ options: Option<Vec<String>>,
+ },
+ Git {
+ name: String,
+ url: String,
+ commit: String,
+ options: Option<Vec<String>>,
+ },
+ Path {
+ name: String,
+ path: PathBuf,
+ options: Option<Vec<String>>,
+ },
+}
+
+/// Represents the actual source code of a crate that we ran "cargo clippy" on
+#[derive(Debug)]
+struct Crate {
+ version: String,
+ name: String,
+ // path to the extracted sources that clippy can check
+ path: PathBuf,
+ options: Option<Vec<String>>,
+}
+
+/// A single warning that clippy issued while checking a `Crate`
+#[derive(Debug)]
+struct ClippyWarning {
+ crate_name: String,
+ crate_version: String,
+ file: String,
+ line: String,
+ column: String,
+ linttype: String,
+ message: String,
+ is_ice: bool,
+}
+
+impl std::fmt::Display for ClippyWarning {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ writeln!(
+ f,
+ r#"target/lintcheck/sources/{}-{}/{}:{}:{} {} "{}""#,
+ &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
+ )
+ }
+}
+
++fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
++ const MAX_RETRIES: u8 = 4;
++ let mut retries = 0;
++ loop {
++ match ureq::get(path).call() {
++ Ok(res) => return Ok(res),
++ Err(e) if retries >= MAX_RETRIES => return Err(e),
++ Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e),
++ Err(e) => return Err(e),
++ }
++ eprintln!("retrying in {} seconds...", retries);
++ thread::sleep(Duration::from_secs(retries as u64));
++ retries += 1;
++ }
++}
++
+impl CrateSource {
+ /// Makes the sources available on the disk for clippy to check.
+ /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
+ /// copies a local folder
+ fn download_and_extract(&self) -> Crate {
+ match self {
+ CrateSource::CratesIo { name, version, options } => {
+ let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
+ let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
+
+ // url to download the crate from crates.io
+ let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version);
+ println!("Downloading and extracting {} {} from {}", name, version, url);
+ create_dirs(&krate_download_dir, &extract_dir);
+
+ let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version));
+ // don't download/extract if we already have done so
+ if !krate_file_path.is_file() {
+ // create a file path to download and write the crate data into
+ let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
++ let mut krate_req = get(&url).unwrap().into_reader();
+ // copy the crate into the file
+ std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
+
+ // unzip the tarball
+ let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
+ // extract the tar archive
+ let mut archive = tar::Archive::new(ungz_tar);
+ archive.unpack(&extract_dir).expect("Failed to extract!");
+ }
+ // crate is extracted, return a new Krate object which contains the path to the extracted
+ // sources that clippy can check
+ Crate {
+ version: version.clone(),
+ name: name.clone(),
+ path: extract_dir.join(format!("{}-{}/", name, version)),
+ options: options.clone(),
+ }
+ },
+ CrateSource::Git {
+ name,
+ url,
+ commit,
+ options,
+ } => {
+ let repo_path = {
+ let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
+ // add a -git suffix in case we have the same crate from crates.io and a git repo
+ repo_path.push(format!("{}-git", name));
+ repo_path
+ };
+ // clone the repo if we have not done so
+ if !repo_path.is_dir() {
+ println!("Cloning {} and checking out {}", url, commit);
+ if !Command::new("git")
+ .arg("clone")
+ .arg(url)
+ .arg(&repo_path)
+ .status()
+ .expect("Failed to clone git repo!")
+ .success()
+ {
+ eprintln!("Failed to clone {} into {}", url, repo_path.display())
+ }
+ }
+ // check out the commit/branch/whatever
+ if !Command::new("git")
+ .arg("checkout")
+ .arg(commit)
+ .current_dir(&repo_path)
+ .status()
+ .expect("Failed to check out commit")
+ .success()
+ {
+ eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display())
+ }
+
+ Crate {
+ version: commit.clone(),
+ name: name.clone(),
+ path: repo_path,
+ options: options.clone(),
+ }
+ },
+ CrateSource::Path { name, path, options } => {
+ // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
+ // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
+ // as a result of this filter.
+ let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
+ if dest_crate_root.exists() {
+ println!("Deleting existing directory at {:?}", dest_crate_root);
+ std::fs::remove_dir_all(&dest_crate_root).unwrap();
+ }
+
+ println!("Copying {:?} to {:?}", path, dest_crate_root);
+
+ fn is_cache_dir(entry: &DirEntry) -> bool {
+ std::fs::read(entry.path().join("CACHEDIR.TAG"))
+ .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
+ .unwrap_or(false)
+ }
+
+ for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
+ let entry = entry.unwrap();
+ let entry_path = entry.path();
+ let relative_entry_path = entry_path.strip_prefix(path).unwrap();
+ let dest_path = dest_crate_root.join(relative_entry_path);
+ let metadata = entry_path.symlink_metadata().unwrap();
+
+ if metadata.is_dir() {
+ std::fs::create_dir(dest_path).unwrap();
+ } else if metadata.is_file() {
+ std::fs::copy(entry_path, dest_path).unwrap();
+ }
+ }
+
+ Crate {
+ version: String::from("local"),
+ name: name.clone(),
+ path: dest_crate_root,
+ options: options.clone(),
+ }
+ },
+ }
+ }
+}
+
+impl Crate {
+ /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
+ /// issued
+ fn run_clippy_lints(
+ &self,
+ cargo_clippy_path: &Path,
+ target_dir_index: &AtomicUsize,
+ thread_limit: usize,
+ total_crates_to_lint: usize,
+ fix: bool,
+ ) -> Vec<ClippyWarning> {
+ // advance the atomic index by one
+ let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
+ // "loop" the index within 0..thread_limit
+ let thread_index = index % thread_limit;
+ let perc = (index * 100) / total_crates_to_lint;
+
+ if thread_limit == 1 {
+ println!(
+ "{}/{} {}% Linting {} {}",
+ index, total_crates_to_lint, perc, &self.name, &self.version
+ );
+ } else {
+ println!(
+ "{}/{} {}% Linting {} {} in target dir {:?}",
+ index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
+ );
+ }
+
+ let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
+
+ let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
+
+ let mut args = if fix {
+ vec!["--fix", "--allow-no-vcs", "--", "--cap-lints=warn"]
+ } else {
+ vec!["--", "--message-format=json", "--", "--cap-lints=warn"]
+ };
+
+ if let Some(options) = &self.options {
+ for opt in options {
+ args.push(opt);
+ }
+ } else {
+ args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
+ }
+
+ let all_output = std::process::Command::new(&cargo_clippy_path)
+ // use the looping index to create individual target dirs
+ .env(
+ "CARGO_TARGET_DIR",
+ shared_target_dir.join(format!("_{:?}", thread_index)),
+ )
+ // lint warnings will look like this:
+ // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
+ .args(&args)
+ .current_dir(&self.path)
+ .output()
+ .unwrap_or_else(|error| {
+ panic!(
+ "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
+ error,
+ &cargo_clippy_path.display(),
+ &self.path.display()
+ );
+ });
+ let stdout = String::from_utf8_lossy(&all_output.stdout);
+ let stderr = String::from_utf8_lossy(&all_output.stderr);
+ let status = &all_output.status;
+
+ if !status.success() {
+ eprintln!(
+ "\nWARNING: bad exit status after checking {} {} \n",
+ self.name, self.version
+ );
+ }
+
+ if fix {
+ if let Some(stderr) = stderr
+ .lines()
+ .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate"))
+ {
+ let subcrate = &stderr[63..];
+ println!(
+ "ERROR: failed to apply some suggetion to {} / to (sub)crate {}",
+ self.name, subcrate
+ );
+ }
+ // fast path, we don't need the warnings anyway
+ return Vec::new();
+ }
+
+ let output_lines = stdout.lines();
+ let warnings: Vec<ClippyWarning> = output_lines
+ .into_iter()
+ // get all clippy warnings and ICEs
+ .filter(|line| filter_clippy_warnings(&line))
+ .map(|json_msg| parse_json_message(json_msg, &self))
+ .collect();
+
+ warnings
+ }
+}
+
+#[derive(Debug)]
+struct LintcheckConfig {
+ // max number of jobs to spawn (default 1)
+ max_jobs: usize,
+ // we read the sources to check from here
+ sources_toml_path: PathBuf,
+ // we save the clippy lint results here
+ lintcheck_results_path: PathBuf,
+ // whether to just run --fix and not collect all the warnings
+ fix: bool,
+}
+
+impl LintcheckConfig {
+ fn from_clap(clap_config: &ArgMatches) -> Self {
+ // first, check if we got anything passed via the LINTCHECK_TOML env var,
+ // if not, ask clap if we got any value for --crates-toml <foo>
+ // if not, use the default "lintcheck/lintcheck_crates.toml"
+ let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
+ clap_config
+ .value_of("crates-toml")
+ .clone()
+ .unwrap_or("lintcheck/lintcheck_crates.toml")
+ .to_string()
+ });
+
+ let sources_toml_path = PathBuf::from(sources_toml);
+
+ // for the path where we save the lint results, get the filename without extension (so for
+ // wasd.toml, use "wasd"...)
+ let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
+ let lintcheck_results_path = PathBuf::from(format!("lintcheck-logs/{}_logs.txt", filename.display()));
+
+ // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
+ // use half of that for the physical core count
+ // by default use a single thread
+ let max_jobs = match clap_config.value_of("threads") {
+ Some(threads) => {
+ let threads: usize = threads
+ .parse()
+ .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads));
+ if threads == 0 {
+ // automatic choice
+ // Rayon seems to return thread count so half that for core count
+ (rayon::current_num_threads() / 2) as usize
+ } else {
+ threads
+ }
+ },
+ // no -j passed, use a single thread
+ None => 1,
+ };
+ let fix: bool = clap_config.is_present("fix");
+
+ LintcheckConfig {
+ max_jobs,
+ sources_toml_path,
+ lintcheck_results_path,
+ fix,
+ }
+ }
+}
+
+/// takes a single json-formatted clippy warnings and returns true (we are interested in that line)
+/// or false (we aren't)
+fn filter_clippy_warnings(line: &str) -> bool {
+ // we want to collect ICEs because clippy might have crashed.
+ // these are summarized later
+ if line.contains("internal compiler error: ") {
+ return true;
+ }
+ // in general, we want all clippy warnings
+ // however due to some kind of bug, sometimes there are absolute paths
+ // to libcore files inside the message
+ // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508)
+
+ // filter out these message to avoid unnecessary noise in the logs
+ if line.contains("clippy::")
+ && !(line.contains("could not read cargo metadata")
+ || (line.contains(".rustup") && line.contains("toolchains")))
+ {
+ return true;
+ }
+ false
+}
+
+/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
+fn build_clippy() {
+ let status = Command::new("cargo")
+ .arg("build")
+ .status()
+ .expect("Failed to build clippy!");
+ if !status.success() {
+ eprintln!("Error: Failed to compile Clippy!");
+ std::process::exit(1);
+ }
+}
+
+/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy
+fn read_crates(toml_path: &Path) -> Vec<CrateSource> {
+ let toml_content: String =
+ std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
+ let crate_list: SourceList =
+ toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
+ // parse the hashmap of the toml file into a list of crates
+ let tomlcrates: Vec<TomlCrate> = crate_list
+ .crates
+ .into_iter()
+ .map(|(_cratename, tomlcrate)| tomlcrate)
+ .collect();
+
+ // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
+ // multiple Cratesources)
+ let mut crate_sources = Vec::new();
+ tomlcrates.into_iter().for_each(|tk| {
+ if let Some(ref path) = tk.path {
+ crate_sources.push(CrateSource::Path {
+ name: tk.name.clone(),
+ path: PathBuf::from(path),
+ options: tk.options.clone(),
+ });
+ }
+
+ // if we have multiple versions, save each one
+ if let Some(ref versions) = tk.versions {
+ versions.iter().for_each(|ver| {
+ crate_sources.push(CrateSource::CratesIo {
+ name: tk.name.clone(),
+ version: ver.to_string(),
+ options: tk.options.clone(),
+ });
+ })
+ }
+ // otherwise, we should have a git source
+ if tk.git_url.is_some() && tk.git_hash.is_some() {
+ crate_sources.push(CrateSource::Git {
+ name: tk.name.clone(),
+ url: tk.git_url.clone().unwrap(),
+ commit: tk.git_hash.clone().unwrap(),
+ options: tk.options.clone(),
+ });
+ }
+ // if we have a version as well as a git data OR only one git data, something is funky
+ if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
+ || tk.git_hash.is_some() != tk.git_url.is_some()
+ {
+ eprintln!("tomlkrate: {:?}", tk);
+ if tk.git_hash.is_some() != tk.git_url.is_some() {
+ panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!");
+ }
+ if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) {
+ panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields");
+ }
+ unreachable!("Failed to translate TomlCrate into CrateSource!");
+ }
+ });
+ // sort the crates
+ crate_sources.sort();
+
+ crate_sources
+}
+
+/// Parse the json output of clippy and return a `ClippyWarning`
+fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
+ let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));
+
+ let file: String = jmsg["message"]["spans"][0]["file_name"]
+ .to_string()
+ .trim_matches('"')
+ .into();
+
+ let file = if file.contains(".cargo") {
+ // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from
+ // the registry.
+ // don't show the full path in that case.
+
+ // /home/matthias/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs
+ let path = PathBuf::from(file);
+ let mut piter = path.iter();
+ // consume all elements until we find ".cargo", so that "/home/matthias" is skipped
+ let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo"));
+ // collect the remaining segments
+ let file = piter.collect::<PathBuf>();
+ format!("{}", file.display())
+ } else {
+ file
+ };
+
+ ClippyWarning {
+ crate_name: krate.name.to_string(),
+ crate_version: krate.version.to_string(),
+ file,
+ line: jmsg["message"]["spans"][0]["line_start"]
+ .to_string()
+ .trim_matches('"')
+ .into(),
+ column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"]
+ .to_string()
+ .trim_matches('"')
+ .into(),
+ linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
+ message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
+ is_ice: json_message.contains("internal compiler error: "),
+ }
+}
+
+/// Generate a short list of occuring lints-types and their count
+fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
+ // count lint type occurrences
+ let mut counter: HashMap<&String, usize> = HashMap::new();
+ clippy_warnings
+ .iter()
+ .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
+
+ // collect into a tupled list for sorting
+ let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
+ // sort by "000{count} {clippy::lintname}"
+ // to not have a lint with 200 and 2 warnings take the same spot
+ stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
+
+ let stats_string = stats
+ .iter()
+ .map(|(lint, count)| format!("{} {}\n", lint, count))
+ .collect::<String>();
+
+ (stats_string, counter)
+}
+
+/// check if the latest modification of the logfile is older than the modification date of the
+/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
+fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool {
+ if !lintcheck_logs_path.exists() {
+ return true;
+ }
+
+ let clippy_modified: std::time::SystemTime = {
+ let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| {
+ std::fs::metadata(p)
+ .expect("failed to get metadata of file")
+ .modified()
+ .expect("failed to get modification date")
+ });
+ // the oldest modification of either of the binaries
+ std::cmp::max(times.next().unwrap(), times.next().unwrap())
+ };
+
+ let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path)
+ .expect("failed to get metadata of file")
+ .modified()
+ .expect("failed to get modification date");
+
+ // time is represented in seconds since X
+ // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck
+ logs_modified < clippy_modified
+}
+
+fn is_in_clippy_root() -> bool {
+ if let Ok(pb) = std::env::current_dir() {
+ if let Some(file) = pb.file_name() {
+ return file == PathBuf::from("rust-clippy");
+ }
+ }
+
+ false
+}
+
+/// lintchecks `main()` function
+///
+/// # Panics
+///
+/// This function panics if the clippy binaries don't exist
+/// or if lintcheck is executed from the wrong directory (aka none-repo-root)
+pub fn main() {
+ // assert that we launch lintcheck from the repo root (via cargo lintcheck)
+ if !is_in_clippy_root() {
+ eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
+ std::process::exit(3);
+ }
+
+ let clap_config = &get_clap_config();
+
+ let config = LintcheckConfig::from_clap(clap_config);
+
+ println!("Compiling clippy...");
+ build_clippy();
+ println!("Done compiling");
+
+ // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
+ // refresh the logs
+ if lintcheck_needs_rerun(&config.lintcheck_results_path) {
+ let shared_target_dir = "target/lintcheck/shared_target_dir";
+ // if we get an Err here, the shared target dir probably does simply not exist
+ if let Ok(metadata) = std::fs::metadata(&shared_target_dir) {
+ if metadata.is_dir() {
+ println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
+ std::fs::remove_dir_all(&shared_target_dir)
+ .expect("failed to remove target/lintcheck/shared_target_dir");
+ }
+ }
+ }
+
+ let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH)
+ .canonicalize()
+ .expect("failed to canonicalize path to clippy binary");
+
+ // assert that clippy is found
+ assert!(
+ cargo_clippy_path.is_file(),
+ "target/debug/cargo-clippy binary not found! {}",
+ cargo_clippy_path.display()
+ );
+
+ let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH)
+ .arg("--version")
+ .output()
+ .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
+ .expect("could not get clippy version!");
+
+ // download and extract the crates, then run clippy on them and collect clippys warnings
+ // flatten into one big list of warnings
+
+ let crates = read_crates(&config.sources_toml_path);
+ let old_stats = read_stats_from_file(&config.lintcheck_results_path);
+
+ let counter = AtomicUsize::new(1);
+
+ let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
+ // if we don't have the specified crate in the .toml, throw an error
+ if !crates.iter().any(|krate| {
+ let name = match krate {
+ CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => {
+ name
+ },
+ };
+ name == only_one_crate
+ }) {
+ eprintln!(
+ "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml",
+ only_one_crate
+ );
+ std::process::exit(1);
+ }
+
+ // only check a single crate that was passed via cmdline
+ crates
+ .into_iter()
+ .map(|krate| krate.download_and_extract())
+ .filter(|krate| krate.name == only_one_crate)
+ .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix))
+ .collect()
+ } else {
+ if config.max_jobs > 1 {
+ // run parallel with rayon
+
+ // Ask rayon for thread count. Assume that half of that is the number of physical cores
+ // Use one target dir for each core so that we can run N clippys in parallel.
+ // We need to use different target dirs because cargo would lock them for a single build otherwise,
+ // killing the parallelism. However this also means that deps will only be reused half/a
+ // quarter of the time which might result in a longer wall clock runtime
+
+ // This helps when we check many small crates with dep-trees that don't have a lot of branches in
+ // order to achive some kind of parallelism
+
+ // by default, use a single thread
+ let num_cpus = config.max_jobs;
+ let num_crates = crates.len();
+
+ // check all crates (default)
+ crates
+ .into_par_iter()
+ .map(|krate| krate.download_and_extract())
+ .flat_map(|krate| {
+ krate.run_clippy_lints(&cargo_clippy_path, &counter, num_cpus, num_crates, config.fix)
+ })
+ .collect()
+ } else {
+ // run sequential
+ let num_crates = crates.len();
+ crates
+ .into_iter()
+ .map(|krate| krate.download_and_extract())
+ .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix))
+ .collect()
+ }
+ };
+
+ // if we are in --fix mode, don't change the log files, terminate here
+ if config.fix {
+ return;
+ }
+
+ // generate some stats
+ let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
+
+ // grab crashes/ICEs, save the crate name and the ice message
+ let ices: Vec<(&String, &String)> = clippy_warnings
+ .iter()
+ .filter(|warning| warning.is_ice)
+ .map(|w| (&w.crate_name, &w.message))
+ .collect();
+
+ let mut all_msgs: Vec<String> = clippy_warnings.iter().map(ToString::to_string).collect();
+ all_msgs.sort();
+ all_msgs.push("\n\n\n\nStats:\n".into());
+ all_msgs.push(stats_formatted);
+
+ // save the text into lintcheck-logs/logs.txt
+ let mut text = clippy_ver; // clippy version number on top
+ text.push_str(&format!("\n{}", all_msgs.join("")));
+ text.push_str("ICEs:\n");
+ ices.iter()
+ .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
+
+ println!("Writing logs to {}", config.lintcheck_results_path.display());
+ write(&config.lintcheck_results_path, text).unwrap();
+
+ print_stats(old_stats, new_stats);
+}
+
+/// read the previous stats from the lintcheck-log file
+fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
+ let file_content: String = match std::fs::read_to_string(file_path).ok() {
+ Some(content) => content,
+ None => {
+ return HashMap::new();
+ },
+ };
+
+ let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
+
+ // search for the beginning "Stats:" and the end "ICEs:" of the section we want
+ let start = lines.iter().position(|line| line == "Stats:").unwrap();
+ let end = lines.iter().position(|line| line == "ICEs:").unwrap();
+
+ let stats_lines = &lines[start + 1..end];
+
+ stats_lines
+ .iter()
+ .map(|line| {
+ let mut spl = line.split(' ');
+ (
+ spl.next().unwrap().to_string(),
+ spl.next().unwrap().parse::<usize>().unwrap(),
+ )
+ })
+ .collect::<HashMap<String, usize>>()
+}
+
+/// print how lint counts changed between runs
+fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>) {
+ let same_in_both_hashmaps = old_stats
+ .iter()
+ .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val))
+ .map(|(k, v)| (k.to_string(), *v))
+ .collect::<Vec<(String, usize)>>();
+
+ let mut old_stats_deduped = old_stats;
+ let mut new_stats_deduped = new_stats;
+
+ // remove duplicates from both hashmaps
+ same_in_both_hashmaps.iter().for_each(|(k, v)| {
+ assert!(old_stats_deduped.remove(k) == Some(*v));
+ assert!(new_stats_deduped.remove(k) == Some(*v));
+ });
+
+ println!("\nStats:");
+
+ // list all new counts (key is in new stats but not in old stats)
+ new_stats_deduped
+ .iter()
+ .filter(|(new_key, _)| old_stats_deduped.get::<str>(&new_key).is_none())
+ .for_each(|(new_key, new_value)| {
+ println!("{} 0 => {}", new_key, new_value);
+ });
+
+ // list all changed counts (key is in both maps but value differs)
+ new_stats_deduped
+ .iter()
+ .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(&new_key).is_some())
+ .for_each(|(new_key, new_val)| {
+ let old_val = old_stats_deduped.get::<str>(&new_key).unwrap();
+ println!("{} {} => {}", new_key, old_val, new_val);
+ });
+
+ // list all gone counts (key is in old status but not in new stats)
+ old_stats_deduped
+ .iter()
+ .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none())
+ .for_each(|(old_key, old_value)| {
+ println!("{} {} => 0", old_key, old_value);
+ });
+}
+
+/// Create necessary directories to run the lintcheck tool.
+///
+/// # Panics
+///
+/// This function panics if creating one of the dirs fails.
+fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
+ std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
+ if err.kind() != ErrorKind::AlreadyExists {
+ panic!("cannot create lintcheck target dir");
+ }
+ });
+ std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| {
+ if err.kind() != ErrorKind::AlreadyExists {
+ panic!("cannot create crate download dir");
+ }
+ });
+ std::fs::create_dir(&extract_dir).unwrap_or_else(|err| {
+ if err.kind() != ErrorKind::AlreadyExists {
+ panic!("cannot create crate extraction dir");
+ }
+ });
+}
+
+fn get_clap_config<'a>() -> ArgMatches<'a> {
+ App::new("lintcheck")
+ .about("run clippy on a set of crates and check output")
+ .arg(
+ Arg::with_name("only")
+ .takes_value(true)
+ .value_name("CRATE")
+ .long("only")
+ .help("only process a single crate of the list"),
+ )
+ .arg(
+ Arg::with_name("crates-toml")
+ .takes_value(true)
+ .value_name("CRATES-SOURCES-TOML-PATH")
+ .long("crates-toml")
+ .help("set the path for a crates.toml where lintcheck should read the sources from"),
+ )
+ .arg(
+ Arg::with_name("threads")
+ .takes_value(true)
+ .value_name("N")
+ .short("j")
+ .long("jobs")
+ .help("number of threads to use, 0 automatic choice"),
+ )
+ .arg(
+ Arg::with_name("fix")
+ .long("--fix")
+ .help("runs cargo clippy --fix and checks if all suggestions apply"),
+ )
+ .get_matches()
+}
+
+/// 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.");
+}
+
+#[test]
+fn lintcheck_test() {
+ let args = [
+ "run",
+ "--target-dir",
+ "lintcheck/target",
+ "--manifest-path",
+ "./lintcheck/Cargo.toml",
+ "--",
+ "--crates-toml",
+ "lintcheck/test_sources.toml",
+ ];
+ let status = std::process::Command::new("cargo")
+ .args(&args)
+ .current_dir("..") // repo root
+ .status();
+ //.output();
+
+ assert!(status.unwrap().success());
+}
--- /dev/null
- channel = "nightly-2021-09-08"
+[toolchain]
++channel = "nightly-2021-09-28"
+components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
--- /dev/null
- use std::ffi::OsString;
+#![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)]
+
+use rustc_tools_util::VersionInfo;
+use std::env;
- --no-deps Run Clippy only on the given crate, without linting the dependencies
+use std::path::PathBuf;
+use std::process::{self, Command};
+
+const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
+
+Usage:
+ cargo clippy [options] [--] [<opts>...]
+
+Common options:
- fn target_dir() -> Option<(&'static str, OsString)> {
- env::var_os("CLIPPY_DOGFOOD")
- .map(|_| {
- env::var_os("CARGO_MANIFEST_DIR").map_or_else(
- || std::ffi::OsString::from("clippy_dogfood"),
- |d| {
- std::path::PathBuf::from(d)
- .join("target")
- .join("dogfood")
- .into_os_string()
- },
- )
- })
- .map(|p| ("CARGO_TARGET_DIR", p))
- }
-
++ --no-deps Run Clippy only on the given crate, without linting the dependencies
+ --fix Automatically apply lint suggestions. This flag implies `--no-deps`
+ -h, --help Print this message
+ -V, --version Print version info and exit
+
+Other options are the same as `cargo check`.
+
+To allow or deny a lint from the command line you can use `cargo clippy --`
+with:
+
+ -W --warn OPT Set lint warnings
+ -A --allow OPT Set lint allowed
+ -D --deny OPT Set lint denied
+ -F --forbid OPT Set lint forbidden
+
+You can use tool lints to allow or deny lints from your code, eg.:
+
+ #[allow(clippy::needless_lifetimes)]
+"#;
+
+fn show_help() {
+ println!("{}", CARGO_CLIPPY_HELP);
+}
+
+fn show_version() {
+ let version_info = rustc_tools_util::get_version_info!();
+ println!("{}", version_info);
+}
+
+pub fn main() {
+ // Check for version and help flags even when invoked as 'cargo-clippy'
+ if env::args().any(|a| a == "--help" || a == "-h") {
+ show_help();
+ return;
+ }
+
+ if env::args().any(|a| a == "--version" || a == "-V") {
+ show_version();
+ return;
+ }
+
+ if let Err(code) = process(env::args().skip(2)) {
+ process::exit(code);
+ }
+}
+
+struct ClippyCmd {
+ cargo_subcommand: &'static str,
+ args: Vec<String>,
+ clippy_args: Vec<String>,
+}
+
+impl ClippyCmd {
+ fn new<I>(mut old_args: I) -> Self
+ where
+ I: Iterator<Item = String>,
+ {
+ let mut cargo_subcommand = "check";
+ let mut args = vec![];
+ let mut clippy_args: Vec<String> = vec![];
+
+ for arg in old_args.by_ref() {
+ match arg.as_str() {
+ "--fix" => {
+ cargo_subcommand = "fix";
+ continue;
+ },
+ "--no-deps" => {
+ clippy_args.push("--no-deps".into());
+ continue;
+ },
+ "--" => break,
+ _ => {},
+ }
+
+ args.push(arg);
+ }
+
+ clippy_args.append(&mut (old_args.collect()));
+ if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
+ clippy_args.push("--no-deps".into());
+ }
+
+ ClippyCmd {
+ cargo_subcommand,
+ args,
+ clippy_args,
+ }
+ }
+
+ fn path() -> PathBuf {
+ let mut path = env::current_exe()
+ .expect("current executable path invalid")
+ .with_file_name("clippy-driver");
+
+ if cfg!(windows) {
+ path.set_extension("exe");
+ }
+
+ path
+ }
+
- .envs(ClippyCmd::target_dir())
+ fn into_std_cmd(self) -> Command {
+ let mut cmd = Command::new("cargo");
+ let clippy_args: String = self
+ .clippy_args
+ .iter()
+ .map(|arg| format!("{}__CLIPPY_HACKERY__", arg))
+ .collect();
+
+ cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
+ .env("CLIPPY_ARGS", clippy_args)
+ .arg(self.cargo_subcommand)
+ .args(&self.args);
+
+ cmd
+ }
+}
+
+fn process<I>(old_args: I) -> Result<(), i32>
+where
+ I: Iterator<Item = String>,
+{
+ let cmd = ClippyCmd::new(old_args);
+
+ let mut cmd = cmd.into_std_cmd();
+
+ let exit_status = cmd
+ .spawn()
+ .expect("could not run cargo")
+ .wait()
+ .expect("failed to wait for cargo?");
+
+ if exit_status.success() {
+ Ok(())
+ } else {
+ Err(exit_status.code().unwrap_or(-1))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::ClippyCmd;
+
+ #[test]
+ fn fix() {
+ let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert_eq!("fix", cmd.cargo_subcommand);
+ assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
+ }
+
+ #[test]
+ fn fix_implies_no_deps() {
+ let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
+ }
+
+ #[test]
+ fn no_deps_not_duplicated_with_fix() {
+ let args = "cargo clippy --fix -- --no-deps"
+ .split_whitespace()
+ .map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
+ }
+
+ #[test]
+ fn check() {
+ let args = "cargo clippy".split_whitespace().map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert_eq!("check", cmd.cargo_subcommand);
+ }
+}
--- /dev/null
- use std::env;
- use std::lazy::SyncLazy;
- use std::path::PathBuf;
-
- pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
- Some(v) => v.into(),
- None => env::current_dir().unwrap().join("target"),
- });
-
- pub static TARGET_LIB: SyncLazy<PathBuf> = SyncLazy::new(|| {
- if let Some(path) = option_env!("TARGET_LIBS") {
- path.into()
- } else {
- let mut dir = CARGO_TARGET_DIR.clone();
- if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
- dir.push(target);
- }
- dir.push(env!("PROFILE"));
- dir
- }
- });
-
+#[must_use]
+pub fn is_rustc_test_suite() -> bool {
+ option_env!("RUSTC_TEST_SUITE").is_some()
+}
--- /dev/null
- #![feature(once_cell)]
+#![feature(test)] // compiletest_rs requires this attribute
- fn host_lib() -> PathBuf {
- option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
- }
-
- fn clippy_driver_path() -> PathBuf {
- option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
- }
-
+#![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;
+
- if !not_found.is_empty() {
- panic!("dependencies not found in depinfo: {:?}", not_found);
- }
+/// 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();
- .map(|(name, path)| format!("--extern {}={} ", name, path))
++ assert!(
++ not_found.is_empty(),
++ "dependencies not found in depinfo: {:?}",
++ not_found
++ );
+ crates
+ .into_iter()
- "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}",
- host_lib().join("deps").display(),
- cargo::TARGET_LIB.join("deps").display(),
++ .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!(
- config.build_base = host_lib().join("test_build_base");
- config.rustc_path = clippy_driver_path();
++ "--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
- static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));
+//! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and
+//! long error messages
+//!
+//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
+
+// Dogfood cannot run on Windows
+#![cfg(not(windows))]
+#![feature(once_cell)]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
+use std::lazy::SyncLazy;
+use std::path::PathBuf;
+use std::process::Command;
+
+mod cargo;
+
- .env("CLIPPY_DOGFOOD", "1")
++static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
++ let mut path = std::env::current_exe().unwrap();
++ assert!(path.pop()); // deps
++ path.set_file_name("cargo-clippy");
++ path
++});
+
+#[test]
+fn dogfood_clippy() {
+ // run clippy on itself and fail the test if lint warnings are reported
+ if cargo::is_rustc_test_suite() {
+ return;
+ }
+ let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+
+ let mut command = Command::new(&*CLIPPY_PATH);
+ command
+ .current_dir(root_dir)
- .env("CLIPPY_DOGFOOD", "1")
+ .env("CARGO_INCREMENTAL", "0")
+ .arg("clippy")
+ .arg("--all-targets")
+ .arg("--all-features")
+ .arg("--")
+ .args(&["-D", "clippy::all"])
+ .args(&["-D", "clippy::pedantic"])
+ .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
+
+ // internal lints only exist if we build with the internal-lints feature
+ if cfg!(feature = "internal-lints") {
+ command.args(&["-D", "clippy::internal"]);
+ }
+
+ let output = command.output().unwrap();
+
+ println!("status: {}", output.status);
+ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+ assert!(output.status.success());
+}
+
+fn test_no_deps_ignores_path_deps_in_workspaces() {
+ if cargo::is_rustc_test_suite() {
+ return;
+ }
+ let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ let target_dir = root.join("target").join("dogfood");
+ let cwd = root.join("clippy_workspace_tests");
+
+ // Make sure we start with a clean state
+ Command::new("cargo")
+ .current_dir(&cwd)
+ .env("CARGO_TARGET_DIR", &target_dir)
+ .arg("clean")
+ .args(&["-p", "subcrate"])
+ .args(&["-p", "path_dep"])
+ .output()
+ .unwrap();
+
+ // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
+ // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
+ let output = Command::new(&*CLIPPY_PATH)
+ .current_dir(&cwd)
- .env("CLIPPY_DOGFOOD", "1")
+ .env("CARGO_INCREMENTAL", "0")
+ .arg("clippy")
+ .args(&["-p", "subcrate"])
+ .arg("--no-deps")
+ .arg("--")
+ .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+ .args(&["--cfg", r#"feature="primary_package_test""#])
+ .output()
+ .unwrap();
+ println!("status: {}", output.status);
+ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+ assert!(output.status.success());
+
+ let lint_path_dep = || {
+ // Test that without the `--no-deps` argument, `path_dep` is linted.
+ let output = Command::new(&*CLIPPY_PATH)
+ .current_dir(&cwd)
- .env("CLIPPY_DOGFOOD", "1")
+ .env("CARGO_INCREMENTAL", "0")
+ .arg("clippy")
+ .args(&["-p", "subcrate"])
+ .arg("--")
+ .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+ .args(&["--cfg", r#"feature="primary_package_test""#])
+ .output()
+ .unwrap();
+ println!("status: {}", output.status);
+ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+ assert!(!output.status.success());
+ assert!(
+ String::from_utf8(output.stderr)
+ .unwrap()
+ .contains("error: empty `loop {}` wastes CPU cycles")
+ );
+ };
+
+ // Make sure Cargo is aware of the removal of `--no-deps`.
+ lint_path_dep();
+
+ let successful_build = || {
+ let output = Command::new(&*CLIPPY_PATH)
+ .current_dir(&cwd)
- .env("CLIPPY_DOGFOOD", "1")
+ .env("CARGO_INCREMENTAL", "0")
+ .arg("clippy")
+ .args(&["-p", "subcrate"])
+ .arg("--")
+ .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+ .output()
+ .unwrap();
+ println!("status: {}", output.status);
+ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+ assert!(output.status.success());
+
+ output
+ };
+
+ // Trigger a sucessful build, so Cargo would like to cache the build result.
+ successful_build();
+
+ // Make sure there's no spurious rebuild when nothing changes.
+ let stderr = String::from_utf8(successful_build().stderr).unwrap();
+ assert!(!stderr.contains("Compiling"));
+ assert!(!stderr.contains("Checking"));
+ assert!(stderr.contains("Finished"));
+
+ // Make sure Cargo is aware of the new `--cfg` flag.
+ lint_path_dep();
+}
+
+#[test]
+fn dogfood_subprojects() {
+ // run clippy on remaining subprojects and fail the test if lint warnings are reported
+ if cargo::is_rustc_test_suite() {
+ return;
+ }
+
+ // NOTE: `path_dep` crate is omitted on purpose here
+ for project in &[
+ "clippy_workspace_tests",
+ "clippy_workspace_tests/src",
+ "clippy_workspace_tests/subcrate",
+ "clippy_workspace_tests/subcrate/src",
+ "clippy_dev",
+ "clippy_lints",
+ "clippy_utils",
+ "rustc_tools_util",
+ ] {
+ run_clippy_for_project(project);
+ }
+
+ // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
+ // same time, so we test this immediately after the dogfood for workspaces.
+ test_no_deps_ignores_path_deps_in_workspaces();
+}
+
+#[test]
+#[ignore]
+#[cfg(feature = "metadata-collector-lint")]
+fn run_metadata_collection_lint() {
+ use std::fs::File;
+ use std::time::SystemTime;
+
+ // Setup for validation
+ let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json");
+ let start_time = SystemTime::now();
+
+ // Run collection as is
+ std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
+ run_clippy_for_project("clippy_lints");
+
+ // Check if cargo caching got in the way
+ if let Ok(file) = File::open(metadata_output_path) {
+ if let Ok(metadata) = file.metadata() {
+ if let Ok(last_modification) = metadata.modified() {
+ if last_modification > start_time {
+ // The output file has been modified. Most likely by a hungry
+ // metadata collection monster. So We'll return.
+ return;
+ }
+ }
+ }
+ }
+
+ // Force cargo to invalidate the caches
+ filetime::set_file_mtime(
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
+ filetime::FileTime::now(),
+ )
+ .unwrap();
+
+ // Running the collection again
+ run_clippy_for_project("clippy_lints");
+}
+
+fn run_clippy_for_project(project: &str) {
+ let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+
+ let mut command = Command::new(&*CLIPPY_PATH);
+
+ command
+ .current_dir(root_dir.join(project))
+ .env("CARGO_INCREMENTAL", "0")
+ .arg("clippy")
+ .arg("--all-targets")
+ .arg("--all-features")
+ .arg("--")
+ .args(&["-D", "clippy::all"])
+ .args(&["-D", "clippy::pedantic"])
+ .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
+
+ // internal lints only exist if we build with the internal-lints feature
+ if cfg!(feature = "internal-lints") {
+ command.args(&["-D", "clippy::internal"]);
+ }
+
+ let output = command.output().unwrap();
+
+ println!("status: {}", output.status);
+ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+ assert!(output.status.success());
+}
--- /dev/null
- } else if stderr.contains("toolchain") && stderr.contains("is not installed") {
- panic!("missing required toolchain");
+#![cfg(feature = "integration")]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
+use std::env;
+use std::ffi::OsStr;
+use std::process::Command;
+
+#[cfg_attr(feature = "integration", test)]
+fn integration_test() {
+ let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set");
+ let repo_url = format!("https://github.com/{}", repo_name);
+ let crate_name = repo_name
+ .split('/')
+ .nth(1)
+ .expect("repo name should have format `<org>/<name>`");
+
+ let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path();
+ repo_dir.push(crate_name);
+
+ let st = Command::new("git")
+ .args(&[
+ OsStr::new("clone"),
+ OsStr::new("--depth=1"),
+ OsStr::new(&repo_url),
+ OsStr::new(&repo_dir),
+ ])
+ .status()
+ .expect("unable to run git");
+ assert!(st.success());
+
+ let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ let target_dir = std::path::Path::new(&root_dir).join("target");
+ let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy");
+
+ let output = Command::new(clippy_binary)
+ .current_dir(repo_dir)
+ .env("RUST_BACKTRACE", "full")
+ .env("CARGO_TARGET_DIR", target_dir)
+ .args(&[
+ "clippy",
+ "--all-targets",
+ "--all-features",
+ "--",
+ "--cap-lints",
+ "warn",
+ "-Wclippy::pedantic",
+ "-Wclippy::nursery",
+ ])
+ .output()
+ .expect("unable to run clippy");
+
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ if stderr.contains("internal compiler error") {
+ let backtrace_start = stderr
+ .find("thread 'rustc' panicked at")
+ .expect("start of backtrace not found");
+ let backtrace_end = stderr
+ .rfind("error: internal compiler error")
+ .expect("end of backtrace not found");
+
+ panic!(
+ "internal compiler error\nBacktrace:\n\n{}",
+ &stderr[backtrace_start..backtrace_end]
+ );
+ } else if stderr.contains("query stack during panic") {
+ panic!("query stack during panic in the output");
+ } else if stderr.contains("E0463") {
+ // Encountering E0463 (can't find crate for `x`) did _not_ cause the build to fail in the
+ // past. Even though it should have. That's why we explicitly panic here.
+ // See PR #3552 and issue #3523 for more background.
+ panic!("error: E0463");
+ } else if stderr.contains("E0514") {
+ panic!("incompatible crate versions");
+ } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
+ panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
++ } else {
++ assert!(
++ !stderr.contains("toolchain") || !stderr.contains("is not installed"),
++ "missing required toolchain"
++ );
+ }
+
+ match output.status.code() {
+ Some(0) => println!("Compilation successful"),
+ Some(code) => eprintln!("Compilation failed. Exit code: {}", code),
+ None => panic!("Process terminated by signal"),
+ }
+}
--- /dev/null
- = note: `-D clippy::clippy-lints-internal` implied by `-D warnings`
+error: invalid path
+ --> $DIR/invalid_paths.rs:17:5
+ |
+LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
++ = note: `-D clippy::invalid-paths` implied by `-D warnings`
+
+error: invalid path
+ --> $DIR/invalid_paths.rs:20:5
+ |
+LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
- "regex::Regex::is_match",
- "regex::Regex::new"
+disallowed-methods = [
++ # just a string is shorthand for path only
+ "std::iter::Iterator::sum",
++ # can give path and reason with an inline table
++ { path = "regex::Regex::is_match", reason = "no matching allowed" },
++ # can use an inline table but omit reason
++ { path = "regex::Regex::new" },
+]
--- /dev/null
- error: use of a disallowed method `regex::re_unicode::Regex::new`
++error: use of a disallowed method `regex::Regex::new`
+ --> $DIR/conf_disallowed_method.rs:7:14
+ |
+LL | let re = Regex::new(r"ab.*c").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::disallowed-method` implied by `-D warnings`
+
- error: use of a disallowed method `regex::re_unicode::Regex::is_match`
++error: use of a disallowed method `regex::Regex::is_match`
+ --> $DIR/conf_disallowed_method.rs:8:5
+ |
+LL | re.is_match("abc");
+ | ^^^^^^^^^^^^^^^^^^
++ |
++ = note: no matching allowed (from clippy.toml)
+
- error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
++error: use of a disallowed method `std::iter::Iterator::sum`
+ --> $DIR/conf_disallowed_method.rs:11:5
+ |
+LL | a.iter().sum::<i32>();
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
--- /dev/null
- #![allow(clippy::many_single_char_names)]
+// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
+// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
+
+#![deny(clippy::trivially_copy_pass_by_ref)]
+
+#[derive(Copy, Clone)]
+struct Foo(u8);
+
+#[derive(Copy, Clone)]
+struct Bar(u32);
+
+fn good(a: &mut u32, b: u32, c: &Bar, d: &u32) {}
+
+fn bad(x: &u16, y: &Foo) {}
+
+fn main() {
+ let (mut a, b, c, d, x, y) = (0, 0, Bar(0), 0, 0, Foo(0));
+ good(&mut a, b, &c, &d);
+ bad(&x, &y);
+}
--- /dev/null
- --> $DIR/test.rs:15:11
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/test.rs:15:20
++ --> $DIR/test.rs:14:11
+ |
+LL | fn bad(x: &u16, y: &Foo) {}
+ | ^^^^ help: consider passing by value instead: `u16`
+ |
+note: the lint level is defined here
+ --> $DIR/test.rs:4:9
+ |
+LL | #![deny(clippy::trivially_copy_pass_by_ref)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/test.rs:14:20
+ |
+LL | fn bad(x: &u16, y: &Foo) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
--- /dev/null
++// compile-flags: --emit=link
++// no-prefer-dynamic
++
++#![crate_type = "proc-macro"]
++
++extern crate proc_macro;
++use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
++use std::iter::FromIterator;
++
++fn read_ident(iter: &mut token_stream::IntoIter) -> Ident {
++ match iter.next() {
++ Some(TokenTree::Ident(i)) => i,
++ _ => panic!("expected ident"),
++ }
++}
++
++#[proc_macro_derive(DeriveBadSpan)]
++pub fn derive_bad_span(input: TokenStream) -> TokenStream {
++ let mut input = input.into_iter();
++ assert_eq!(read_ident(&mut input).to_string(), "struct");
++ let ident = read_ident(&mut input);
++ let mut tys = match input.next() {
++ Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.stream().into_iter(),
++ _ => panic!(),
++ };
++ let field1 = read_ident(&mut tys);
++ tys.next();
++ let field2 = read_ident(&mut tys);
++
++ <TokenStream as FromIterator<TokenTree>>::from_iter(
++ [
++ Ident::new("impl", Span::call_site()).into(),
++ ident.into(),
++ Group::new(
++ Delimiter::Brace,
++ <TokenStream as FromIterator<TokenTree>>::from_iter(
++ [
++ Ident::new("fn", Span::call_site()).into(),
++ Ident::new("_foo", Span::call_site()).into(),
++ Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
++ Group::new(
++ Delimiter::Brace,
++ <TokenStream as FromIterator<TokenTree>>::from_iter(
++ [
++ Ident::new("if", field1.span()).into(),
++ Ident::new("true", field1.span()).into(),
++ {
++ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
++ group.set_span(field1.span());
++ group.into()
++ },
++ Ident::new("if", field2.span()).into(),
++ Ident::new("true", field2.span()).into(),
++ {
++ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
++ group.set_span(field2.span());
++ group.into()
++ },
++ ]
++ .iter()
++ .cloned(),
++ ),
++ )
++ .into(),
++ ]
++ .iter()
++ .cloned(),
++ ),
++ )
++ .into(),
++ ]
++ .iter()
++ .cloned(),
++ )
++}
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![allow(
++ clippy::boxed_local,
++ clippy::needless_pass_by_value,
++ clippy::blacklisted_name,
++ unused
++)]
++
++use std::collections::HashMap;
++
++macro_rules! boxit {
++ ($init:expr, $x:ty) => {
++ let _: Box<$x> = Box::new($init);
++ };
++}
++
++fn test_macro() {
++ boxit!(Vec::new(), Vec<u8>);
++}
++
++fn test(foo: Box<Vec<bool>>) {}
++
++fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
++ // pass if #31 is fixed
++ foo(vec![1, 2, 3])
++}
++
++fn test3(foo: Box<String>) {}
++
++fn test4(foo: Box<HashMap<String, String>>) {}
++
++fn test_local_not_linted() {
++ let _: Box<Vec<bool>>;
++}
++
++// All of these test should be allowed because they are part of the
++// public api and `avoid_breaking_exported_api` is `false` by default.
++pub fn pub_test(foo: Box<Vec<bool>>) {}
++
++pub fn pub_test_ret() -> Box<Vec<bool>> {
++ Box::new(Vec::new())
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
++ --> $DIR/box_collection.rs:21:14
++ |
++LL | fn test(foo: Box<Vec<bool>>) {}
++ | ^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::box-collection` implied by `-D warnings`
++ = help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
++
++error: you seem to be trying to use `Box<String>`. Consider using just `String`
++ --> $DIR/box_collection.rs:28:15
++ |
++LL | fn test3(foo: Box<String>) {}
++ | ^^^^^^^^^^^
++ |
++ = help: `String` is already on the heap, `Box<String>` makes an extra allocation
++
++error: you seem to be trying to use `Box<HashMap<..>>`. Consider using just `HashMap<..>`
++ --> $DIR/box_collection.rs:30:15
++ |
++LL | fn test4(foo: Box<HashMap<String, String>>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
++
++error: aborting due to 3 previous errors
++
--- /dev/null
- #![allow(unused_imports,dead_code)]
+// run-rustfix
+
++#![allow(unused_imports, dead_code)]
+#![deny(clippy::default_trait_access)]
+
+use std::default;
+use std::default::Default as D2;
+use std::string;
+
+fn main() {
+ let s1: String = std::string::String::default();
+
+ let s2 = String::default();
+
+ let s3: String = std::string::String::default();
+
+ let s4: String = std::string::String::default();
+
+ let s5 = string::String::default();
+
+ let s6: String = std::string::String::default();
+
+ let s7 = std::string::String::default();
+
+ let s8: String = DefaultFactory::make_t_badly();
+
+ let s9: String = DefaultFactory::make_t_nicely();
+
+ let s10 = DerivedDefault::default();
+
+ let s11: GenericDerivedDefault<String> = GenericDerivedDefault::default();
+
+ let s12 = GenericDerivedDefault::<String>::default();
+
+ let s13 = TupleDerivedDefault::default();
+
+ let s14: TupleDerivedDefault = TupleDerivedDefault::default();
+
+ let s15: ArrayDerivedDefault = ArrayDerivedDefault::default();
+
+ let s16 = ArrayDerivedDefault::default();
+
+ let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default();
+
+ let s18 = TupleStructDerivedDefault::default();
+
+ let s19 = <DerivedDefault as Default>::default();
+
+ println!(
+ "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
+ s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19,
+ );
+}
+
+struct DefaultFactory;
+
+impl DefaultFactory {
+ pub fn make_t_badly<T: Default>() -> T {
+ Default::default()
+ }
+
+ pub fn make_t_nicely<T: Default>() -> T {
+ T::default()
+ }
+}
+
+#[derive(Debug, Default)]
+struct DerivedDefault {
+ pub s: String,
+}
+
+#[derive(Debug, Default)]
+struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
+ pub s: T,
+}
+
+#[derive(Debug, Default)]
+struct TupleDerivedDefault {
+ pub s: (String, String),
+}
+
+#[derive(Debug, Default)]
+struct ArrayDerivedDefault {
+ pub s: [String; 10],
+}
+
+#[derive(Debug, Default)]
+struct TupleStructDerivedDefault(String);
--- /dev/null
- #![allow(unused_imports,dead_code)]
+// run-rustfix
+
++#![allow(unused_imports, dead_code)]
+#![deny(clippy::default_trait_access)]
+
+use std::default;
+use std::default::Default as D2;
+use std::string;
+
+fn main() {
+ let s1: String = Default::default();
+
+ let s2 = String::default();
+
+ let s3: String = D2::default();
+
+ let s4: String = std::default::Default::default();
+
+ let s5 = string::String::default();
+
+ let s6: String = default::Default::default();
+
+ let s7 = std::string::String::default();
+
+ let s8: String = DefaultFactory::make_t_badly();
+
+ let s9: String = DefaultFactory::make_t_nicely();
+
+ let s10 = DerivedDefault::default();
+
+ let s11: GenericDerivedDefault<String> = Default::default();
+
+ let s12 = GenericDerivedDefault::<String>::default();
+
+ let s13 = TupleDerivedDefault::default();
+
+ let s14: TupleDerivedDefault = Default::default();
+
+ let s15: ArrayDerivedDefault = Default::default();
+
+ let s16 = ArrayDerivedDefault::default();
+
+ let s17: TupleStructDerivedDefault = Default::default();
+
+ let s18 = TupleStructDerivedDefault::default();
+
+ let s19 = <DerivedDefault as Default>::default();
+
+ println!(
+ "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
+ s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19,
+ );
+}
+
+struct DefaultFactory;
+
+impl DefaultFactory {
+ pub fn make_t_badly<T: Default>() -> T {
+ Default::default()
+ }
+
+ pub fn make_t_nicely<T: Default>() -> T {
+ T::default()
+ }
+}
+
+#[derive(Debug, Default)]
+struct DerivedDefault {
+ pub s: String,
+}
+
+#[derive(Debug, Default)]
+struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
+ pub s: T,
+}
+
+#[derive(Debug, Default)]
+struct TupleDerivedDefault {
+ pub s: (String, String),
+}
+
+#[derive(Debug, Default)]
+struct ArrayDerivedDefault {
+ pub s: [String; 10],
+}
+
+#[derive(Debug, Default)]
+struct TupleStructDerivedDefault(String);
--- /dev/null
- #[allow(clippy::many_single_char_names, clippy::double_parens)]
+// run-rustfix
+#![warn(clippy::deref_addrof)]
+
+fn get_number() -> usize {
+ 10
+}
+
+fn get_reference(n: &usize) -> &usize {
+ n
+}
+
++#[allow(clippy::double_parens)]
+#[allow(unused_variables, unused_parens)]
+fn main() {
+ let a = 10;
+ let aref = &a;
+
+ let b = a;
+
+ let b = get_number();
+
+ let b = *get_reference(&a);
+
+ let bytes: Vec<usize> = vec![1, 2, 3, 4];
+ let b = bytes[1..2][0];
+
+ //This produces a suggestion of 'let b = (a);' which
+ //will trigger the 'unused_parens' lint
+ let b = (a);
+
+ let b = a;
+
+ #[rustfmt::skip]
+ let b = a;
+
+ let b = &a;
+
+ let b = *aref;
+}
+
+#[rustfmt::skip]
+macro_rules! m {
+ ($visitor: expr) => {
+ $visitor
+ };
+}
+
+#[rustfmt::skip]
+macro_rules! m_mut {
+ ($visitor: expr) => {
+ $visitor
+ };
+}
+
+pub struct S;
+impl S {
+ pub fn f(&self) -> &Self {
+ m!(self)
+ }
+ pub fn f_mut(&self) -> &Self {
+ m_mut!(self)
+ }
+}
--- /dev/null
- #[allow(clippy::many_single_char_names, clippy::double_parens)]
+// run-rustfix
+#![warn(clippy::deref_addrof)]
+
+fn get_number() -> usize {
+ 10
+}
+
+fn get_reference(n: &usize) -> &usize {
+ n
+}
+
++#[allow(clippy::double_parens)]
+#[allow(unused_variables, unused_parens)]
+fn main() {
+ let a = 10;
+ let aref = &a;
+
+ let b = *&a;
+
+ let b = *&get_number();
+
+ let b = *get_reference(&a);
+
+ let bytes: Vec<usize> = vec![1, 2, 3, 4];
+ let b = *&bytes[1..2][0];
+
+ //This produces a suggestion of 'let b = (a);' which
+ //will trigger the 'unused_parens' lint
+ let b = *&(a);
+
+ let b = *(&a);
+
+ #[rustfmt::skip]
+ let b = *((&a));
+
+ let b = *&&a;
+
+ let b = **&aref;
+}
+
+#[rustfmt::skip]
+macro_rules! m {
+ ($visitor: expr) => {
+ *& $visitor
+ };
+}
+
+#[rustfmt::skip]
+macro_rules! m_mut {
+ ($visitor: expr) => {
+ *& mut $visitor
+ };
+}
+
+pub struct S;
+impl S {
+ pub fn f(&self) -> &Self {
+ m!(self)
+ }
+ pub fn f_mut(&self) -> &Self {
+ m_mut!(self)
+ }
+}
--- /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 }
++ }
++}
++
+fn main() {}
--- /dev/null
- #[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)]
+// does not test any rustfixable lints
+
+#[rustfmt::skip]
+#[warn(clippy::eq_op)]
++#[allow(clippy::identity_op, clippy::double_parens)]
+#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
+#[allow(clippy::nonminimal_bool)]
+#[allow(unused)]
+#[allow(clippy::unnecessary_cast)]
+fn main() {
+ // simple values and comparisons
+ 1 == 1;
+ "no" == "no";
+ // even though I agree that no means no ;-)
+ false != false;
+ 1.5 < 1.5;
+ 1u64 >= 1u64;
+
+ // casts, methods, parentheses
+ (1 as u64) & (1 as u64);
+ 1 ^ ((((((1))))));
+
+ // unary and binary operators
+ (-(2) < -(2));
+ ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
+
+ // various other things
+ ([1] != [1]);
+ ((1, 2) != (1, 2));
+ vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros
+
+ // const folding
+ 1 + 1 == 2;
+ 1 - 1 == 0;
+
+ 1 - 1;
+ 1 / 1;
+ true && true;
+
+ true || true;
+
+
+ let a: u32 = 0;
+ let b: u32 = 0;
+
+ a == b && b == a;
+ a != b && b != a;
+ a < b && b > a;
+ a <= b && b >= a;
+
+ let mut a = vec![1];
+ a == a;
+ 2*a.len() == 2*a.len(); // ok, functions
+ a.pop() == a.pop(); // ok, functions
+
+ check_ignore_macro();
+
+ // named constants
+ const A: u32 = 10;
+ const B: u32 = 10;
+ const C: u32 = A / B; // ok, different named constants
+ const D: u32 = A / A;
+}
+
+#[rustfmt::skip]
+macro_rules! check_if_named_foo {
+ ($expression:expr) => (
+ if stringify!($expression) == "foo" {
+ println!("foo!");
+ } else {
+ println!("not foo.");
+ }
+ )
+}
+
+macro_rules! bool_macro {
+ ($expression:expr) => {
+ true
+ };
+}
+
+#[allow(clippy::short_circuit_statement)]
+fn check_ignore_macro() {
+ check_if_named_foo!(foo);
+ // checks if the lint ignores macros with `!` operator
+ !bool_macro!(1) && !bool_macro!("");
+}
+
+struct Nested {
+ inner: ((i32,), (i32,), (i32,)),
+}
+
+fn check_nested(n1: &Nested, n2: &Nested) -> bool {
+ // `n2.inner.0.0` mistyped as `n1.inner.0.0`
+ (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
+}
--- /dev/null
- clippy::many_single_char_names,
+// run-rustfix
+
+#![allow(
+ unused,
+ clippy::no_effect,
+ clippy::redundant_closure_call,
- use std::path::PathBuf;
+ clippy::needless_pass_by_value,
+ clippy::option_map_unit_fn
+)]
+#![warn(
+ clippy::redundant_closure,
+ clippy::redundant_closure_for_method_calls,
+ clippy::needless_borrow
+)]
+
- meta(foo);
++use std::path::{Path, PathBuf};
+
+macro_rules! mac {
+ () => {
+ foobar()
+ };
+}
+
+macro_rules! closure_mac {
+ () => {
+ |n| foo(n)
+ };
+}
+
+fn main() {
+ let a = Some(1u8).map(foo);
- let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
- all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted
+ let c = Some(1u8).map(|a| {1+2; foo}(a));
+ true.then(|| mac!()); // don't lint function in macro expansion
+ Some(1).map(closure_mac!()); // don't lint closure in macro expansion
+ let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
- let e = Some(1u8).map(|a| divergent(a));
++ let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
++ all(&[1, 2, 3], &2, below); //is adjusted
+ unsafe {
+ Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
+ }
+
+ // See #815
- let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
++ let e = Some(1u8).map(divergent);
+ let e = Some(1u8).map(generic);
+ let e = Some(1u8).map(generic);
+ // See #515
+ let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
+ Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
+
+ // issue #7224
+ let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
+}
+
+trait TestTrait {
+ fn trait_foo(self) -> bool;
+ fn trait_foo_ref(&self) -> bool;
+}
+
+struct TestStruct<'a> {
+ some_ref: &'a i32,
+}
+
+impl<'a> TestStruct<'a> {
+ fn foo(self) -> bool {
+ false
+ }
+ unsafe fn foo_unsafe(self) -> bool {
+ true
+ }
+}
+
+impl<'a> TestTrait for TestStruct<'a> {
+ fn trait_foo(self) -> bool {
+ false
+ }
+ fn trait_foo_ref(&self) -> bool {
+ false
+ }
+}
+
+impl<'a> std::ops::Deref for TestStruct<'a> {
+ type Target = char;
+ fn deref(&self) -> &char {
+ &'a'
+ }
+}
+
+fn test_redundant_closures_containing_method_calls() {
+ let i = 10;
+ let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
- let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
- let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
+ let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
- let e = Some("str").map(str::to_string);
- let e = Some('a').map(char::to_uppercase);
+ let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
+ unsafe {
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
+ }
+ let e = Some("str").map(std::string::ToString::to_string);
- let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
- let p = Some(PathBuf::new());
- let e = p.as_ref().and_then(|s| s.to_str());
+ let e = Some('a').map(char::to_uppercase);
+ let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
+ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
-
- let mut some = Some(|x| x * x);
- let arr = [Ok(1), Err(2)];
- let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
++ let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
+ let c = Some(TestStruct { some_ref: &i })
+ .as_ref()
+ .map(|c| c.to_ascii_uppercase());
+
+ fn test_different_borrow_levels<T>(t: &[&T])
+ where
+ T: TestTrait,
+ {
+ t.iter().filter(|x| x.trait_foo_ref());
+ t.iter().map(|x| x.trait_foo_ref());
+ }
- fn meta<F>(f: F)
- where
- F: Fn(u8),
- {
- f(1u8)
- }
-
+}
+
+struct Thunk<T>(Box<dyn FnMut() -> T>);
+
+impl<T> Thunk<T> {
+ fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
+ let mut option = Some(f);
+ // This should not trigger redundant_closure (#1439)
+ Thunk(Box::new(move || option.take().unwrap()()))
+ }
+
+ fn unwrap(self) -> T {
+ let Thunk(mut f) = self;
+ f()
+ }
+}
+
+fn foobar() {
+ let thunk = Thunk::new(|| println!("Hello, world!"));
+ thunk.unwrap()
+}
+
- requires_fn_once(|| x());
+fn foo(_: u8) {}
+
+fn foo2(_: u8) -> u8 {
+ 1u8
+}
+
+fn all<X, F>(x: &[X], y: &X, f: F) -> bool
+where
+ F: Fn(&X, &X) -> bool,
+{
+ x.iter().all(|e| f(e, y))
+}
+
+fn below(x: &u8, y: &u8) -> bool {
+ x < y
+}
+
+unsafe fn unsafe_fn(_: u8) {}
+
+fn divergent(_: u8) -> ! {
+ unimplemented!()
+}
+
+fn generic<T>(_: T) -> u8 {
+ 0
+}
+
+fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
++ requires_fn_once(x);
+}
+fn requires_fn_once<T: FnOnce()>(_: T) {}
+
+fn test_redundant_closure_with_function_pointer() {
+ type FnPtrType = fn(u8);
+ let foo_ptr: FnPtrType = foo;
+ let a = Some(1u8).map(foo_ptr);
+}
+
+fn test_redundant_closure_with_another_closure() {
+ let closure = |a| println!("{}", a);
+ let a = Some(1u8).map(closure);
+}
+
+fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
+ // Currently f is called when result of make_lazy is called.
+ // If the closure is removed, f will be called when make_lazy itself is
+ // called. This changes semantics, so the closure must stay.
+ Box::new(move |x| f()(x))
+}
+
+fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
+ f(&mut "Hello".to_owned())
+}
+fn test_difference_in_mutability() {
+ call(|s| s.clone());
+}
+
+struct Bar;
+impl std::ops::Deref for Bar {
+ type Target = str;
+ fn deref(&self) -> &str {
+ "hi"
+ }
+}
+
+fn test_deref_with_trait_method() {
+ let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
+}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(&mut add_to_res);
+ y.into_iter().for_each(&mut add_to_res);
+ z.into_iter().for_each(add_to_res);
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(&mut closure);
+ }
+}
++
++fn late_bound_lifetimes() {
++ fn take_asref_path<P: AsRef<Path>>(path: P) {}
++
++ fn map_str<F>(thunk: F)
++ where
++ F: FnOnce(&str),
++ {
++ }
++
++ fn map_str_to_path<F>(thunk: F)
++ where
++ F: FnOnce(&str) -> &Path,
++ {
++ }
++ map_str(|s| take_asref_path(s));
++ map_str_to_path(std::convert::AsRef::as_ref);
++}
++
++mod type_param_bound {
++ trait Trait {
++ fn fun();
++ }
++
++ fn take<T: 'static>(_: T) {}
++
++ fn test<X: Trait>() {
++ // don't lint, but it's questionable that rust requires a cast
++ take(|| X::fun());
++ take(X::fun as fn());
++ }
++}
--- /dev/null
- clippy::many_single_char_names,
+// run-rustfix
+
+#![allow(
+ unused,
+ clippy::no_effect,
+ clippy::redundant_closure_call,
- use std::path::PathBuf;
+ clippy::needless_pass_by_value,
+ clippy::option_map_unit_fn
+)]
+#![warn(
+ clippy::redundant_closure,
+ clippy::redundant_closure_for_method_calls,
+ clippy::needless_borrow
+)]
+
- meta(|a| foo(a));
++use std::path::{Path, PathBuf};
+
+macro_rules! mac {
+ () => {
+ foobar()
+ };
+}
+
+macro_rules! closure_mac {
+ () => {
+ |n| foo(n)
+ };
+}
+
+fn main() {
+ let a = Some(1u8).map(|a| foo(a));
- let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
+ let c = Some(1u8).map(|a| {1+2; foo}(a));
+ true.then(|| mac!()); // don't lint function in macro expansion
+ Some(1).map(closure_mac!()); // don't lint closure in macro expansion
+ let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
+ let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
+ all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
+ unsafe {
+ Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
+ }
+
+ // See #815
+ let e = Some(1u8).map(|a| divergent(a));
+ let e = Some(1u8).map(|a| generic(a));
+ let e = Some(1u8).map(generic);
+ // See #515
+ let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
+ Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
+
+ // issue #7224
+ let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
+}
+
+trait TestTrait {
+ fn trait_foo(self) -> bool;
+ fn trait_foo_ref(&self) -> bool;
+}
+
+struct TestStruct<'a> {
+ some_ref: &'a i32,
+}
+
+impl<'a> TestStruct<'a> {
+ fn foo(self) -> bool {
+ false
+ }
+ unsafe fn foo_unsafe(self) -> bool {
+ true
+ }
+}
+
+impl<'a> TestTrait for TestStruct<'a> {
+ fn trait_foo(self) -> bool {
+ false
+ }
+ fn trait_foo_ref(&self) -> bool {
+ false
+ }
+}
+
+impl<'a> std::ops::Deref for TestStruct<'a> {
+ type Target = char;
+ fn deref(&self) -> &char {
+ &'a'
+ }
+}
+
+fn test_redundant_closures_containing_method_calls() {
+ let i = 10;
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
- let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
- let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
+ let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
- let e = Some("str").map(str::to_string);
+ unsafe {
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
+ }
+ let e = Some("str").map(|s| s.to_string());
- let e = Some('a').map(char::to_uppercase);
+ let e = Some('a').map(|s| s.to_uppercase());
- let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
- let p = Some(PathBuf::new());
- let e = p.as_ref().and_then(|s| s.to_str());
+ let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
+ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
-
- let mut some = Some(|x| x * x);
- let arr = [Ok(1), Err(2)];
- let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
++ let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
+ let c = Some(TestStruct { some_ref: &i })
+ .as_ref()
+ .map(|c| c.to_ascii_uppercase());
+
+ fn test_different_borrow_levels<T>(t: &[&T])
+ where
+ T: TestTrait,
+ {
+ t.iter().filter(|x| x.trait_foo_ref());
+ t.iter().map(|x| x.trait_foo_ref());
+ }
- fn meta<F>(f: F)
- where
- F: Fn(u8),
- {
- f(1u8)
- }
-
+}
+
+struct Thunk<T>(Box<dyn FnMut() -> T>);
+
+impl<T> Thunk<T> {
+ fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
+ let mut option = Some(f);
+ // This should not trigger redundant_closure (#1439)
+ Thunk(Box::new(move || option.take().unwrap()()))
+ }
+
+ fn unwrap(self) -> T {
+ let Thunk(mut f) = self;
+ f()
+ }
+}
+
+fn foobar() {
+ let thunk = Thunk::new(|| println!("Hello, world!"));
+ thunk.unwrap()
+}
+
+fn foo(_: u8) {}
+
+fn foo2(_: u8) -> u8 {
+ 1u8
+}
+
+fn all<X, F>(x: &[X], y: &X, f: F) -> bool
+where
+ F: Fn(&X, &X) -> bool,
+{
+ x.iter().all(|e| f(e, y))
+}
+
+fn below(x: &u8, y: &u8) -> bool {
+ x < y
+}
+
+unsafe fn unsafe_fn(_: u8) {}
+
+fn divergent(_: u8) -> ! {
+ unimplemented!()
+}
+
+fn generic<T>(_: T) -> u8 {
+ 0
+}
+
+fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
+ requires_fn_once(|| x());
+}
+fn requires_fn_once<T: FnOnce()>(_: T) {}
+
+fn test_redundant_closure_with_function_pointer() {
+ type FnPtrType = fn(u8);
+ let foo_ptr: FnPtrType = foo;
+ let a = Some(1u8).map(|a| foo_ptr(a));
+}
+
+fn test_redundant_closure_with_another_closure() {
+ let closure = |a| println!("{}", a);
+ let a = Some(1u8).map(|a| closure(a));
+}
+
+fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
+ // Currently f is called when result of make_lazy is called.
+ // If the closure is removed, f will be called when make_lazy itself is
+ // called. This changes semantics, so the closure must stay.
+ Box::new(move |x| f()(x))
+}
+
+fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
+ f(&mut "Hello".to_owned())
+}
+fn test_difference_in_mutability() {
+ call(|s| s.clone());
+}
+
+struct Bar;
+impl std::ops::Deref for Bar {
+ type Target = str;
+ fn deref(&self) -> &str {
+ "hi"
+ }
+}
+
+fn test_deref_with_trait_method() {
+ let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
+}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(|x| add_to_res(x));
+ y.into_iter().for_each(|x| add_to_res(x));
+ z.into_iter().for_each(|x| add_to_res(x));
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(|n| closure(n));
+ }
+}
++
++fn late_bound_lifetimes() {
++ fn take_asref_path<P: AsRef<Path>>(path: P) {}
++
++ fn map_str<F>(thunk: F)
++ where
++ F: FnOnce(&str),
++ {
++ }
++
++ fn map_str_to_path<F>(thunk: F)
++ where
++ F: FnOnce(&str) -> &Path,
++ {
++ }
++ map_str(|s| take_asref_path(s));
++ map_str_to_path(|s| s.as_ref());
++}
++
++mod type_param_bound {
++ trait Trait {
++ fn fun();
++ }
++
++ fn take<T: 'static>(_: T) {}
++
++ fn test<X: Trait>() {
++ // don't lint, but it's questionable that rust requires a cast
++ take(|| X::fun());
++ take(X::fun as fn());
++ }
++}
--- /dev/null
- --> $DIR/eta.rs:32:27
+error: redundant closure
- --> $DIR/eta.rs:33:10
++ --> $DIR/eta.rs:31:27
+ |
+LL | let a = Some(1u8).map(|a| foo(a));
+ | ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
+ |
+ = note: `-D clippy::redundant-closure` implied by `-D warnings`
+
+error: redundant closure
- LL | meta(|a| foo(a));
- | ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
++ --> $DIR/eta.rs:35:40
+ |
- --> $DIR/eta.rs:37:40
++LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
++ | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
+
+error: redundant closure
- LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
- | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
++ --> $DIR/eta.rs:36:35
+ |
- --> $DIR/eta.rs:39:21
++LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
++ | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
+
+error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler
- --> $DIR/eta.rs:46:27
++ --> $DIR/eta.rs:37:21
+ |
+LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
+ | ^^^ help: change this to: `&2`
+ |
+ = note: `-D clippy::needless-borrow` implied by `-D warnings`
+
+error: redundant closure
- --> $DIR/eta.rs:92:51
++ --> $DIR/eta.rs:37:26
++ |
++LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
++ | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
++
++error: redundant closure
++ --> $DIR/eta.rs:43:27
++ |
++LL | let e = Some(1u8).map(|a| divergent(a));
++ | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
++
++error: redundant closure
++ --> $DIR/eta.rs:44:27
+ |
+LL | let e = Some(1u8).map(|a| generic(a));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
+
+error: redundant closure
- --> $DIR/eta.rs:94:51
++ --> $DIR/eta.rs:90:51
+ |
+LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
+ | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
+ |
+ = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
+
+error: redundant closure
- --> $DIR/eta.rs:97:42
++ --> $DIR/eta.rs:91:51
+ |
+LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
+
+error: redundant closure
- --> $DIR/eta.rs:102:29
++ --> $DIR/eta.rs:93:42
+ |
+LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
+ | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
+
+error: redundant closure
- --> $DIR/eta.rs:104:27
++ --> $DIR/eta.rs:97:29
+ |
+LL | let e = Some("str").map(|s| s.to_string());
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
+
+error: redundant closure
- --> $DIR/eta.rs:107:65
++ --> $DIR/eta.rs:98:27
+ |
+LL | let e = Some('a').map(|s| s.to_uppercase());
+ | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
+
+error: redundant closure
- --> $DIR/eta.rs:190:27
++ --> $DIR/eta.rs:100:65
+ |
+LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
+
+error: redundant closure
- --> $DIR/eta.rs:195:27
++ --> $DIR/eta.rs:163:22
++ |
++LL | requires_fn_once(|| x());
++ | ^^^^^^ help: replace the closure with the function itself: `x`
++
++error: redundant closure
++ --> $DIR/eta.rs:170:27
+ |
+LL | let a = Some(1u8).map(|a| foo_ptr(a));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
+
+error: redundant closure
- --> $DIR/eta.rs:227:28
++ --> $DIR/eta.rs:175:27
+ |
+LL | let a = Some(1u8).map(|a| closure(a));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
+
+error: redundant closure
- --> $DIR/eta.rs:228:28
++ --> $DIR/eta.rs:207:28
+ |
+LL | x.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
- --> $DIR/eta.rs:229:28
++ --> $DIR/eta.rs:208:28
+ |
+LL | y.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
- --> $DIR/eta.rs:236:21
++ --> $DIR/eta.rs:209:28
+ |
+LL | z.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
+
+error: redundant closure
- error: aborting due to 17 previous errors
++ --> $DIR/eta.rs:216:21
+ |
+LL | Some(1).map(|n| closure(n));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
+
++error: redundant closure
++ --> $DIR/eta.rs:235:21
++ |
++LL | map_str_to_path(|s| s.as_ref());
++ | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
++
++error: aborting due to 21 previous errors
+
--- /dev/null
- clippy::many_single_char_names,
+// edition:2018
+
+#[warn(clippy::eval_order_dependence)]
+#[allow(
+ unused_assignments,
+ unused_variables,
+ clippy::no_effect,
+ dead_code,
+ clippy::blacklisted_name
+)]
+fn main() {
+ let mut x = 0;
+ let a = {
+ x = 1;
+ 1
+ } + x;
+
+ // Example from iss#277
+ x += {
+ x = 20;
+ 2
+ };
+
+ // Does it work in weird places?
+ // ...in the base for a struct expression?
+ struct Foo {
+ a: i32,
+ b: i32,
+ };
+ let base = Foo { a: 4, b: 5 };
+ let foo = Foo {
+ a: x,
+ ..{
+ x = 6;
+ base
+ }
+ };
+ // ...inside a closure?
+ let closure = || {
+ let mut x = 0;
+ x += {
+ x = 20;
+ 2
+ };
+ };
+ // ...not across a closure?
+ let mut y = 0;
+ let b = (y, || y = 1);
+
+ // && and || evaluate left-to-right.
+ let a = {
+ x = 1;
+ true
+ } && (x == 3);
+ let a = {
+ x = 1;
+ true
+ } || (x == 3);
+
+ // Make sure we don't get confused by alpha conversion.
+ let a = {
+ let mut x = 1;
+ x = 2;
+ 1
+ } + x;
+
+ // No warning if we don't read the variable...
+ x = {
+ x = 20;
+ 2
+ };
+ // ...if the assignment is in a closure...
+ let b = {
+ || {
+ x = 1;
+ };
+ 1
+ } + x;
+ // ... or the access is under an address.
+ let b = (
+ {
+ let p = &x;
+ 1
+ },
+ {
+ x = 1;
+ x
+ },
+ );
+
+ // Limitation: l-values other than simple variables don't trigger
+ // the warning.
+ let mut tup = (0, 0);
+ let c = {
+ tup.0 = 1;
+ 1
+ } + tup.0;
+ // Limitation: you can get away with a read under address-of.
+ let mut z = 0;
+ let b = (
+ &{
+ z = x;
+ x
+ },
+ {
+ x = 3;
+ x
+ },
+ );
+}
+
+async fn issue_6925() {
+ let _ = vec![async { true }.await, async { false }.await];
+}
--- /dev/null
- --> $DIR/eval_order_dependence.rs:17:9
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:15:9
++ --> $DIR/eval_order_dependence.rs:16:9
+ |
+LL | } + x;
+ | ^
+ |
+ = note: `-D clippy::eval-order-dependence` implied by `-D warnings`
+note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:20:5
++ --> $DIR/eval_order_dependence.rs:14:9
+ |
+LL | x = 1;
+ | ^^^^^
+
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:21:9
++ --> $DIR/eval_order_dependence.rs:19:5
+ |
+LL | x += {
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:33:12
++ --> $DIR/eval_order_dependence.rs:20:9
+ |
+LL | x = 20;
+ | ^^^^^^
+
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:35:13
++ --> $DIR/eval_order_dependence.rs:32:12
+ |
+LL | a: x,
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:42:9
++ --> $DIR/eval_order_dependence.rs:34:13
+ |
+LL | x = 6;
+ | ^^^^^
+
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:43:13
++ --> $DIR/eval_order_dependence.rs:41:9
+ |
+LL | x += {
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
++ --> $DIR/eval_order_dependence.rs:42:13
+ |
+LL | x = 20;
+ | ^^^^^^
+
+error: aborting due to 4 previous errors
+
--- /dev/null
- const BAD64_1: f64 = 0.123_456_789_012_345_66_f64;
- const BAD64_2: f64 = 0.123_456_789_012_345_66;
+// 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;
+
- let bad64: f64 = 0.123_456_789_012_345_66;
- let bad64_suf: f64 = 0.123_456_789_012_345_66_f64;
- let bad64_inf = 0.123_456_789_012_345_66;
++ 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;
+}
--- /dev/null
- error: float has excessive precision
- --> $DIR/excessive_precision.rs:20:26
- |
- LL | const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
-
- error: float has excessive precision
- --> $DIR/excessive_precision.rs:21:26
- |
- LL | const BAD64_2: f64 = 0.123_456_789_012_345_67;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
+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:40:22
- |
- LL | let bad64: f64 = 0.123_456_789_012_345_67;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
- error: float has excessive precision
- --> $DIR/excessive_precision.rs:41:26
- |
- LL | let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
-
- error: float has excessive precision
- --> $DIR/excessive_precision.rs:42:21
- |
- LL | let bad64_inf = 0.123_456_789_012_345_67;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
+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: aborting due to 18 previous errors
+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: aborting due to 13 previous errors
+
--- /dev/null
- #![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+// run-rustfix
+
++#![allow(unused_variables, clippy::clone_double_ref)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+ format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+ deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Vec<u8> {
+ &self.0
+ }
+}
+
+fn main() {
+ let a: &mut String = &mut String::from("foo");
+
+ // these should require linting
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut **a;
+
+ // both derefs should get linted here
+ let b: String = format!("{}, {}", &*a, &*a);
+
+ println!("{}", &*a);
+
+ #[allow(clippy::match_single_binding)]
+ match &*a {
+ _ => (),
+ }
+
+ let b: String = concat(&*a);
+
+ let b = just_return(a);
+
+ let b: String = concat(just_return(a));
+
+ let b: &str = &**a;
+
+ let opt_a = Some(a.clone());
+ let b = &*opt_a.unwrap();
+
+ // following should not require linting
+
+ let cv = CustomVec(vec![0, 42]);
+ let c = cv.deref()[0];
+
+ let b: &str = &*a.deref();
+
+ let b: String = a.deref().clone();
+
+ let b: usize = a.deref_mut().len();
+
+ let b: &usize = &a.deref().len();
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut *a;
+
+ macro_rules! expr_deref {
+ ($body:expr) => {
+ $body.deref()
+ };
+ }
+ let b: &str = expr_deref!(a);
+
+ let b: &str = expr_deref!(&*a);
+
+ // The struct does not implement Deref trait
+ #[derive(Copy, Clone)]
+ struct NoLint(u32);
+ impl NoLint {
+ pub fn deref(self) -> u32 {
+ self.0
+ }
+ pub fn deref_mut(self) -> u32 {
+ self.0
+ }
+ }
+ let no_lint = NoLint(42);
+ let b = no_lint.deref();
+ let b = no_lint.deref_mut();
+}
--- /dev/null
- #![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+// run-rustfix
+
++#![allow(unused_variables, clippy::clone_double_ref)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+ format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+ deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Vec<u8> {
+ &self.0
+ }
+}
+
+fn main() {
+ let a: &mut String = &mut String::from("foo");
+
+ // these should require linting
+
+ let b: &str = a.deref();
+
+ let b: &mut str = a.deref_mut();
+
+ // both derefs should get linted here
+ let b: String = format!("{}, {}", a.deref(), a.deref());
+
+ println!("{}", a.deref());
+
+ #[allow(clippy::match_single_binding)]
+ match a.deref() {
+ _ => (),
+ }
+
+ let b: String = concat(a.deref());
+
+ let b = just_return(a).deref();
+
+ let b: String = concat(just_return(a).deref());
+
+ let b: &str = a.deref().deref();
+
+ let opt_a = Some(a.clone());
+ let b = opt_a.unwrap().deref();
+
+ // following should not require linting
+
+ let cv = CustomVec(vec![0, 42]);
+ let c = cv.deref()[0];
+
+ let b: &str = &*a.deref();
+
+ let b: String = a.deref().clone();
+
+ let b: usize = a.deref_mut().len();
+
+ let b: &usize = &a.deref().len();
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut *a;
+
+ macro_rules! expr_deref {
+ ($body:expr) => {
+ $body.deref()
+ };
+ }
+ let b: &str = expr_deref!(a);
+
+ let b: &str = expr_deref!(a.deref());
+
+ // The struct does not implement Deref trait
+ #[derive(Copy, Clone)]
+ struct NoLint(u32);
+ impl NoLint {
+ pub fn deref(self) -> u32 {
+ self.0
+ }
+ pub fn deref_mut(self) -> u32 {
+ self.0
+ }
+ }
+ let no_lint = NoLint(42);
+ let b = no_lint.deref();
+ let b = no_lint.deref_mut();
+}
--- /dev/null
+#![deny(clippy::fallible_impl_from)]
++#![allow(clippy::if_then_panic)]
+
+// docs example
+struct Foo(i32);
+impl From<String> for Foo {
+ fn from(s: String) -> Self {
+ Foo(s.parse().unwrap())
+ }
+}
+
+struct Valid(Vec<u8>);
+
+impl<'a> From<&'a str> for Valid {
+ fn from(s: &'a str) -> Valid {
+ Valid(s.to_owned().into_bytes())
+ }
+}
+impl From<usize> for Valid {
+ fn from(i: usize) -> Valid {
+ Valid(Vec::with_capacity(i))
+ }
+}
+
+struct Invalid;
+
+impl From<usize> for Invalid {
+ fn from(i: usize) -> Invalid {
+ if i != 42 {
+ panic!();
+ }
+ Invalid
+ }
+}
+
+impl From<Option<String>> for Invalid {
+ fn from(s: Option<String>) -> Invalid {
+ let s = s.unwrap();
+ if !s.is_empty() {
+ panic!("42");
+ } else if s.parse::<u32>().unwrap() != 42 {
+ panic!("{:?}", s);
+ }
+ Invalid
+ }
+}
+
+trait ProjStrTrait {
+ type ProjString;
+}
+impl<T> ProjStrTrait for Box<T> {
+ type ProjString = String;
+}
+impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
+ fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
+ if s.parse::<u32>().ok().unwrap() != 42 {
+ panic!("{:?}", s);
+ }
+ Invalid
+ }
+}
+
+struct Unreachable;
+
+impl From<String> for Unreachable {
+ fn from(s: String) -> Unreachable {
+ if s.is_empty() {
+ return Unreachable;
+ }
+ match s.chars().next() {
+ Some(_) => Unreachable,
+ None => unreachable!(), // do not lint the unreachable macro
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/fallible_impl_from.rs:5:1
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:7:13
++ --> $DIR/fallible_impl_from.rs:6:1
+ |
+LL | / impl From<String> for Foo {
+LL | | fn from(s: String) -> Self {
+LL | | Foo(s.parse().unwrap())
+LL | | }
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/fallible_impl_from.rs:1:9
+ |
+LL | #![deny(clippy::fallible_impl_from)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
- --> $DIR/fallible_impl_from.rs:26:1
++ --> $DIR/fallible_impl_from.rs:8:13
+ |
+LL | Foo(s.parse().unwrap())
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:29:13
++ --> $DIR/fallible_impl_from.rs:27:1
+ |
+LL | / impl From<usize> for Invalid {
+LL | | fn from(i: usize) -> Invalid {
+LL | | if i != 42 {
+LL | | panic!();
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
- --> $DIR/fallible_impl_from.rs:35:1
++ --> $DIR/fallible_impl_from.rs:30:13
+ |
+LL | panic!();
+ | ^^^^^^^^^
+ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:37:17
++ --> $DIR/fallible_impl_from.rs:36:1
+ |
+LL | / impl From<Option<String>> for Invalid {
+LL | | fn from(s: Option<String>) -> Invalid {
+LL | | let s = s.unwrap();
+LL | | if !s.is_empty() {
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
- --> $DIR/fallible_impl_from.rs:53:1
++ --> $DIR/fallible_impl_from.rs:38:17
+ |
+LL | let s = s.unwrap();
+ | ^^^^^^^^^^
+LL | if !s.is_empty() {
+LL | panic!("42");
+ | ^^^^^^^^^^^^^
+LL | } else if s.parse::<u32>().unwrap() != 42 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | panic!("{:?}", s);
+ | ^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:55:12
++ --> $DIR/fallible_impl_from.rs:54:1
+ |
+LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
+LL | | fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
+LL | | if s.parse::<u32>().ok().unwrap() != 42 {
+LL | | panic!("{:?}", s);
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
++ --> $DIR/fallible_impl_from.rs:56:12
+ |
+LL | if s.parse::<u32>().ok().unwrap() != 42 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | panic!("{:?}", s);
+ | ^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
--- /dev/null
- clippy::cast_lossless,
- clippy::many_single_char_names
+#![warn(clippy::float_cmp)]
+#![allow(
+ unused,
+ clippy::no_effect,
+ clippy::op_ref,
+ clippy::unnecessary_operation,
++ clippy::cast_lossless
+)]
+
+use std::ops::Add;
+
+const ZERO: f32 = 0.0;
+const ONE: f32 = ZERO + 1.0;
+
+fn twice<T>(x: T) -> T
+where
+ T: Add<T, Output = T> + Copy,
+{
+ x + x
+}
+
+fn eq_fl(x: f32, y: f32) -> bool {
+ if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn
+}
+
+fn fl_eq(x: f32, y: f32) -> bool {
+ if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn
+}
+
+struct X {
+ val: f32,
+}
+
+impl PartialEq for X {
+ fn eq(&self, o: &X) -> bool {
+ if self.val.is_nan() {
+ o.val.is_nan()
+ } else {
+ self.val == o.val // no error, inside "eq" fn
+ }
+ }
+}
+
+fn main() {
+ ZERO == 0f32; //no error, comparison with zero is ok
+ 1.0f32 != f32::INFINITY; // also comparison with infinity
+ 1.0f32 != f32::NEG_INFINITY; // and negative infinity
+ ZERO == 0.0; //no error, comparison with zero is ok
+ ZERO + ZERO != 1.0; //no error, comparison with zero is ok
+
+ ONE == 1f32;
+ ONE == 1.0 + 0.0;
+ ONE + ONE == ZERO + ONE + ONE;
+ ONE != 2.0;
+ ONE != 0.0; // no error, comparison with zero is ok
+ twice(ONE) != ONE;
+ ONE as f64 != 2.0;
+ ONE as f64 != 0.0; // no error, comparison with zero is ok
+
+ let x: f64 = 1.0;
+
+ x == 1.0;
+ x != 0f64; // no error, comparison with zero is ok
+
+ twice(x) != twice(ONE as f64);
+
+ x < 0.0; // no errors, lower or greater comparisons need no fuzzyness
+ x > 0.0;
+ x <= 0.0;
+ x >= 0.0;
+
+ let xs: [f32; 1] = [0.0];
+ let a: *const f32 = xs.as_ptr();
+ let b: *const f32 = xs.as_ptr();
+
+ assert_eq!(a, b); // no errors
+
+ const ZERO_ARRAY: [f32; 2] = [0.0, 0.0];
+ const NON_ZERO_ARRAY: [f32; 2] = [0.0, 0.1];
+
+ let i = 0;
+ let j = 1;
+
+ ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i
+ NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
+
+ let a1: [f32; 1] = [0.0];
+ let a2: [f32; 1] = [1.1];
+
+ a1 == a2;
+ a1[0] == a2[0];
+
+ // no errors - comparing signums is ok
+ let x32 = 3.21f32;
+ 1.23f32.signum() == x32.signum();
+ 1.23f32.signum() == -(x32.signum());
+ 1.23f32.signum() == 3.21f32.signum();
+
+ 1.23f32.signum() != x32.signum();
+ 1.23f32.signum() != -(x32.signum());
+ 1.23f32.signum() != 3.21f32.signum();
+
+ let x64 = 3.21f64;
+ 1.23f64.signum() == x64.signum();
+ 1.23f64.signum() == -(x64.signum());
+ 1.23f64.signum() == 3.21f64.signum();
+
+ 1.23f64.signum() != x64.signum();
+ 1.23f64.signum() != -(x64.signum());
+ 1.23f64.signum() != 3.21f64.signum();
+
+ // the comparison should also look through references
+ &0.0 == &ZERO;
+ &&&&0.0 == &&&&ZERO;
+}
--- /dev/null
- --> $DIR/float_cmp.rs:58:5
+error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:63:5
++ --> $DIR/float_cmp.rs:57:5
+ |
+LL | ONE as f64 != 2.0;
+ | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
+ |
+ = note: `-D clippy::float-cmp` implied by `-D warnings`
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:66:5
++ --> $DIR/float_cmp.rs:62:5
+ |
+LL | x == 1.0;
+ | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:86:5
++ --> $DIR/float_cmp.rs:65:5
+ |
+LL | twice(x) != twice(ONE as f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:91:5
++ --> $DIR/float_cmp.rs:85:5
+ |
+LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` arrays
- --> $DIR/float_cmp.rs:92:5
++ --> $DIR/float_cmp.rs:90:5
+ |
+LL | a1 == a2;
+ | ^^^^^^^^
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
++ --> $DIR/float_cmp.rs:91:5
+ |
+LL | a1[0] == a2[0];
+ | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: aborting due to 6 previous errors
+
--- /dev/null
- #[allow(clippy::many_single_char_names, unused_variables)]
+// 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::shadow_unrelated,
+ 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::many_single_char_names, unused_variables)]
+// 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::shadow_unrelated,
+ 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
--- /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");
++ }
++}
--- /dev/null
--- /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");
++ }
++}
--- /dev/null
--- /dev/null
++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: aborting due to 2 previous errors
++
--- /dev/null
- #[allow(clippy::many_single_char_names)]
+#![allow(clippy::blocks_in_if_conditions)]
+
+fn fn_val(i: i32) -> i32 {
+ unimplemented!()
+}
+fn fn_constref(i: &i32) -> i32 {
+ unimplemented!()
+}
+fn fn_mutref(i: &mut i32) {
+ unimplemented!()
+}
+fn fooi() -> i32 {
+ unimplemented!()
+}
+fn foob() -> bool {
+ unimplemented!()
+}
+
+fn immutable_condition() {
+ // Should warn when all vars mentioned are immutable
+ let y = 0;
+ while y < 10 {
+ println!("KO - y is immutable");
+ }
+
+ let x = 0;
+ while y < 10 && x < 3 {
+ let mut k = 1;
+ k += 2;
+ println!("KO - x and y immutable");
+ }
+
+ let cond = false;
+ while !cond {
+ println!("KO - cond immutable");
+ }
+
+ let mut i = 0;
+ while y < 10 && i < 3 {
+ i += 1;
+ println!("OK - i is mutable");
+ }
+
+ let mut mut_cond = false;
+ while !mut_cond || cond {
+ mut_cond = true;
+ println!("OK - mut_cond is mutable");
+ }
+
+ while fooi() < x {
+ println!("OK - Fn call results may vary");
+ }
+
+ while foob() {
+ println!("OK - Fn call results may vary");
+ }
+
+ let mut a = 0;
+ let mut c = move || {
+ while a < 5 {
+ a += 1;
+ println!("OK - a is mutable");
+ }
+ };
+ c();
+
+ let mut tup = (0, 0);
+ while tup.0 < 5 {
+ tup.0 += 1;
+ println!("OK - tup.0 gets mutated")
+ }
+}
+
+fn unused_var() {
+ // Should warn when a (mutable) var is not used in while body
+ let (mut i, mut j) = (0, 0);
+
+ while i < 3 {
+ j = 3;
+ println!("KO - i not mentioned");
+ }
+
+ while i < 3 && j > 0 {
+ println!("KO - i and j not mentioned");
+ }
+
+ while i < 3 {
+ let mut i = 5;
+ fn_mutref(&mut i);
+ println!("KO - shadowed");
+ }
+
+ while i < 3 && j > 0 {
+ i = 5;
+ println!("OK - i in cond and mentioned");
+ }
+}
+
+fn used_immutable() {
+ let mut i = 0;
+
+ while i < 3 {
+ fn_constref(&i);
+ println!("KO - const reference");
+ }
+
+ while i < 3 {
+ fn_val(i);
+ println!("KO - passed by value");
+ }
+
+ while i < 3 {
+ println!("OK - passed by mutable reference");
+ fn_mutref(&mut i)
+ }
+
+ while i < 3 {
+ fn_mutref(&mut i);
+ println!("OK - passed by mutable reference");
+ }
+}
+
+const N: i32 = 5;
+const B: bool = false;
+
+fn consts() {
+ while false {
+ println!("Constants are not linted");
+ }
+
+ while B {
+ println!("Constants are not linted");
+ }
+
+ while N > 0 {
+ println!("Constants are not linted");
+ }
+}
+
+use std::cell::Cell;
+
+fn maybe_i_mutate(i: &Cell<bool>) {
+ unimplemented!()
+}
+
+fn internally_mutable() {
+ let b = Cell::new(true);
+
+ while b.get() {
+ // b cannot be silently coerced to `bool`
+ maybe_i_mutate(&b);
+ println!("OK - Method call within condition");
+ }
+}
+
+struct Counter {
+ count: usize,
+}
+
+impl Counter {
+ fn inc(&mut self) {
+ self.count += 1;
+ }
+
+ fn inc_n(&mut self, n: usize) {
+ while self.count < n {
+ self.inc();
+ }
+ println!("OK - self borrowed mutably");
+ }
+
+ fn print_n(&self, n: usize) {
+ while self.count < n {
+ println!("KO - {} is not mutated", self.count);
+ }
+ }
+}
+
+fn while_loop_with_break_and_return() {
+ let y = 0;
+ while y < 10 {
+ if y == 0 {
+ break;
+ }
+ println!("KO - loop contains break");
+ }
+
+ while y < 10 {
+ if y == 0 {
+ return;
+ }
+ println!("KO - loop contains return");
+ }
+}
+
+fn immutable_condition_false_positive(mut n: u64) -> u32 {
+ let mut count = 0;
+ while {
+ n >>= 1;
+ n != 0
+ } {
+ count += 1;
+ }
+ count
+}
+
+fn main() {
+ immutable_condition();
+ unused_var();
+ used_immutable();
+ internally_mutable();
+ immutable_condition_false_positive(5);
+
+ let mut c = Counter { count: 0 };
+ c.inc_n(5);
+ c.print_n(2);
+
+ while_loop_with_break_and_return();
+}
--- /dev/null
- --> $DIR/infinite_loop.rs:23:11
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:28:11
++ --> $DIR/infinite_loop.rs:22:11
+ |
+LL | while y < 10 {
+ | ^^^^^^
+ |
+ = note: `#[deny(clippy::while_immutable_condition)]` on by default
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:35:11
++ --> $DIR/infinite_loop.rs:27:11
+ |
+LL | while y < 10 && x < 3 {
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:79:11
++ --> $DIR/infinite_loop.rs:34:11
+ |
+LL | while !cond {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:84:11
++ --> $DIR/infinite_loop.rs:78:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:88:11
++ --> $DIR/infinite_loop.rs:83:11
+ |
+LL | while i < 3 && j > 0 {
+ | ^^^^^^^^^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:103:11
++ --> $DIR/infinite_loop.rs:87:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:108:11
++ --> $DIR/infinite_loop.rs:102:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:174:15
++ --> $DIR/infinite_loop.rs:107:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:182:11
++ --> $DIR/infinite_loop.rs:173:15
+ |
+LL | while self.count < n {
+ | ^^^^^^^^^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:189:11
++ --> $DIR/infinite_loop.rs:181:11
+ |
+LL | while y < 10 {
+ | ^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+ = note: this loop contains `return`s or `break`s
+ = help: rewrite it as `if cond { loop { } }`
+
+error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:188:11
+ |
+LL | while y < 10 {
+ | ^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+ = note: this loop contains `return`s or `break`s
+ = help: rewrite it as `if cond { loop { } }`
+
+error: aborting due to 11 previous errors
+
--- /dev/null
- #![allow(clippy::many_single_char_names)]
+#![warn(clippy::inherent_to_string)]
+#![deny(clippy::inherent_to_string_shadow_display)]
+
+use std::fmt;
+
+trait FalsePositive {
+ fn to_string(&self) -> String;
+}
+
+struct A;
+struct B;
+struct C;
+struct D;
+struct E;
+struct F;
+struct G;
+
+impl A {
+ // Should be detected; emit warning
+ fn to_string(&self) -> String {
+ "A.to_string()".to_string()
+ }
+
+ // Should not be detected as it does not match the function signature
+ fn to_str(&self) -> String {
+ "A.to_str()".to_string()
+ }
+}
+
+// Should not be detected as it is a free function
+fn to_string() -> String {
+ "free to_string()".to_string()
+}
+
+impl B {
+ // Should not be detected, wrong return type
+ fn to_string(&self) -> i32 {
+ 42
+ }
+}
+
+impl C {
+ // Should be detected and emit error as C also implements Display
+ fn to_string(&self) -> String {
+ "C.to_string()".to_string()
+ }
+}
+
+impl fmt::Display for C {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "impl Display for C")
+ }
+}
+
+impl FalsePositive for D {
+ // Should not be detected, as it is a trait function
+ fn to_string(&self) -> String {
+ "impl FalsePositive for D".to_string()
+ }
+}
+
+impl E {
+ // Should not be detected, as it is not bound to an instance
+ fn to_string() -> String {
+ "E::to_string()".to_string()
+ }
+}
+
+impl F {
+ // Should not be detected, as it does not match the function signature
+ fn to_string(&self, _i: i32) -> String {
+ "F.to_string()".to_string()
+ }
+}
+
+impl G {
+ // Should not be detected, as it does not match the function signature
+ fn to_string<const _N: usize>(&self) -> String {
+ "G.to_string()".to_string()
+ }
+}
+
+fn main() {
+ let a = A;
+ a.to_string();
+ a.to_str();
+
+ to_string();
+
+ let b = B;
+ b.to_string();
+
+ let c = C;
+ C.to_string();
+
+ let d = D;
+ d.to_string();
+
+ E::to_string();
+
+ let f = F;
+ f.to_string(1);
+
+ let g = G;
+ g.to_string::<1>();
+}
--- /dev/null
- --> $DIR/inherent_to_string.rs:21:5
+error: implementation of inherent method `to_string(&self) -> String` for type `A`
- --> $DIR/inherent_to_string.rs:45:5
++ --> $DIR/inherent_to_string.rs:20:5
+ |
+LL | / fn to_string(&self) -> String {
+LL | | "A.to_string()".to_string()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::inherent-to-string` implied by `-D warnings`
+ = help: implement trait `Display` for type `A` instead
+
+error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
++ --> $DIR/inherent_to_string.rs:44:5
+ |
+LL | / fn to_string(&self) -> String {
+LL | | "C.to_string()".to_string()
+LL | | }
+ | |_____^
+ |
+note: the lint level is defined here
+ --> $DIR/inherent_to_string.rs:2:9
+ |
+LL | #![deny(clippy::inherent_to_string_shadow_display)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: remove the inherent method from type `C`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
--- /dev/null
++#![warn(clippy::iter_not_returning_iterator)]
++
++struct Data {
++ begin: u32,
++}
++
++struct Counter {
++ count: u32,
++}
++
++impl Data {
++ fn iter(&self) -> Counter {
++ todo!()
++ }
++
++ fn iter_mut(&self) -> Counter {
++ todo!()
++ }
++}
++
++struct Data2 {
++ begin: u32,
++}
++
++struct Counter2 {
++ count: u32,
++}
++
++impl Data2 {
++ fn iter(&self) -> Counter2 {
++ todo!()
++ }
++
++ fn iter_mut(&self) -> Counter2 {
++ todo!()
++ }
++}
++
++impl Iterator for Counter {
++ type Item = u32;
++
++ fn next(&mut self) -> Option<Self::Item> {
++ todo!()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this method is named `iter` but its return type does not implement `Iterator`
++ --> $DIR/iter_not_returning_iterator.rs:30:5
++ |
++LL | fn iter(&self) -> Counter2 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::iter-not-returning-iterator` implied by `-D warnings`
++
++error: this method is named `iter_mut` but its return type does not implement `Iterator`
++ --> $DIR/iter_not_returning_iterator.rs:34:5
++ |
++LL | fn iter_mut(&self) -> Counter2 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
- #![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
++#![allow(unused, clippy::diverging_sub_expression)]
+#![warn(clippy::logic_bug)]
+
+fn main() {
+ let a: bool = unimplemented!();
+ let b: bool = unimplemented!();
+ let c: bool = unimplemented!();
+ let d: bool = unimplemented!();
+ let e: bool = unimplemented!();
+ let _ = a && b || a;
+ let _ = !(a && b);
+ let _ = false && a;
+ // don't lint on cfgs
+ let _ = cfg!(you_shall_not_not_pass) && a;
+ let _ = a || !b || !c || !d || !e;
+ let _ = !(a && b || c);
+}
+
+fn equality_stuff() {
+ let a: i32 = unimplemented!();
+ let b: i32 = unimplemented!();
+ let _ = a == b && a != b;
+ let _ = a < b && a >= b;
+ let _ = a > b && a <= b;
+ let _ = a > b && a == b;
+}
--- /dev/null
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_split_once)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
+ let _ = "key=value".splitn(2, '=').nth(2);
+ let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
+ let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
+ let _ = "key=value".split_once('=').unwrap().1;
+ let _ = "key=value".split_once('=').unwrap().1;
+ let (_, _) = "key=value".split_once('=').unwrap();
+
+ let s = String::from("key=value");
+ let _ = s.split_once('=').map_or(&*s, |x| x.0);
+
+ let s = Box::<str>::from("key=value");
+ let _ = s.split_once('=').map_or(&*s, |x| x.0);
+
+ let s = &"key=value";
+ let _ = s.split_once('=').map_or(*s, |x| x.0);
+
+ fn _f(s: &str) -> Option<&str> {
+ let _ = s.split_once("key=value").map_or(s, |x| x.0);
+ let _ = s.split_once("key=value")?.1;
+ let _ = s.split_once("key=value")?.1;
+ None
+ }
+
+ // Don't lint, slices don't have `split_once`
+ let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
++
++ // `rsplitn` gives the results in the reverse order of `rsplit_once`
++ let _ = "key=value".rsplit_once('=').unwrap().1;
++ let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
++ let _ = "key=value".rsplit_once('=').map(|x| x.1);
++ let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
+}
+
+fn _msrv_1_51() {
+ #![clippy::msrv = "1.51"]
+ // `str::split_once` was stabilized in 1.16. Do not lint this
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+}
+
+fn _msrv_1_52() {
+ #![clippy::msrv = "1.52"]
+ let _ = "key=value".split_once('=').unwrap().1;
+}
--- /dev/null
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_split_once)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let _ = "key=value".splitn(2, '=').next();
+ let _ = "key=value".splitn(2, '=').nth(2);
+ let _ = "key=value".splitn(2, '=').next().unwrap();
+ let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
+ let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
+
+ let s = String::from("key=value");
+ let _ = s.splitn(2, '=').next().unwrap();
+
+ let s = Box::<str>::from("key=value");
+ let _ = s.splitn(2, '=').nth(0).unwrap();
+
+ let s = &"key=value";
+ let _ = s.splitn(2, '=').skip(0).next().unwrap();
+
+ fn _f(s: &str) -> Option<&str> {
+ let _ = s.splitn(2, "key=value").next()?;
+ let _ = s.splitn(2, "key=value").nth(1)?;
+ let _ = s.splitn(2, "key=value").skip(1).next()?;
+ None
+ }
+
+ // Don't lint, slices don't have `split_once`
+ let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
++
++ // `rsplitn` gives the results in the reverse order of `rsplit_once`
++ let _ = "key=value".rsplitn(2, '=').next().unwrap();
++ let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
++ let _ = "key=value".rsplitn(2, '=').nth(0);
++ let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+}
+
+fn _msrv_1_51() {
+ #![clippy::msrv = "1.51"]
+ // `str::split_once` was stabilized in 1.16. Do not lint this
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+}
+
+fn _msrv_1_52() {
+ #![clippy::msrv = "1.52"]
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+}
--- /dev/null
- --> $DIR/manual_split_once.rs:49:13
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:13:13
+ |
+LL | let _ = "key=value".splitn(2, '=').next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
+ |
+ = note: `-D clippy::manual-split-once` implied by `-D warnings`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:15:13
+ |
+LL | let _ = "key=value".splitn(2, '=').next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:16:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:17:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:18:13
+ |
+LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:19:18
+ |
+LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:22:13
+ |
+LL | let _ = s.splitn(2, '=').next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:25:13
+ |
+LL | let _ = s.splitn(2, '=').nth(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:28:13
+ |
+LL | let _ = s.splitn(2, '=').skip(0).next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:31:17
+ |
+LL | let _ = s.splitn(2, "key=value").next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:32:17
+ |
+LL | let _ = s.splitn(2, "key=value").nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:33:17
+ |
+LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+
++error: manual implementation of `rsplit_once`
++ --> $DIR/manual_split_once.rs:41:13
++ |
++LL | let _ = "key=value".rsplitn(2, '=').next().unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().1`
++
++error: manual implementation of `rsplit_once`
++ --> $DIR/manual_split_once.rs:42:13
++ |
++LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map_or("key=value", |x| x.0)`
++
++error: manual implementation of `rsplit_once`
++ --> $DIR/manual_split_once.rs:43:13
++ |
++LL | let _ = "key=value".rsplitn(2, '=').nth(0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
++
++error: manual implementation of `rsplit_once`
++ --> $DIR/manual_split_once.rs:44:18
++ |
++LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
++
+error: manual implementation of `split_once`
- error: aborting due to 13 previous errors
++ --> $DIR/manual_split_once.rs:55:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
++error: aborting due to 17 previous errors
+
--- /dev/null
- #[warn(clippy::many_single_char_names)]
++#![warn(clippy::many_single_char_names)]
+
+fn bla() {
+ let a: i32;
+ let (b, c, d): (i32, i64, i16);
+ {
+ {
+ let cdefg: i32;
+ let blar: i32;
+ }
+ {
+ let e: i32;
+ }
+ {
+ let e: i32;
+ let f: i32;
+ }
+ match 5 {
+ 1 => println!(),
+ e => panic!(),
+ }
+ match 5 {
+ 1 => println!(),
+ _ => panic!(),
+ }
+ }
+}
+
+fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
+
+fn bindings2() {
+ let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
+}
+
+fn shadowing() {
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ {
+ let a = 0i32;
+ }
+}
+
+fn patterns() {
+ enum Z {
+ A(i32),
+ B(i32),
+ C(i32),
+ D(i32),
+ E(i32),
+ F(i32),
+ }
+
+ // These should not trigger a warning, since the pattern bindings are a new scope.
+ match Z::A(0) {
+ Z::A(a) => {},
+ Z::B(b) => {},
+ Z::C(c) => {},
+ Z::D(d) => {},
+ Z::E(e) => {},
+ Z::F(f) => {},
+ }
+}
+
+#[allow(clippy::many_single_char_names)]
+fn issue_3198_allow_works() {
+ let (a, b, c, d, e) = (0, 0, 0, 0, 0);
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(clippy::let_underscore_drop)]
+#![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::map_identity)]
++#![allow(clippy::redundant_closure)]
+#![allow(clippy::unnecessary_wraps)]
+#![feature(result_flattening)]
+
+fn main() {
+ // mapping to Option on Iterator
+ fn option_id(x: i8) -> Option<i8> {
+ Some(x)
+ }
+ let option_id_ref: fn(i8) -> Option<i8> = option_id;
+ let option_id_closure = |x| Some(x);
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
+
+ // mapping to Iterator on Iterator
+ let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
+
+ // mapping to Option on Option
+ let _: Option<_> = (Some(Some(1))).and_then(|x| x);
+
+ // mapping to Result on Result
+ let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(clippy::let_underscore_drop)]
+#![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::map_identity)]
++#![allow(clippy::redundant_closure)]
+#![allow(clippy::unnecessary_wraps)]
+#![feature(result_flattening)]
+
+fn main() {
+ // mapping to Option on Iterator
+ fn option_id(x: i8) -> Option<i8> {
+ Some(x)
+ }
+ let option_id_ref: fn(i8) -> Option<i8> = option_id;
+ let option_id_closure = |x| Some(x);
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+
+ // mapping to Iterator on Iterator
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+
+ // mapping to Option on Option
+ let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
+
+ // mapping to Result on Result
+ let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
+}
--- /dev/null
- --> $DIR/map_flatten.rs:17:46
+error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:18:46
++ --> $DIR/map_flatten.rs:18:46
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
+ |
+ = note: `-D clippy::map-flatten` implied by `-D warnings`
+
+error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:19:46
++ --> $DIR/map_flatten.rs:19:46
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
+
+error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:20:46
++ --> $DIR/map_flatten.rs:20:46
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
+
+error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:23:46
++ --> $DIR/map_flatten.rs:21:46
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
+
+error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:26:39
++ --> $DIR/map_flatten.rs:24:46
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
+
+error: called `map(..).flatten()` on an `Option`
- --> $DIR/map_flatten.rs:29:41
++ --> $DIR/map_flatten.rs:27:39
+ |
+LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
+
+error: called `map(..).flatten()` on an `Result`
++ --> $DIR/map_flatten.rs:30:41
+ |
+LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
+
+error: aborting due to 7 previous errors
+
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::match_result_ok)]
++#![allow(clippy::boxed_local)]
++#![allow(dead_code)]
++
++// Checking `if` cases
++
++fn str_to_int(x: &str) -> i32 {
++ if let Ok(y) = x.parse() { y } else { 0 }
++}
++
++fn str_to_int_ok(x: &str) -> i32 {
++ if let Ok(y) = x.parse() { y } else { 0 }
++}
++
++#[rustfmt::skip]
++fn strange_some_no_else(x: &str) -> i32 {
++ {
++ if let Ok(y) = x . parse() {
++ return y;
++ };
++ 0
++ }
++}
++
++// Checking `while` cases
++
++struct Wat {
++ counter: i32,
++}
++
++impl Wat {
++ fn next(&mut self) -> Result<i32, &str> {
++ self.counter += 1;
++ if self.counter < 5 {
++ Ok(self.counter)
++ } else {
++ Err("Oh no")
++ }
++ }
++}
++
++fn base_1(x: i32) {
++ let mut wat = Wat { counter: x };
++ while let Ok(a) = wat.next() {
++ println!("{}", a);
++ }
++}
++
++fn base_2(x: i32) {
++ let mut wat = Wat { counter: x };
++ while let Ok(a) = wat.next() {
++ println!("{}", a);
++ }
++}
++
++fn base_3(test_func: Box<Result<i32, &str>>) {
++ // Expected to stay as is
++ while let Some(_b) = test_func.ok() {}
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::match_result_ok)]
++#![allow(clippy::boxed_local)]
++#![allow(dead_code)]
++
++// Checking `if` cases
++
++fn str_to_int(x: &str) -> i32 {
++ if let Some(y) = x.parse().ok() { y } else { 0 }
++}
++
++fn str_to_int_ok(x: &str) -> i32 {
++ if let Ok(y) = x.parse() { y } else { 0 }
++}
++
++#[rustfmt::skip]
++fn strange_some_no_else(x: &str) -> i32 {
++ {
++ if let Some(y) = x . parse() . ok () {
++ return y;
++ };
++ 0
++ }
++}
++
++// Checking `while` cases
++
++struct Wat {
++ counter: i32,
++}
++
++impl Wat {
++ fn next(&mut self) -> Result<i32, &str> {
++ self.counter += 1;
++ if self.counter < 5 {
++ Ok(self.counter)
++ } else {
++ Err("Oh no")
++ }
++ }
++}
++
++fn base_1(x: i32) {
++ let mut wat = Wat { counter: x };
++ while let Some(a) = wat.next().ok() {
++ println!("{}", a);
++ }
++}
++
++fn base_2(x: i32) {
++ let mut wat = Wat { counter: x };
++ while let Ok(a) = wat.next() {
++ println!("{}", a);
++ }
++}
++
++fn base_3(test_func: Box<Result<i32, &str>>) {
++ // Expected to stay as is
++ while let Some(_b) = test_func.ok() {}
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: matching on `Some` with `ok()` is redundant
++ --> $DIR/match_result_ok.rs:10:5
++ |
++LL | if let Some(y) = x.parse().ok() { y } else { 0 }
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::match-result-ok` implied by `-D warnings`
++help: consider matching on `Ok(y)` and removing the call to `ok` instead
++ |
++LL | if let Ok(y) = x.parse() { y } else { 0 }
++ | ~~~~~~~~~~~~~~~~~~~~~~~~
++
++error: matching on `Some` with `ok()` is redundant
++ --> $DIR/match_result_ok.rs:20:9
++ |
++LL | if let Some(y) = x . parse() . ok () {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: consider matching on `Ok(y)` and removing the call to `ok` instead
++ |
++LL | if let Ok(y) = x . parse() {
++ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++error: matching on `Some` with `ok()` is redundant
++ --> $DIR/match_result_ok.rs:46:5
++ |
++LL | while let Some(a) = wat.next().ok() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: consider matching on `Ok(a)` and removing the call to `ok` instead
++ |
++LL | while let Ok(a) = wat.next() {
++ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++error: aborting due to 3 previous errors
++
--- /dev/null
- #![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
++#![allow(unused_variables, clippy::toplevel_ref_arg)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn coords() -> Point {
+ Point { x: 1, y: 2 }
+}
+
+macro_rules! foo {
+ ($param:expr) => {
+ match $param {
+ _ => println!("whatever"),
+ }
+ };
+}
+
+fn main() {
+ let a = 1;
+ let b = 2;
+ let c = 3;
+ // Lint
+ let (x, y, z) = (a, b, c);
+ {
+ println!("{} {} {}", x, y, z);
+ }
+ // Lint
+ let (x, y, z) = (a, b, c);
+ println!("{} {} {}", x, y, z);
+ // Ok
+ foo!(a);
+ // Ok
+ match a {
+ 2 => println!("2"),
+ _ => println!("Not 2"),
+ }
+ // Ok
+ let d = Some(5);
+ match d {
+ Some(d) => println!("{}", d),
+ _ => println!("None"),
+ }
+ // Lint
+ println!("whatever");
+ // Lint
+ {
+ let x = 29;
+ println!("x has a value of {}", x);
+ }
+ // Lint
+ {
+ let e = 5 * a;
+ if e >= 5 {
+ println!("e is superior to 5");
+ }
+ }
+ // Lint
+ let p = Point { x: 0, y: 7 };
+ let Point { x, y } = p;
+ println!("Coords: ({}, {})", x, y);
+ // Lint
+ let Point { x: x1, y: y1 } = p;
+ println!("Coords: ({}, {})", x1, y1);
+ // Lint
+ let x = 5;
+ let ref r = x;
+ println!("Got a reference to {}", r);
+ // Lint
+ let mut x = 5;
+ let ref mut mr = x;
+ println!("Got a mutable reference to {}", mr);
+ // Lint
+ let Point { x, y } = coords();
+ let product = x * y;
+ // Lint
+ let v = vec![Some(1), Some(2), Some(3), Some(4)];
+ #[allow(clippy::let_and_return)]
+ let _ = v
+ .iter()
+ .map(|i| {
+ let unwrapped = i.unwrap();
+ unwrapped
+ })
+ .collect::<Vec<u8>>();
+ // Ok
+ let x = 1;
+ match x {
+ #[cfg(disabled_feature)]
+ 0 => println!("Disabled branch"),
+ _ => println!("Enabled branch"),
+ }
+
+ // Ok
+ let x = 1;
+ let y = 1;
+ match match y {
+ 0 => 1,
+ _ => 2,
+ } {
+ #[cfg(disabled_feature)]
+ 0 => println!("Array index start"),
+ _ => println!("Not an array index start"),
+ }
+ // False negative
+ let x = 1;
+ match x {
+ // =>
+ _ => println!("Not an array index start"),
+ }
+}
--- /dev/null
- #![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
++#![allow(unused_variables, clippy::toplevel_ref_arg)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn coords() -> Point {
+ Point { x: 1, y: 2 }
+}
+
+macro_rules! foo {
+ ($param:expr) => {
+ match $param {
+ _ => println!("whatever"),
+ }
+ };
+}
+
+fn main() {
+ let a = 1;
+ let b = 2;
+ let c = 3;
+ // Lint
+ match (a, b, c) {
+ (x, y, z) => {
+ println!("{} {} {}", x, y, z);
+ },
+ }
+ // Lint
+ match (a, b, c) {
+ (x, y, z) => println!("{} {} {}", x, y, z),
+ }
+ // Ok
+ foo!(a);
+ // Ok
+ match a {
+ 2 => println!("2"),
+ _ => println!("Not 2"),
+ }
+ // Ok
+ let d = Some(5);
+ match d {
+ Some(d) => println!("{}", d),
+ _ => println!("None"),
+ }
+ // Lint
+ match a {
+ _ => println!("whatever"),
+ }
+ // Lint
+ match a {
+ _ => {
+ let x = 29;
+ println!("x has a value of {}", x);
+ },
+ }
+ // Lint
+ match a {
+ _ => {
+ let e = 5 * a;
+ if e >= 5 {
+ println!("e is superior to 5");
+ }
+ },
+ }
+ // Lint
+ let p = Point { x: 0, y: 7 };
+ match p {
+ Point { x, y } => println!("Coords: ({}, {})", x, y),
+ }
+ // Lint
+ match p {
+ Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
+ }
+ // Lint
+ let x = 5;
+ match x {
+ ref r => println!("Got a reference to {}", r),
+ }
+ // Lint
+ let mut x = 5;
+ match x {
+ ref mut mr => println!("Got a mutable reference to {}", mr),
+ }
+ // Lint
+ let product = match coords() {
+ Point { x, y } => x * y,
+ };
+ // Lint
+ let v = vec![Some(1), Some(2), Some(3), Some(4)];
+ #[allow(clippy::let_and_return)]
+ let _ = v
+ .iter()
+ .map(|i| match i.unwrap() {
+ unwrapped => unwrapped,
+ })
+ .collect::<Vec<u8>>();
+ // Ok
+ let x = 1;
+ match x {
+ #[cfg(disabled_feature)]
+ 0 => println!("Disabled branch"),
+ _ => println!("Enabled branch"),
+ }
+
+ // Ok
+ let x = 1;
+ let y = 1;
+ match match y {
+ 0 => 1,
+ _ => 2,
+ } {
+ #[cfg(disabled_feature)]
+ 0 => println!("Array index start"),
+ _ => println!("Not an array index start"),
+ }
+ // False negative
+ let x = 1;
+ match x {
+ // =>
+ _ => println!("Not an array index start"),
+ }
+}
--- /dev/null
- use std::collections::{HashMap, HashSet};
++use std::cell::Cell;
++use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::hash::{Hash, Hasher};
++use std::rc::Rc;
+use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
++use std::sync::Arc;
+
+struct Key(AtomicUsize);
+
+impl Clone for Key {
+ fn clone(&self) -> Self {
+ Key(AtomicUsize::new(self.0.load(Relaxed)))
+ }
+}
+
+impl PartialEq for Key {
+ fn eq(&self, other: &Self) -> bool {
+ self.0.load(Relaxed) == other.0.load(Relaxed)
+ }
+}
+
+impl Eq for Key {}
+
+impl Hash for Key {
+ fn hash<H: Hasher>(&self, h: &mut H) {
+ self.0.load(Relaxed).hash(h);
+ }
+}
+
+fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
+ let _other: HashMap<Key, bool> = HashMap::new();
+ m.keys().cloned().collect()
+}
+
+fn this_is_ok(_m: &mut HashMap<usize, Key>) {}
+
++// Raw pointers are hashed by the address they point to, so it doesn't matter if they point to a
++// type with interior mutability. See:
++// - clippy issue: https://github.com/rust-lang/rust-clippy/issues/6745
++// - std lib: https://github.com/rust-lang/rust/blob/1.54.0/library/core/src/hash/mod.rs#L717-L736
++// So these are OK:
++fn raw_ptr_is_ok(_m: &mut HashMap<*const Key, ()>) {}
++fn raw_mut_ptr_is_ok(_m: &mut HashMap<*mut Key, ()>) {}
++
+#[allow(unused)]
+trait Trait {
+ type AssociatedType;
+
- fn trait_fn(&self, set: std::collections::HashSet<Self::AssociatedType>);
++ fn trait_fn(&self, set: HashSet<Self::AssociatedType>);
+}
+
+fn generics_are_ok_too<K>(_m: &mut HashSet<K>) {
+ // nothing to see here, move along
+}
+
+fn tuples<U>(_m: &mut HashMap<((), U), ()>) {}
+
+fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
+
+fn main() {
+ let _ = should_not_take_this_arg(&mut HashMap::new(), 1);
+ this_is_ok(&mut HashMap::new());
+ tuples::<Key>(&mut HashMap::new());
+ tuples::<()>(&mut HashMap::new());
+ tuples_bad::<()>(&mut HashMap::new());
++
++ raw_ptr_is_ok(&mut HashMap::new());
++ raw_mut_ptr_is_ok(&mut HashMap::new());
++
++ let _map = HashMap::<Cell<usize>, usize>::new();
++ let _map = HashMap::<&mut Cell<usize>, usize>::new();
++ let _map = HashMap::<&mut usize, usize>::new();
++ // Collection types from `std` who's impl of `Hash` or `Ord` delegate their type parameters
++ let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
++ let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
++ let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
++ let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
++ let _map = HashMap::<Option<Cell<usize>>, usize>::new();
++ let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
++ let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
++ // Smart pointers from `std` who's impl of `Hash` or `Ord` delegate their type parameters
++ let _map = HashMap::<Box<Cell<usize>>, usize>::new();
++ let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
++ let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
+}
--- /dev/null
- --> $DIR/mut_key.rs:27:32
+error: mutable key type
- --> $DIR/mut_key.rs:27:72
++ --> $DIR/mut_key.rs:30:32
+ |
+LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::mutable-key-type` implied by `-D warnings`
+
+error: mutable key type
- --> $DIR/mut_key.rs:28:5
++ --> $DIR/mut_key.rs:30:72
+ |
+LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
+ | ^^^^^^^^^^^^
+
+error: mutable key type
- --> $DIR/mut_key.rs:47:22
++ --> $DIR/mut_key.rs:31:5
+ |
+LL | let _other: HashMap<Key, bool> = HashMap::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
- error: aborting due to 4 previous errors
++ --> $DIR/mut_key.rs:58:22
+ |
+LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
++error: mutable key type
++ --> $DIR/mut_key.rs:70:5
++ |
++LL | let _map = HashMap::<Cell<usize>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:71:5
++ |
++LL | let _map = HashMap::<&mut Cell<usize>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:72:5
++ |
++LL | let _map = HashMap::<&mut usize, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:74:5
++ |
++LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:75:5
++ |
++LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:76:5
++ |
++LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:77:5
++ |
++LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:78:5
++ |
++LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:79:5
++ |
++LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:80:5
++ |
++LL | let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:82:5
++ |
++LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:83:5
++ |
++LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++ --> $DIR/mut_key.rs:84:5
++ |
++LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 17 previous errors
+
--- /dev/null
- #![allow(clippy::needless_borrowed_reference)]
-
- fn x(y: &i32) -> i32 {
- *y
- }
-
+// run-rustfix
+
- let b = x(&a);
- let c = x(&a);
+#[warn(clippy::all, clippy::needless_borrow)]
+#[allow(unused_variables)]
+fn main() {
+ let a = 5;
++ let _ = x(&a); // no warning
++ let _ = x(&a); // warn
++
++ let mut b = 5;
++ mut_ref(&mut b); // no warning
++ mut_ref(&mut b); // warn
++
+ let s = &String::from("hi");
+ let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
+ let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
+ let vec = Vec::new();
+ let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
+ h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
+ let garbl = match 42 {
+ 44 => &a,
+ 45 => {
+ println!("foo");
+ &&a // FIXME: this should lint, too
+ },
+ 46 => &a,
+ _ => panic!(),
+ };
+}
+
++#[allow(clippy::needless_borrowed_reference)]
++fn x(y: &i32) -> i32 {
++ *y
++}
++
++fn mut_ref(y: &mut i32) {
++ *y = 5;
++}
++
+fn f<T: Copy>(y: &T) -> T {
+ *y
+}
+
+fn g(y: &[u8]) -> u8 {
+ y[0]
+}
+
+trait Trait {}
+
+impl<'a> Trait for &'a str {}
+
+fn h(_: &dyn Trait) {}
--- /dev/null
- #![allow(clippy::needless_borrowed_reference)]
-
- fn x(y: &i32) -> i32 {
- *y
- }
-
+// run-rustfix
+
- let b = x(&a);
- let c = x(&&a);
+#[warn(clippy::all, clippy::needless_borrow)]
+#[allow(unused_variables)]
+fn main() {
+ let a = 5;
++ let _ = x(&a); // no warning
++ let _ = x(&&a); // warn
++
++ let mut b = 5;
++ mut_ref(&mut b); // no warning
++ mut_ref(&mut &mut b); // warn
++
+ let s = &String::from("hi");
+ let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
+ let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
+ let vec = Vec::new();
+ let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
+ h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
+ let garbl = match 42 {
+ 44 => &a,
+ 45 => {
+ println!("foo");
+ &&a // FIXME: this should lint, too
+ },
+ 46 => &&a,
+ _ => panic!(),
+ };
+}
+
++#[allow(clippy::needless_borrowed_reference)]
++fn x(y: &i32) -> i32 {
++ *y
++}
++
++fn mut_ref(y: &mut i32) {
++ *y = 5;
++}
++
+fn f<T: Copy>(y: &T) -> T {
+ *y
+}
+
+fn g(y: &[u8]) -> u8 {
+ y[0]
+}
+
+trait Trait {}
+
+impl<'a> Trait for &'a str {}
+
+fn h(_: &dyn Trait) {}
--- /dev/null
- --> $DIR/needless_borrow.rs:14:15
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
- LL | let c = x(&&a);
++ --> $DIR/needless_borrow.rs:8:15
+ |
- --> $DIR/needless_borrow.rs:27:15
++LL | let _ = x(&&a); // warn
+ | ^^^ help: change this to: `&a`
+ |
+ = note: `-D clippy::needless-borrow` implied by `-D warnings`
+
++error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
++ --> $DIR/needless_borrow.rs:12:13
++ |
++LL | mut_ref(&mut &mut b); // warn
++ | ^^^^^^^^^^^ help: change this to: `&mut b`
++
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
- error: aborting due to 2 previous errors
++ --> $DIR/needless_borrow.rs:26:15
+ |
+LL | 46 => &&a,
+ | ^^^ help: change this to: `&a`
+
++error: aborting due to 3 previous errors
+
--- /dev/null
- clippy::many_single_char_names,
+#![warn(clippy::needless_pass_by_value)]
+#![allow(
+ dead_code,
+ clippy::single_match,
+ clippy::redundant_pattern_matching,
+ clippy::option_option,
+ clippy::redundant_clone
+)]
+
+use std::borrow::Borrow;
+use std::collections::HashSet;
+use std::convert::AsRef;
+use std::mem::MaybeUninit;
+
+// `v` should be warned
+// `w`, `x` and `y` are allowed (moved or mutated)
+fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
+ assert_eq!(v.len(), 42);
+
+ consume(w);
+
+ x.push(T::default());
+
+ y
+}
+
+fn consume<T>(_: T) {}
+
+struct Wrapper(String);
+
+fn bar(x: String, y: Wrapper) {
+ assert_eq!(x.len(), 42);
+ assert_eq!(y.0.len(), 42);
+}
+
+// V implements `Borrow<V>`, but should be warned correctly
+fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
+ println!("{}", t.borrow());
+ println!("{}", u.as_ref());
+ consume(&v);
+}
+
+// ok
+fn test_fn<F: Fn(i32) -> i32>(f: F) {
+ f(1);
+}
+
+// x should be warned, but y is ok
+fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
+ match x {
+ Some(Some(_)) => 1, // not moved
+ _ => 0,
+ };
+
+ match y {
+ Some(Some(s)) => consume(s), // moved
+ _ => (),
+ };
+}
+
+// x and y should be warned, but z is ok
+fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
+ let Wrapper(s) = z; // moved
+ let Wrapper(ref t) = y; // not moved
+ let Wrapper(_) = y; // still not moved
+
+ assert_eq!(x.0.len(), s.len());
+ println!("{}", t);
+}
+
+trait Foo {}
+
+// `S: Serialize` is allowed to be passed by value, since a caller can pass `&S` instead
+trait Serialize {}
+impl<'a, T> Serialize for &'a T where T: Serialize {}
+impl Serialize for i32 {}
+
+fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
+
+fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ s.capacity();
+ let _ = t.clone();
+ u.capacity();
+ let _ = v.clone();
+}
+
+struct S<T, U>(T, U);
+
+impl<T: Serialize, U> S<T, U> {
+ fn foo(
+ self,
+ // taking `self` by value is always allowed
+ s: String,
+ t: String,
+ ) -> usize {
+ s.len() + t.capacity()
+ }
+
+ fn bar(_t: T, // Ok, since `&T: Serialize` too
+ ) {
+ }
+
+ fn baz(&self, _u: U, _s: Self) {}
+}
+
+trait FalsePositive {
+ fn visit_str(s: &str);
+ fn visit_string(s: String) {
+ Self::visit_str(&s);
+ }
+}
+
+// shouldn't warn on extern funcs
+extern "C" fn ext(x: MaybeUninit<usize>) -> usize {
+ unsafe { x.assume_init() }
+}
+
+// exempt RangeArgument
+fn range<T: ::std::ops::RangeBounds<usize>>(range: T) {
+ let _ = range.start_bound();
+}
+
+struct CopyWrapper(u32);
+
+fn bar_copy(x: u32, y: CopyWrapper) {
+ assert_eq!(x, 42);
+ assert_eq!(y.0, 42);
+}
+
+// x and y should be warned, but z is ok
+fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ let CopyWrapper(s) = z; // moved
+ let CopyWrapper(ref t) = y; // not moved
+ let CopyWrapper(_) = y; // still not moved
+
+ assert_eq!(x.0, s);
+ println!("{}", t);
+}
+
+// The following 3 lines should not cause an ICE. See #2831
+trait Bar<'a, A> {}
+impl<'b, T> Bar<'b, T> for T {}
+fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+
+// Also this should not cause an ICE. See #2831
+trait Club<'a, A> {}
+impl<T> Club<'static, T> for T {}
+fn more_fun(_item: impl Club<'static, i32>) {}
+
+fn is_sync<T>(_: T)
+where
+ T: Sync,
+{
+}
+
+fn main() {
+ // This should not cause an ICE either
+ // https://github.com/rust-lang/rust-clippy/issues/3144
+ is_sync(HashSet::<usize>::new());
+}
--- /dev/null
- --> $DIR/needless_pass_by_value.rs:18:23
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:32:11
++ --> $DIR/needless_pass_by_value.rs:17:23
+ |
+LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
+ | ^^^^^^ help: consider changing the type to: `&[T]`
+ |
+ = note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:32:22
++ --> $DIR/needless_pass_by_value.rs:31:11
+ |
+LL | fn bar(x: String, y: Wrapper) {
+ | ^^^^^^ help: consider changing the type to: `&str`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:38:71
++ --> $DIR/needless_pass_by_value.rs:31:22
+ |
+LL | fn bar(x: String, y: Wrapper) {
+ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:50:18
++ --> $DIR/needless_pass_by_value.rs:37:71
+ |
+LL | fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
+ | ^ help: consider taking a reference instead: `&V`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:63:24
++ --> $DIR/needless_pass_by_value.rs:49:18
+ |
+LL | fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option<Option<String>>`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:63:36
++ --> $DIR/needless_pass_by_value.rs:62:24
+ |
+LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
+ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:79:49
++ --> $DIR/needless_pass_by_value.rs:62:36
+ |
+LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
+ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:18
++ --> $DIR/needless_pass_by_value.rs:78:49
+ |
+LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
+ | ^ help: consider taking a reference instead: `&T`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:29
++ --> $DIR/needless_pass_by_value.rs:80:18
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^ help: consider taking a reference instead: `&String`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:40
++ --> $DIR/needless_pass_by_value.rs:80:29
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^
+ |
+help: consider changing the type to
+ |
+LL | fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) {
+ | ~~~~
+help: change `t.clone()` to
+ |
+LL | let _ = t.to_string();
+ | ~~~~~~~~~~~~~
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:53
++ --> $DIR/needless_pass_by_value.rs:80:40
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:94:12
++ --> $DIR/needless_pass_by_value.rs:80:53
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^^^
+ |
+help: consider changing the type to
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) {
+ | ~~~~~~
+help: change `v.clone()` to
+ |
+LL | let _ = v.to_owned();
+ | ~~~~~~~~~~~~
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:95:12
++ --> $DIR/needless_pass_by_value.rs:93:12
+ |
+LL | s: String,
+ | ^^^^^^ help: consider changing the type to: `&str`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:104:23
++ --> $DIR/needless_pass_by_value.rs:94:12
+ |
+LL | t: String,
+ | ^^^^^^ help: consider taking a reference instead: `&String`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:104:30
++ --> $DIR/needless_pass_by_value.rs:103:23
+ |
+LL | fn baz(&self, _u: U, _s: Self) {}
+ | ^ help: consider taking a reference instead: `&U`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:126:24
++ --> $DIR/needless_pass_by_value.rs:103:30
+ |
+LL | fn baz(&self, _u: U, _s: Self) {}
+ | ^^^^ help: consider taking a reference instead: `&Self`
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:124:1
++ --> $DIR/needless_pass_by_value.rs:125:24
+ |
+LL | fn bar_copy(x: u32, y: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:132:29
++ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:124:1
++ --> $DIR/needless_pass_by_value.rs:131:29
+ |
+LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:132:45
++ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:124:1
++ --> $DIR/needless_pass_by_value.rs:131:45
+ |
+LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:132:61
++ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:124:1
++ --> $DIR/needless_pass_by_value.rs:131:61
+ |
+LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:144:40
++ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:149:20
++ --> $DIR/needless_pass_by_value.rs:143:40
+ |
+LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+ | ^ help: consider taking a reference instead: `&S`
+
+error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:148:20
+ |
+LL | fn more_fun(_item: impl Club<'static, i32>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
+
+error: aborting due to 22 previous errors
+
--- /dev/null
- #![allow(
- clippy::if_same_then_else,
- clippy::single_match,
- clippy::branches_sharing_code,
- clippy::needless_bool
- )]
+// run-rustfix
+// edition:2018
+
++#![feature(let_else)]
+#![allow(unused)]
++#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
+#![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::branches_sharing_code,
- clippy::needless_bool
- )]
+// run-rustfix
+// edition:2018
+
++#![feature(let_else)]
+#![allow(unused)]
++#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
+#![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:24:5
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:28:5
++ --> $DIR/needless_return.rs:20: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:33:9
++ --> $DIR/needless_return.rs:24:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:35:9
++ --> $DIR/needless_return.rs:29:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:41:17
++ --> $DIR/needless_return.rs:31:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:43:13
++ --> $DIR/needless_return.rs:37:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:50:9
++ --> $DIR/needless_return.rs:39:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:52:16
++ --> $DIR/needless_return.rs:46:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:60:5
++ --> $DIR/needless_return.rs:48:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:65:9
++ --> $DIR/needless_return.rs:56:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:67:9
++ --> $DIR/needless_return.rs:61:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:74:14
++ --> $DIR/needless_return.rs:63:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:89:9
++ --> $DIR/needless_return.rs:70:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:91:9
++ --> $DIR/needless_return.rs:85:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:112:32
++ --> $DIR/needless_return.rs:87:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:117:13
++ --> $DIR/needless_return.rs:108:32
+ |
+LL | bar.unwrap_or_else(|_| return)
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:119:20
++ --> $DIR/needless_return.rs:113:13
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:125:32
++ --> $DIR/needless_return.rs:115:20
+ |
+LL | let _ = || return;
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:134:5
++ --> $DIR/needless_return.rs:121:32
+ |
+LL | res.unwrap_or_else(|_| return Foo)
+ | ^^^^^^^^^^ help: remove `return`: `Foo`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:138:5
++ --> $DIR/needless_return.rs:130:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:143:9
++ --> $DIR/needless_return.rs:134:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:145:9
++ --> $DIR/needless_return.rs:139:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:151:17
++ --> $DIR/needless_return.rs:141:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:153:13
++ --> $DIR/needless_return.rs:147:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:160:9
++ --> $DIR/needless_return.rs:149:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:162:16
++ --> $DIR/needless_return.rs:156:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:170:5
++ --> $DIR/needless_return.rs:158:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:175:9
++ --> $DIR/needless_return.rs:166:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:177:9
++ --> $DIR/needless_return.rs:171:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:184:14
++ --> $DIR/needless_return.rs:173:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:199:9
++ --> $DIR/needless_return.rs:180:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:201:9
++ --> $DIR/needless_return.rs:195:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
++ --> $DIR/needless_return.rs:197:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: aborting due to 32 previous errors
+
--- /dev/null
- #![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
++#![allow(unused, clippy::diverging_sub_expression)]
+#![warn(clippy::nonminimal_bool)]
+
+fn main() {
+ let a: bool = unimplemented!();
+ let b: bool = unimplemented!();
+ let c: bool = unimplemented!();
+ let d: bool = unimplemented!();
+ let e: bool = unimplemented!();
+ let _ = !true;
+ let _ = !false;
+ let _ = !!a;
+ let _ = false || a;
+ // don't lint on cfgs
+ let _ = cfg!(you_shall_not_not_pass) && a;
+ let _ = a || !b || !c || !d || !e;
+ let _ = !(!a && b);
+ let _ = !(!a || b);
+ let _ = !a && !(b && c);
+}
+
+fn equality_stuff() {
+ let a: i32 = unimplemented!();
+ let b: i32 = unimplemented!();
+ let c: i32 = unimplemented!();
+ let d: i32 = unimplemented!();
+ let _ = a == b && c == 5 && a == b;
+ let _ = a == b || c == 5 || a == b;
+ let _ = a == b && c == 5 && b == a;
+ let _ = a != b || !(a != b || c == d);
+ let _ = a != b && !(a != b && c == d);
+}
+
+fn issue3847(a: u32, b: u32) -> bool {
+ const THRESHOLD: u32 = 1_000;
+
+ if a < THRESHOLD && b >= THRESHOLD || a >= THRESHOLD && b < THRESHOLD {
+ return false;
+ }
+ true
+}
+
+fn issue4548() {
+ fn f(_i: u32, _j: u32) -> u32 {
+ unimplemented!();
+ }
+
+ let i = 0;
+ let j = 0;
+
+ if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {}
+}
--- /dev/null
- #![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
++#![allow(unused, clippy::diverging_sub_expression)]
+#![warn(clippy::nonminimal_bool)]
+
+fn methods_with_negation() {
+ let a: Option<i32> = unimplemented!();
+ let b: Result<i32, i32> = unimplemented!();
+ let _ = a.is_some();
+ let _ = !a.is_some();
+ let _ = a.is_none();
+ let _ = !a.is_none();
+ let _ = b.is_err();
+ let _ = !b.is_err();
+ let _ = b.is_ok();
+ let _ = !b.is_ok();
+ let c = false;
+ let _ = !(a.is_some() && !c);
+ let _ = !(a.is_some() || !c);
+ let _ = !(!c ^ c) || !a.is_some();
+ let _ = (!c ^ c) || !a.is_some();
+ let _ = !c ^ c || !a.is_some();
+}
+
+// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638
+// clippy::nonminimal_bool should only check the built-in Result and Some type, not
+// any other types like the following.
+enum CustomResultOk<E> {
+ Ok,
+ Err(E),
+}
+enum CustomResultErr<E> {
+ Ok,
+ Err(E),
+}
+enum CustomSomeSome<T> {
+ Some(T),
+ None,
+}
+enum CustomSomeNone<T> {
+ Some(T),
+ None,
+}
+
+impl<E> CustomResultOk<E> {
+ pub fn is_ok(&self) -> bool {
+ true
+ }
+}
+
+impl<E> CustomResultErr<E> {
+ pub fn is_err(&self) -> bool {
+ true
+ }
+}
+
+impl<T> CustomSomeSome<T> {
+ pub fn is_some(&self) -> bool {
+ true
+ }
+}
+
+impl<T> CustomSomeNone<T> {
+ pub fn is_none(&self) -> bool {
+ true
+ }
+}
+
+fn dont_warn_for_custom_methods_with_negation() {
+ let res = CustomResultOk::Err("Error");
+ // Should not warn and suggest 'is_err()' because the type does not
+ // implement is_err().
+ if !res.is_ok() {}
+
+ let res = CustomResultErr::Err("Error");
+ // Should not warn and suggest 'is_ok()' because the type does not
+ // implement is_ok().
+ if !res.is_err() {}
+
+ let res = CustomSomeSome::Some("thing");
+ // Should not warn and suggest 'is_none()' because the type does not
+ // implement is_none().
+ if !res.is_some() {}
+
+ let res = CustomSomeNone::Some("thing");
+ // Should not warn and suggest 'is_some()' because the type does not
+ // implement is_some().
+ if !res.is_none() {}
+}
+
+// Only Built-in Result and Some types should suggest the negated alternative
+fn warn_for_built_in_methods_with_negation() {
+ let res: Result<usize, usize> = Ok(1);
+ if !res.is_ok() {}
+ if !res.is_err() {}
+
+ let res = Some(1);
+ if !res.is_some() {}
+ if !res.is_none() {}
+}
+
+#[allow(clippy::neg_cmp_op_on_partial_ord)]
+fn dont_warn_for_negated_partial_ord_comparison() {
+ let a: f64 = unimplemented!();
+ let b: f64 = unimplemented!();
+ let _ = !(a < b);
+ let _ = !(a <= b);
+ let _ = !(a > b);
+ let _ = !(a >= b);
+}
+
+fn main() {}
--- /dev/null
- #![allow(clippy::many_single_char_names)]
+#![allow(unused_variables, clippy::blacklisted_name)]
+#![warn(clippy::op_ref)]
+use std::collections::HashSet;
+use std::ops::BitAnd;
+
+fn main() {
+ let tracked_fds: HashSet<i32> = HashSet::new();
+ let new_fds = HashSet::new();
+ let unwanted = &tracked_fds - &new_fds;
+
+ let foo = &5 - &6;
+
+ let bar = String::new();
+ let bar = "foo" == &bar;
+
+ let a = "a".to_string();
+ let b = "a";
+
+ if b < &a {
+ println!("OK");
+ }
+
+ struct X(i32);
+ impl BitAnd for X {
+ type Output = X;
+ fn bitand(self, rhs: X) -> X {
+ X(self.0 & rhs.0)
+ }
+ }
+ impl<'a> BitAnd<&'a X> for X {
+ type Output = X;
+ fn bitand(self, rhs: &'a X) -> X {
+ X(self.0 & rhs.0)
+ }
+ }
+ let x = X(1);
+ let y = X(2);
+ let z = x & &y;
+
+ #[derive(Copy, Clone)]
+ struct Y(i32);
+ impl BitAnd for Y {
+ type Output = Y;
+ fn bitand(self, rhs: Y) -> Y {
+ Y(self.0 & rhs.0)
+ }
+ }
+ impl<'a> BitAnd<&'a Y> for Y {
+ type Output = Y;
+ fn bitand(self, rhs: &'a Y) -> Y {
+ Y(self.0 & rhs.0)
+ }
+ }
+ let x = Y(1);
+ let y = Y(2);
+ let z = x & &y;
+}
--- /dev/null
- --> $DIR/op_ref.rs:12:15
+error: needlessly taken reference of both operands
- --> $DIR/op_ref.rs:57:13
++ --> $DIR/op_ref.rs:11:15
+ |
+LL | let foo = &5 - &6;
+ | ^^^^^^^
+ |
+ = note: `-D clippy::op-ref` implied by `-D warnings`
+help: use the values directly
+ |
+LL | let foo = 5 - 6;
+ | ~ ~
+
+error: taken reference of right operand
++ --> $DIR/op_ref.rs:56:13
+ |
+LL | let z = x & &y;
+ | ^^^^--
+ | |
+ | help: use the right value directly: `y`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
- #![allow(clippy::many_single_char_names)]
+#![warn(clippy::overflow_check_conditional)]
+
+fn main() {
+ let a: u32 = 1;
+ let b: u32 = 2;
+ let c: u32 = 3;
+ if a + b < a {}
+ if a > a + b {}
+ if a + b < b {}
+ if b > a + b {}
+ if a - b > b {}
+ if b < a - b {}
+ if a - b > a {}
+ if a < a - b {}
+ if a + b < c {}
+ if c > a + b {}
+ if a - b < c {}
+ if c > a - b {}
+ let i = 1.1;
+ let j = 2.2;
+ if i + j < i {}
+ if i - j < i {}
+ if i > i + j {}
+ if i - j < i {}
+}
--- /dev/null
- --> $DIR/overflow_check_conditional.rs:8:8
+error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:9:8
++ --> $DIR/overflow_check_conditional.rs:7:8
+ |
+LL | if a + b < a {}
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:10:8
++ --> $DIR/overflow_check_conditional.rs:8:8
+ |
+LL | if a > a + b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:11:8
++ --> $DIR/overflow_check_conditional.rs:9:8
+ |
+LL | if a + b < b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:12:8
++ --> $DIR/overflow_check_conditional.rs:10:8
+ |
+LL | if b > a + b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:13:8
++ --> $DIR/overflow_check_conditional.rs:11:8
+ |
+LL | if a - b > b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:14:8
++ --> $DIR/overflow_check_conditional.rs:12:8
+ |
+LL | if b < a - b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:15:8
++ --> $DIR/overflow_check_conditional.rs:13:8
+ |
+LL | if a - b > a {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
++ --> $DIR/overflow_check_conditional.rs:14:8
+ |
+LL | if a < a - b {}
+ | ^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
--- /dev/null
- #![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
++#![allow(
++ unused,
++ clippy::many_single_char_names,
++ clippy::redundant_clone,
++ clippy::if_then_panic
++)]
+#![warn(clippy::ptr_arg)]
+
+use std::borrow::Cow;
+use std::path::PathBuf;
+
+fn do_vec(x: &Vec<i64>) {
+ //Nothing here
+}
+
+fn do_vec_mut(x: &mut Vec<i64>) {
+ // no error here
+ //Nothing here
+}
+
+fn do_str(x: &String) {
+ //Nothing here either
+}
+
+fn do_str_mut(x: &mut String) {
+ // no error here
+ //Nothing here either
+}
+
+fn do_path(x: &PathBuf) {
+ //Nothing here either
+}
+
+fn do_path_mut(x: &mut PathBuf) {
+ // no error here
+ //Nothing here either
+}
+
+fn main() {}
+
+trait Foo {
+ type Item;
+ fn do_vec(x: &Vec<i64>);
+ fn do_item(x: &Self::Item);
+}
+
+struct Bar;
+
+// no error, in trait impl (#425)
+impl Foo for Bar {
+ type Item = Vec<u8>;
+ fn do_vec(x: &Vec<i64>) {}
+ fn do_item(x: &Vec<u8>) {}
+}
+
+fn cloned(x: &Vec<u8>) -> Vec<u8> {
+ let e = x.clone();
+ let f = e.clone(); // OK
+ let g = x;
+ let h = g.clone(); // Alas, we cannot reliably detect this without following data.
+ let i = (e).clone();
+ x.clone()
+}
+
+fn str_cloned(x: &String) -> String {
+ let a = x.clone();
+ let b = x.clone();
+ let c = b.clone();
+ let d = a.clone().clone().clone();
+ x.clone()
+}
+
+fn path_cloned(x: &PathBuf) -> PathBuf {
+ let a = x.clone();
+ let b = x.clone();
+ let c = b.clone();
+ let d = a.clone().clone().clone();
+ x.clone()
+}
+
+fn false_positive_capacity(x: &Vec<u8>, y: &String) {
+ let a = x.capacity();
+ let b = y.clone();
+ let c = y.as_str();
+}
+
+fn false_positive_capacity_too(x: &String) -> String {
+ if x.capacity() > 1024 {
+ panic!("Too large!");
+ }
+ x.clone()
+}
+
+#[allow(dead_code)]
+fn test_cow_with_ref(c: &Cow<[i32]>) {}
+
+fn test_cow(c: Cow<[i32]>) {
+ let _c = c;
+}
+
+trait Foo2 {
+ fn do_string(&self);
+}
+
+// no error for &self references where self is of type String (#2293)
+impl Foo2 for String {
+ fn do_string(&self) {}
+}
+
+// Check that the allow attribute on parameters is honored
+mod issue_5644 {
+ use std::borrow::Cow;
+ use std::path::PathBuf;
+
+ fn allowed(
+ #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+ #[allow(clippy::ptr_arg)] _s: &String,
+ #[allow(clippy::ptr_arg)] _p: &PathBuf,
+ #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+ ) {
+ }
+
+ struct S {}
+ impl S {
+ fn allowed(
+ #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+ #[allow(clippy::ptr_arg)] _s: &String,
+ #[allow(clippy::ptr_arg)] _p: &PathBuf,
+ #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+ ) {
+ }
+ }
+
+ trait T {
+ fn allowed(
+ #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+ #[allow(clippy::ptr_arg)] _s: &String,
+ #[allow(clippy::ptr_arg)] _p: &PathBuf,
+ #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+ ) {
+ }
+ }
+}
+
+mod issue6509 {
+ use std::path::PathBuf;
+
+ fn foo_vec(vec: &Vec<u8>) {
+ let _ = vec.clone().pop();
+ let _ = vec.clone().clone();
+ }
+
+ fn foo_path(path: &PathBuf) {
+ let _ = path.clone().pop();
+ let _ = path.clone().clone();
+ }
+
+ fn foo_str(str: &PathBuf) {
+ let _ = str.clone().pop();
+ let _ = str.clone().clone();
+ }
+}
--- /dev/null
- --> $DIR/ptr_arg.rs:7:14
+error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:16:14
++ --> $DIR/ptr_arg.rs:12:14
+ |
+LL | fn do_vec(x: &Vec<i64>) {
+ | ^^^^^^^^^ help: change this to: `&[i64]`
+ |
+ = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:25:15
++ --> $DIR/ptr_arg.rs:21:14
+ |
+LL | fn do_str(x: &String) {
+ | ^^^^^^^ help: change this to: `&str`
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:38:18
++ --> $DIR/ptr_arg.rs:30:15
+ |
+LL | fn do_path(x: &PathBuf) {
+ | ^^^^^^^^ help: change this to: `&Path`
+
+error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:51:14
++ --> $DIR/ptr_arg.rs:43:18
+ |
+LL | fn do_vec(x: &Vec<i64>);
+ | ^^^^^^^^^ help: change this to: `&[i64]`
+
+error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:60:18
++ --> $DIR/ptr_arg.rs:56:14
+ |
+LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL | fn cloned(x: &[u8]) -> Vec<u8> {
+ | ~~~~~
+help: change `x.clone()` to
+ |
+LL | let e = x.to_owned();
+ | ~~~~~~~~~~~~
+help: change `x.clone()` to
+ |
+LL | x.to_owned()
+ |
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:68:19
++ --> $DIR/ptr_arg.rs:65:18
+ |
+LL | fn str_cloned(x: &String) -> String {
+ | ^^^^^^^
+ |
+help: change this to
+ |
+LL | fn str_cloned(x: &str) -> String {
+ | ~~~~
+help: change `x.clone()` to
+ |
+LL | let a = x.to_string();
+ | ~~~~~~~~~~~~~
+help: change `x.clone()` to
+ |
+LL | let b = x.to_string();
+ | ~~~~~~~~~~~~~
+help: change `x.clone()` to
+ |
+LL | x.to_string()
+ |
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:76:44
++ --> $DIR/ptr_arg.rs:73:19
+ |
+LL | fn path_cloned(x: &PathBuf) -> PathBuf {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL | fn path_cloned(x: &Path) -> PathBuf {
+ | ~~~~~
+help: change `x.clone()` to
+ |
+LL | let a = x.to_path_buf();
+ | ~~~~~~~~~~~~~~~
+help: change `x.clone()` to
+ |
+LL | let b = x.to_path_buf();
+ | ~~~~~~~~~~~~~~~
+help: change `x.clone()` to
+ |
+LL | x.to_path_buf()
+ |
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:90:25
++ --> $DIR/ptr_arg.rs:81:44
+ |
+LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
+ | ^^^^^^^
+ |
+help: change this to
+ |
+LL | fn false_positive_capacity(x: &Vec<u8>, y: &str) {
+ | ~~~~
+help: change `y.clone()` to
+ |
+LL | let b = y.to_string();
+ | ~~~~~~~~~~~~~
+help: change `y.as_str()` to
+ |
+LL | let c = y;
+ | ~
+
+error: using a reference to `Cow` is not recommended
- --> $DIR/ptr_arg.rs:143:21
++ --> $DIR/ptr_arg.rs:95:25
+ |
+LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
+ | ^^^^^^^^^^^ help: change this to: `&[i32]`
+
+error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:148:23
++ --> $DIR/ptr_arg.rs:148:21
+ |
+LL | fn foo_vec(vec: &Vec<u8>) {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL | fn foo_vec(vec: &[u8]) {
+ | ~~~~~
+help: change `vec.clone()` to
+ |
+LL | let _ = vec.to_owned().pop();
+ | ~~~~~~~~~~~~~~
+help: change `vec.clone()` to
+ |
+LL | let _ = vec.to_owned().clone();
+ | ~~~~~~~~~~~~~~
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:153:21
++ --> $DIR/ptr_arg.rs:153:23
+ |
+LL | fn foo_path(path: &PathBuf) {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL | fn foo_path(path: &Path) {
+ | ~~~~~
+help: change `path.clone()` to
+ |
+LL | let _ = path.to_path_buf().pop();
+ | ~~~~~~~~~~~~~~~~~~
+help: change `path.clone()` to
+ |
+LL | let _ = path.to_path_buf().clone();
+ | ~~~~~~~~~~~~~~~~~~
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
++ --> $DIR/ptr_arg.rs:158:21
+ |
+LL | fn foo_str(str: &PathBuf) {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL | fn foo_str(str: &Path) {
+ | ~~~~~
+help: change `str.clone()` to
+ |
+LL | let _ = str.to_path_buf().pop();
+ | ~~~~~~~~~~~~~~~~~
+help: change `str.clone()` to
+ |
+LL | let _ = str.to_path_buf().clone();
+ | ~~~~~~~~~~~~~~~~~
+
+error: aborting due to 12 previous errors
+
--- /dev/null
- #[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+// run-rustfix
+#![warn(clippy::repeat_once)]
++#[allow(unused, clippy::redundant_clone)]
+fn main() {
+ const N: usize = 1;
+ let s = "str";
+ let string = "String".to_string();
+ let slice = [1; 5];
+
+ let a = [1; 5].to_vec();
+ let b = slice.to_vec();
+ let c = "hello".to_string();
+ let d = "hi".to_string();
+ let e = s.to_string();
+ let f = string.clone();
+}
--- /dev/null
- #[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+// run-rustfix
+#![warn(clippy::repeat_once)]
++#[allow(unused, clippy::redundant_clone)]
+fn main() {
+ const N: usize = 1;
+ let s = "str";
+ let string = "String".to_string();
+ let slice = [1; 5];
+
+ let a = [1; 5].repeat(1);
+ let b = slice.repeat(1);
+ let c = "hello".repeat(N);
+ let d = "hi".repeat(1);
+ let e = s.repeat(1);
+ let f = string.repeat(1);
+}
--- /dev/null
--- /dev/null
++#![warn(clippy::same_name_method)]
++#![allow(dead_code, non_camel_case_types)]
++
++trait T1 {
++ fn foo() {}
++}
++
++trait T2 {
++ fn foo() {}
++}
++
++mod should_lint {
++
++ mod test_basic_case {
++ use crate::T1;
++
++ struct S;
++
++ impl S {
++ fn foo() {}
++ }
++
++ impl T1 for S {
++ fn foo() {}
++ }
++ }
++
++ mod test_derive {
++
++ #[derive(Clone)]
++ struct S;
++
++ impl S {
++ fn clone() {}
++ }
++ }
++
++ mod with_generic {
++ use crate::T1;
++
++ struct S<U>(U);
++
++ impl<U> S<U> {
++ fn foo() {}
++ }
++
++ impl<U: Copy> T1 for S<U> {
++ fn foo() {}
++ }
++ }
++
++ mod default_method {
++ use crate::T1;
++
++ struct S;
++
++ impl S {
++ fn foo() {}
++ }
++
++ impl T1 for S {}
++ }
++
++ mod mulitply_conflicit_trait {
++ use crate::{T1, T2};
++
++ struct S;
++
++ impl S {
++ fn foo() {}
++ }
++
++ impl T1 for S {}
++
++ impl T2 for S {}
++ }
++}
++
++mod should_not_lint {
++
++ mod not_lint_two_trait_method {
++ use crate::{T1, T2};
++
++ struct S;
++
++ impl T1 for S {
++ fn foo() {}
++ }
++
++ impl T2 for S {
++ fn foo() {}
++ }
++ }
++
++ mod only_lint_on_method {
++ trait T3 {
++ type foo;
++ }
++
++ struct S;
++
++ impl S {
++ fn foo() {}
++ }
++ impl T3 for S {
++ type foo = usize;
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: method's name is same to an existing method in a trait
++ --> $DIR/same_name_method.rs:20:13
++ |
++LL | fn foo() {}
++ | ^^^^^^^^^^^
++ |
++ = note: `-D clippy::same-name-method` implied by `-D warnings`
++note: existing `foo` defined here
++ --> $DIR/same_name_method.rs:24:13
++ |
++LL | fn foo() {}
++ | ^^^^^^^^^^^
++
++error: method's name is same to an existing method in a trait
++ --> $DIR/same_name_method.rs:44:13
++ |
++LL | fn foo() {}
++ | ^^^^^^^^^^^
++ |
++note: existing `foo` defined here
++ --> $DIR/same_name_method.rs:48:13
++ |
++LL | fn foo() {}
++ | ^^^^^^^^^^^
++
++error: method's name is same to an existing method in a trait
++ --> $DIR/same_name_method.rs:58:13
++ |
++LL | fn foo() {}
++ | ^^^^^^^^^^^
++ |
++note: existing `foo` defined here
++ --> $DIR/same_name_method.rs:61:9
++ |
++LL | impl T1 for S {}
++ | ^^^^^^^^^^^^^^^^
++
++error: method's name is same to an existing method in a trait
++ --> $DIR/same_name_method.rs:70:13
++ |
++LL | fn foo() {}
++ | ^^^^^^^^^^^
++ |
++note: existing `foo` defined here
++ --> $DIR/same_name_method.rs:73:9
++ |
++LL | impl T1 for S {}
++ | ^^^^^^^^^^^^^^^^
++
++error: method's name is same to an existing method in a trait
++ --> $DIR/same_name_method.rs:34:13
++ |
++LL | fn clone() {}
++ | ^^^^^^^^^^^^^
++ |
++note: existing `clone` defined here
++ --> $DIR/same_name_method.rs:30:18
++ |
++LL | #[derive(Clone)]
++ | ^^^^^
++ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 5 previous errors
++
--- /dev/null
- let _d = || unsafe {
- ptr::drop_in_place(s.as_mut_ptr())
+#![warn(clippy::semicolon_if_nothing_returned)]
++#![allow(clippy::redundant_closure)]
+#![feature(label_break_value)]
+
+fn get_unit() {}
+
+// the functions below trigger the lint
+fn main() {
+ println!("Hello")
+}
+
+fn hello() {
+ get_unit()
+}
+
+fn basic101(x: i32) {
+ let y: i32;
+ y = x + 1
+}
+
+#[rustfmt::skip]
+fn closure_error() {
+ let _d = || {
+ hello()
+ };
+}
+
+#[rustfmt::skip]
+fn unsafe_checks_error() {
+ use std::mem::MaybeUninit;
+ use std::ptr;
+
+ let mut s = MaybeUninit::<String>::uninit();
++ let _d = || unsafe {
++ ptr::drop_in_place(s.as_mut_ptr())
+ };
+}
+
+// this is fine
+fn print_sum(a: i32, b: i32) {
+ println!("{}", a + b);
+ assert_eq!(true, false);
+}
+
+fn foo(x: i32) {
+ let y: i32;
+ if x < 1 {
+ y = 4;
+ } else {
+ y = 5;
+ }
+}
+
+fn bar(x: i32) {
+ let y: i32;
+ match x {
+ 1 => y = 4,
+ _ => y = 32,
+ }
+}
+
+fn foobar(x: i32) {
+ let y: i32;
+ 'label: {
+ y = x + 1;
+ }
+}
+
+fn loop_test(x: i32) {
+ let y: i32;
+ for &ext in &["stdout", "stderr", "fixed"] {
+ println!("{}", ext);
+ }
+}
+
+fn closure() {
+ let _d = || hello();
+}
+
+#[rustfmt::skip]
+fn closure_block() {
+ let _d = || { hello() };
+}
+
+unsafe fn some_unsafe_op() {}
+unsafe fn some_other_unsafe_fn() {}
+
+fn do_something() {
+ unsafe { some_unsafe_op() };
+
+ unsafe { some_other_unsafe_fn() };
+}
+
+fn unsafe_checks() {
+ use std::mem::MaybeUninit;
+ use std::ptr;
+
+ let mut s = MaybeUninit::<String>::uninit();
+ let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) };
+}
--- /dev/null
- --> $DIR/semicolon_if_nothing_returned.rs:8:5
+error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:12:5
++ --> $DIR/semicolon_if_nothing_returned.rs:9:5
+ |
+LL | println!("Hello")
+ | ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
+ |
+ = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
+
+error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:17:5
++ --> $DIR/semicolon_if_nothing_returned.rs:13:5
+ |
+LL | get_unit()
+ | ^^^^^^^^^^ help: add a `;` here: `get_unit();`
+
+error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:23:9
++ --> $DIR/semicolon_if_nothing_returned.rs:18:5
+ |
+LL | y = x + 1
+ | ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
+
+error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:34:9
++ --> $DIR/semicolon_if_nothing_returned.rs:24:9
+ |
+LL | hello()
+ | ^^^^^^^ help: add a `;` here: `hello();`
+
+error: consider adding a `;` to the last statement for consistent formatting
- LL | ptr::drop_in_place(s.as_mut_ptr())
++ --> $DIR/semicolon_if_nothing_returned.rs:35:9
+ |
++LL | ptr::drop_in_place(s.as_mut_ptr())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
+
+error: aborting due to 5 previous errors
+
--- /dev/null
++// aux-build:proc_macro_suspicious_else_formatting.rs
++
+#![warn(clippy::suspicious_else_formatting)]
+
++extern crate proc_macro_suspicious_else_formatting;
++use proc_macro_suspicious_else_formatting::DeriveBadSpan;
++
+fn foo() -> bool {
+ true
+}
+
+#[rustfmt::skip]
+fn main() {
+ // weird `else` formatting:
+ if foo() {
+ } {
+ }
+
+ if foo() {
+ } if foo() {
+ }
+
+ let _ = { // if as the last expression
+ let _ = 0;
+
+ if foo() {
+ } if foo() {
+ }
+ else {
+ }
+ };
+
+ let _ = { // if in the middle of a block
+ if foo() {
+ } if foo() {
+ }
+ else {
+ }
+
+ let _ = 0;
+ };
+
+ if foo() {
+ } else
+ {
+ }
+
+ // This is fine, though weird. Allman style braces on the else.
+ if foo() {
+ }
+ else
+ {
+ }
+
+ if foo() {
+ } else
+ if foo() { // the span of the above error should continue here
+ }
+
+ if foo() {
+ }
+ else
+ if foo() { // the span of the above error should continue here
+ }
+
+ // those are ok:
+ if foo() {
+ }
+ {
+ }
+
+ if foo() {
+ } else {
+ }
+
+ if foo() {
+ }
+ else {
+ }
+
+ if foo() {
+ }
+ if foo() {
+ }
+
+ // Almost Allman style braces. Lint these.
+ if foo() {
+ }
+
+ else
+ {
+
+ }
+
+ if foo() {
+ }
+ else
+
+ {
+
+ }
+
+ // #3864 - Allman style braces
+ if foo()
+ {
+ }
+ else
+ {
+ }
+}
++
++// #7650 - Don't lint. Proc-macro using bad spans for `if` expressions.
++#[derive(DeriveBadSpan)]
++struct _Foo(u32, u32);
--- /dev/null
- --> $DIR/suspicious_else_formatting.rs:11:6
+error: this looks like an `else {..}` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:15:6
++ --> $DIR/suspicious_else_formatting.rs:16:6
+ |
+LL | } {
+ | ^
+ |
+ = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
+ = note: to remove this lint, add the missing `else` or add a new line before the next block
+
+error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:22:10
++ --> $DIR/suspicious_else_formatting.rs:20:6
+ |
+LL | } if foo() {
+ | ^
+ |
+ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:30:10
++ --> $DIR/suspicious_else_formatting.rs:27:10
+ |
+LL | } if foo() {
+ | ^
+ |
+ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:39:6
++ --> $DIR/suspicious_else_formatting.rs:35:10
+ |
+LL | } if foo() {
+ | ^
+ |
+ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:51:6
++ --> $DIR/suspicious_else_formatting.rs:44:6
+ |
+LL | } else
+ | ______^
+LL | | {
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:56:6
++ --> $DIR/suspicious_else_formatting.rs:56:6
+ |
+LL | } else
+ | ______^
+LL | | if foo() { // the span of the above error should continue here
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+
+error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:83:6
++ --> $DIR/suspicious_else_formatting.rs:61:6
+ |
+LL | }
+ | ______^
+LL | | else
+LL | | if foo() { // the span of the above error should continue here
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+
+error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:91:6
++ --> $DIR/suspicious_else_formatting.rs:88:6
+ |
+LL | }
+ | ______^
+LL | |
+LL | | else
+LL | | {
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else {..}` but the formatting might hide it
++ --> $DIR/suspicious_else_formatting.rs:96:6
+ |
+LL | }
+ | ______^
+LL | | else
+LL | |
+LL | | {
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: aborting due to 9 previous errors
+
--- /dev/null
- #![allow(
- clippy::many_single_char_names,
- clippy::blacklisted_name,
- clippy::redundant_field_names
- )]
+// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
+// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
+
+#![deny(clippy::trivially_copy_pass_by_ref)]
++#![allow(clippy::blacklisted_name, clippy::redundant_field_names)]
+
+#[derive(Copy, Clone)]
+struct Foo(u32);
+
+#[derive(Copy, Clone)]
+struct Bar([u8; 24]);
+
+#[derive(Copy, Clone)]
+pub struct Color {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+ pub a: u8,
+}
+
+struct FooRef<'a> {
+ foo: &'a Foo,
+}
+
+type Baz = u32;
+
+fn good(a: &mut u32, b: u32, c: &Bar) {}
+
+fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 {
+ &foo.0
+}
+
+#[allow(clippy::needless_lifetimes)]
+fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
+ &foo.0
+}
+
+fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
+ FooRef { foo }
+}
+
+#[allow(clippy::needless_lifetimes)]
+fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> {
+ FooRef { foo }
+}
+
+fn bad(x: &u32, y: &Foo, z: &Baz) {}
+
+impl Foo {
+ fn good(self, a: &mut u32, b: u32, c: &Bar) {}
+
+ fn good2(&mut self) {}
+
+ fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+
+ fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+
+ fn bad_issue7518(self, other: &Self) {}
+}
+
+impl AsRef<u32> for Foo {
+ fn as_ref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+impl Bar {
+ fn good(&self, a: &mut u32, b: u32, c: &Bar) {}
+
+ fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+}
+
+trait MyTrait {
+ fn trait_method(&self, _foo: &Foo);
+}
+
+pub trait MyTrait2 {
+ fn trait_method2(&self, _color: &Color);
+}
+
+impl MyTrait for Foo {
+ fn trait_method(&self, _foo: &Foo) {
+ unimplemented!()
+ }
+}
+
+#[allow(unused_variables)]
+mod issue3992 {
+ pub trait A {
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ fn a(b: &u16) {}
+ }
+
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn c(d: &u16) {}
+}
+
+mod issue5876 {
+ // Don't lint here as it is always inlined
+ #[inline(always)]
+ fn foo_always(x: &i32) {
+ println!("{}", x);
+ }
+
+ #[inline(never)]
+ fn foo_never(x: &i32) {
+ println!("{}", x);
+ }
+
+ #[inline]
+ fn foo(x: &i32) {
+ println!("{}", x);
+ }
+}
+
+fn main() {
+ let (mut foo, bar) = (Foo(0), Bar([0; 24]));
+ let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
+ good(&mut a, b, &c);
+ good_return_implicit_lt_ref(&y);
+ good_return_explicit_lt_ref(&y);
+ bad(&x, &y, &z);
+ foo.good(&mut a, b, &c);
+ foo.good2();
+ foo.bad(&x, &y, &z);
+ Foo::bad2(&x, &y, &z);
+ bar.good(&mut a, b, &c);
+ Bar::bad2(&x, &y, &z);
+ foo.as_ref();
+}
--- /dev/null
- --> $DIR/trivially_copy_pass_by_ref.rs:51:11
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:51:20
++ --> $DIR/trivially_copy_pass_by_ref.rs:47:11
+ |
+LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+ |
+note: the lint level is defined here
+ --> $DIR/trivially_copy_pass_by_ref.rs:4:9
+ |
+LL | #![deny(clippy::trivially_copy_pass_by_ref)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:51:29
++ --> $DIR/trivially_copy_pass_by_ref.rs:47:20
+ |
+LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:12
++ --> $DIR/trivially_copy_pass_by_ref.rs:47:29
+ |
+LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:22
++ --> $DIR/trivially_copy_pass_by_ref.rs:54:12
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^^ help: consider passing by value instead: `self`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:31
++ --> $DIR/trivially_copy_pass_by_ref.rs:54:22
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:40
++ --> $DIR/trivially_copy_pass_by_ref.rs:54:31
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:60:16
++ --> $DIR/trivially_copy_pass_by_ref.rs:54:40
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:60:25
++ --> $DIR/trivially_copy_pass_by_ref.rs:56:16
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:60:34
++ --> $DIR/trivially_copy_pass_by_ref.rs:56:25
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:62:35
++ --> $DIR/trivially_copy_pass_by_ref.rs:56:34
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:74:16
++ --> $DIR/trivially_copy_pass_by_ref.rs:58:35
+ |
+LL | fn bad_issue7518(self, other: &Self) {}
+ | ^^^^^ help: consider passing by value instead: `Self`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:74:25
++ --> $DIR/trivially_copy_pass_by_ref.rs:70:16
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:74:34
++ --> $DIR/trivially_copy_pass_by_ref.rs:70:25
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:78:34
++ --> $DIR/trivially_copy_pass_by_ref.rs:70:34
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:110:21
++ --> $DIR/trivially_copy_pass_by_ref.rs:74:34
+ |
+LL | fn trait_method(&self, _foo: &Foo);
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:115:15
++ --> $DIR/trivially_copy_pass_by_ref.rs:106:21
+ |
+LL | fn foo_never(x: &i32) {
+ | ^^^^ help: consider passing by value instead: `i32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:111:15
+ |
+LL | fn foo(x: &i32) {
+ | ^^^^ help: consider passing by value instead: `i32`
+
+error: aborting due to 17 previous errors
+
--- /dev/null
- for m in &mut it {
+// run-rustfix
+
+#![warn(clippy::while_let_on_iterator)]
+#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)]
+
+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 &mut it {
++ 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 &mut it {
++ 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 i in &mut self.0 {
++ 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 &mut self.0.0.0 {
++ 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 n in &mut it {
++ 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 x in &mut it {
++ 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 &mut *it {
++ 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 &mut *it.0 {
++ 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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:8: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:13: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:18: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:94: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:101: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:114: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
+ |
+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:191:9
+ |
+LL | while let Some(m) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:202: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:204: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:213: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
+ |
+LL | while let Some(m) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:239:9
+ |
+LL | while let Some(m) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
+ |
+LL | while let Some(i) = self.0.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/while_let_on_iterator.rs:255: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:286:13
+ |
+LL | while let Some(i) = self.0.0.0.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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:315:5
+ |
+LL | while let Some(n) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:327:9
+ |
+LL | while let Some(x) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:341:5
+ |
+LL | while let Some(x) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it.0`
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
+ |
+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:371:5
+ |
+LL | while let Some(..) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
+
+error: aborting due to 21 previous errors
+
--- /dev/null
+// edition:2018
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+mod issue6983 {
+ pub struct Thing;
+ pub trait Trait {
+ fn to_thing(&self) -> Thing;
+ }
+
+ impl Trait for u8 {
+ // don't trigger, e.g. `ToString` from `std` requires `&self`
+ fn to_thing(&self) -> Thing {
+ Thing
+ }
+ }
+
+ trait ToU64 {
+ fn to_u64(self) -> u64;
+ }
+
+ struct FooNoCopy;
+ // don't trigger
+ impl ToU64 for FooNoCopy {
+ fn to_u64(self) -> u64 {
+ 2
+ }
+ }
+}
+
+mod issue7032 {
+ trait Foo {
+ fn from_usize(x: usize) -> Self;
+ }
+ // don't trigger
+ impl Foo for usize {
+ fn from_usize(x: usize) -> Self {
+ x
+ }
+ }
+}
+
+mod issue7179 {
+ pub struct S(i32);
+
+ impl S {
+ // don't trigger (`s` is not `self`)
+ pub fn from_be(s: Self) -> Self {
+ S(i32::from_be(s.0))
+ }
+
+ // lint
+ pub fn from_be_self(self) -> Self {
+ S(i32::from_be(self.0))
+ }
+ }
+
+ trait T {
+ // don't trigger (`s` is not `self`)
+ fn from_be(s: Self) -> Self;
+ // lint
+ fn from_be_self(self) -> Self;
+ }
+
+ trait Foo: Sized {
+ fn as_byte_slice(slice: &[Self]) -> &[u8];
+ }
+}
++
++mod issue3414 {
++ struct CellLikeThing<T>(T);
++
++ impl<T> CellLikeThing<T> {
++ // don't trigger
++ fn into_inner(this: Self) -> T {
++ this.0
++ }
++ }
++
++ impl<T> std::ops::Deref for CellLikeThing<T> {
++ type Target = T;
++
++ fn deref(&self) -> &T {
++ &self.0
++ }
++ }
++}
++
++// don't trigger
++mod issue4546 {
++ use std::pin::Pin;
++
++ struct S;
++ impl S {
++ pub fn as_mut(self: Pin<&mut Self>) {}
++
++ pub fn as_other_thingy(self: Pin<&Self>) {}
++
++ pub fn is_other_thingy(self: Pin<&Self>) {}
++
++ pub fn to_mut(self: Pin<&mut Self>) {}
++
++ pub fn to_other_thingy(self: Pin<&Self>) {}
++ }
++}