--- /dev/null
--- /dev/null
++[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
- ln -sf "$TAG_NAME" out/stable
+#!/bin/bash
+
+set -ex
+
+echo "Removing the current docs for master"
+rm -rf out/master/ || exit 0
+
+echo "Making the docs for master"
+mkdir out/master/
+cp util/gh-pages/index.html out/master
+cp util/gh-pages/lints.json out/master
+
+if [[ -n $TAG_NAME ]]; then
+ echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it"
+ cp -Tr out/master "out/$TAG_NAME"
++ rm -f out/stable
++ ln -s "$TAG_NAME" out/stable
+fi
+
+if [[ $BETA = "true" ]]; then
+ echo "Update documentation for the beta release"
+ cp -r out/master/* out/beta
+fi
+
+# Generate version index that is shown as root index page
+cp util/gh-pages/versions.html out/index.html
+
+echo "Making the versions.json file"
+python3 ./util/versions.py out
+
+# Now let's go have some fun with the cloned repo
+cd out
+git config user.name "GHA CI"
+git config user.email "gha@ci.invalid"
+
+if [[ -n $TAG_NAME ]]; then
+ # track files, so that the following check works
+ git add --intent-to-add "$TAG_NAME"
+ if git diff --exit-code --quiet -- $TAG_NAME/; then
+ echo "No changes to the output on this push; exiting."
+ exit 0
+ fi
+ # Add the new dir
+ git add "$TAG_NAME"
+ # Update the symlink
+ git add stable
+ # Update versions file
+ git add versions.json
+ git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
+elif [[ $BETA = "true" ]]; then
+ if git diff --exit-code --quiet -- beta/; then
+ echo "No changes to the output on this push; exiting."
+ exit 0
+ fi
+ git add beta
+ git commit -m "Automatic deploy to GitHub Pages (beta): ${SHA}"
+else
+ if git diff --exit-code --quiet; then
+ echo "No changes to the output on this push; exiting."
+ exit 0
+ fi
+ git add .
+ git commit -m "Automatic deploy to GitHub Pages: ${SHA}"
+fi
+
+git push "$SSH_REPO" "$TARGET_BRANCH"
--- /dev/null
- [7bfc26e...master](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...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-10-21
++[b7f3f7f...master](https://github.com/rust-lang/rust-clippy/compare/b7f3f7f...master)
++
++## Rust 1.57
++
++Current beta, release 2021-12-02
++
++[7bfc26e...b7f3f7f](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...b7f3f7f)
++
++### New Lints
++
++* [`negative_feature_names`]
++ [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
++* [`redundant_feature_names`]
++ [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
++* [`mod_module_files`]
++ [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
++* [`self_named_module_files`]
++ [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
++* [`manual_split_once`]
++ [#7565](https://github.com/rust-lang/rust-clippy/pull/7565)
++* [`derivable_impls`]
++ [#7570](https://github.com/rust-lang/rust-clippy/pull/7570)
++* [`needless_option_as_deref`]
++ [#7596](https://github.com/rust-lang/rust-clippy/pull/7596)
++* [`iter_not_returning_iterator`]
++ [#7610](https://github.com/rust-lang/rust-clippy/pull/7610)
++* [`same_name_method`]
++ [#7653](https://github.com/rust-lang/rust-clippy/pull/7653)
++* [`manual_assert`] [#7669](https://github.com/rust-lang/rust-clippy/pull/7669)
++* [`non_send_fields_in_send_ty`]
++ [#7709](https://github.com/rust-lang/rust-clippy/pull/7709)
++* [`equatable_if_let`]
++ [#7762](https://github.com/rust-lang/rust-clippy/pull/7762)
++
++### Moves and Deprecations
++
++* Move [`shadow_unrelated`] to `restriction`
++ [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
++* Move [`option_if_let_else`] to `nursery`
++ [#7568](https://github.com/rust-lang/rust-clippy/pull/7568)
++* Move [`branches_sharing_code`] to `nursery`
++ [#7595](https://github.com/rust-lang/rust-clippy/pull/7595)
++* Rename `if_let_some_result` to [`match_result_ok`] which now also handles
++ `while let` cases [#7608](https://github.com/rust-lang/rust-clippy/pull/7608)
++* Move [`many_single_char_names`] to `pedantic`
++ [#7671](https://github.com/rust-lang/rust-clippy/pull/7671)
++* Move [`float_cmp`] to `pedantic`
++ [#7692](https://github.com/rust-lang/rust-clippy/pull/7692)
++* Rename `box_vec` to [`box_collection`] and lint on more general cases
++ [#7693](https://github.com/rust-lang/rust-clippy/pull/7693)
++* Uplift `invalid_atomic_ordering` to rustc
++ [rust-lang/rust#84039](https://github.com/rust-lang/rust/pull/84039)
++
++### Enhancements
++
++* Rewrite the `shadow*` lints, so that they find a lot more shadows and are not
++ limited to certain patterns
++ [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
++* The `avoid-breaking-exported-api` configuration now also works for
++ [`box_collection`], [`redundant_allocation`], [`rc_buffer`], [`vec_box`],
++ [`option_option`], [`linkedlist`], [`rc_mutex`]
++ [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
++* [`unnecessary_unwrap`]: Now also checks for `expect`s
++ [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
++* [`disallowed_method`]: Allow adding a reason that will be displayed with the
++ lint message
++ [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
++* [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
++ [#7629](https://github.com/rust-lang/rust-clippy/pull/7629)
++* [`approx_constant`]: Add `TAU`
++ [#7642](https://github.com/rust-lang/rust-clippy/pull/7642)
++* [`needless_borrow`]: Now also lints on needless mutable borrows
++ [#7657](https://github.com/rust-lang/rust-clippy/pull/7657)
++* [`missing_safety_doc`]: Now also lints on unsafe traits
++ [#7734](https://github.com/rust-lang/rust-clippy/pull/7734)
++
++### False Positive Fixes
++
++* [`manual_map`]: No longer lints when the option is borrowed in the match and
++ also consumed in the arm
++ [#7531](https://github.com/rust-lang/rust-clippy/pull/7531)
++* [`filter_next`]: No longer lints if `filter` method is not the
++ `Iterator::filter` method
++ [#7562](https://github.com/rust-lang/rust-clippy/pull/7562)
++* [`manual_flatten`]: No longer lints if expression is used after `if let`
++ [#7566](https://github.com/rust-lang/rust-clippy/pull/7566)
++* [`option_if_let_else`]: Multiple fixes
++ [#7573](https://github.com/rust-lang/rust-clippy/pull/7573)
++ * `break` and `continue` statements local to the would-be closure are
++ allowed
++ * Don't lint in const contexts
++ * Don't lint when yield expressions are used
++ * Don't lint when the captures made by the would-be closure conflict with
++ the other branch
++ * Don't lint when a field of a local is used when the type could be
++ potentially moved from
++ * In some cases, don't lint when scrutinee expression conflicts with the
++ captures of the would-be closure
++* [`redundant_allocation`]: No longer lints on `Box<Box<dyn T>>` which replaces
++ wide pointers with thin pointers
++ [#7592](https://github.com/rust-lang/rust-clippy/pull/7592)
++* [`bool_assert_comparison`]: No longer lints on types that do not implement the
++ `Not` trait with `Output = bool`
++ [#7605](https://github.com/rust-lang/rust-clippy/pull/7605)
++* [`mut_range_bound`]: No longer lints on range bound mutations, that are
++ immediately followed by a `break;`
++ [#7607](https://github.com/rust-lang/rust-clippy/pull/7607)
++* [`mutable_key_type`]: Improve accuracy and document remaining false positives
++ and false negatives
++ [#7640](https://github.com/rust-lang/rust-clippy/pull/7640)
++* [`redundant_closure`]: Rewrite the lint to fix various false positives and
++ false negatives [#7661](https://github.com/rust-lang/rust-clippy/pull/7661)
++* [`large_enum_variant`]: No longer wrongly identifies the second largest
++ variant [#7677](https://github.com/rust-lang/rust-clippy/pull/7677)
++* [`needless_return`]: No longer lints on let-else expressions
++ [#7685](https://github.com/rust-lang/rust-clippy/pull/7685)
++* [`suspicious_else_formatting`]: No longer lints in proc-macros
++ [#7707](https://github.com/rust-lang/rust-clippy/pull/7707)
++* [`excessive_precision`]: No longer lints when in some cases the float was
++ already written in the shortest form
++ [#7722](https://github.com/rust-lang/rust-clippy/pull/7722)
++* [`doc_markdown`]: No longer lints on intra-doc links
++ [#7772](https://github.com/rust-lang/rust-clippy/pull/7772)
++
++### Suggestion Fixes/Improvements
++
++* [`unnecessary_operation`]: Recommend using an `assert!` instead of using a
++ function call in an indexing operation
++ [#7453](https://github.com/rust-lang/rust-clippy/pull/7453)
++* [`manual_split_once`]: Produce semantically equivalent suggestion when
++ `rsplitn` is used [#7663](https://github.com/rust-lang/rust-clippy/pull/7663)
++* [`while_let_on_iterator`]: Produce correct suggestion when using `&mut`
++ [#7690](https://github.com/rust-lang/rust-clippy/pull/7690)
++* [`manual_assert`]: No better handles complex conditions
++ [#7741](https://github.com/rust-lang/rust-clippy/pull/7741)
++* Correctly handle signs in exponents in numeric literals lints
++ [#7747](https://github.com/rust-lang/rust-clippy/pull/7747)
++* [`suspicious_map`]: Now also suggests to use `inspect` as an alternative
++ [#7770](https://github.com/rust-lang/rust-clippy/pull/7770)
++* Drop exponent from suggestion if it is 0 in numeric literals lints
++ [#7774](https://github.com/rust-lang/rust-clippy/pull/7774)
++
++### ICE Fixes
++
++* [`implicit_hasher`]
++ [#7761](https://github.com/rust-lang/rust-clippy/pull/7761)
++
++### Others
++
++* Clippy now uses the 2021
++ [Edition!](https://www.youtube.com/watch?v=q0aNduqb2Ro)
++ [#7664](https://github.com/rust-lang/rust-clippy/pull/7664)
+
+## Rust 1.56
+
- ### New Lints
-
- * Renamed Lint: `if_let_some_result` is now called [`match_result_ok`]. Now also handles `while let` case.
-
++Current stable, released 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)
+
- Current stable, released 2021-09-09
+## Rust 1.55
+
- [`if_then_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_panic
++Released 2021-09-09
+
+[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
+
+### Important Changes
+
+* Stabilized `cargo clippy --fix` :tada:
+ [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
+
+### New Lints
+
+* [`rc_mutex`]
+ [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
+* [`nonstandard_macro_braces`]
+ [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
+* [`strlen_on_c_strings`]
+ [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
+* [`self_named_constructors`]
+ [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
+* [`disallowed_script_idents`]
+ [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
+* [`disallowed_type`]
+ [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
+* [`missing_enforced_import_renames`]
+ [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
+* [`extend_with_drain`]
+ [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
+
+### Moves and Deprecations
+
+* Moved [`from_iter_instead_of_collect`] to `pedantic`
+ [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
+* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
+ [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
+ * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
+ * Moved [`empty_loop`] to `suspicious`
+ * Moved [`eval_order_dependence`] to `suspicious`
+ * Moved [`float_equality_without_abs`] to `suspicious`
+ * Moved [`for_loops_over_fallibles`] to `suspicious`
+ * Moved [`misrefactored_assign_op`] to `suspicious`
+ * Moved [`mut_range_bound`] to `suspicious`
+ * Moved [`mutable_key_type`] to `suspicious`
+ * Moved [`suspicious_arithmetic_impl`] to `suspicious`
+ * Moved [`suspicious_assignment_formatting`] to `suspicious`
+ * Moved [`suspicious_else_formatting`] to `suspicious`
+ * Moved [`suspicious_map`] to `suspicious`
+ * Moved [`suspicious_op_assign_impl`] to `suspicious`
+ * Moved [`suspicious_unary_op_formatting`] to `suspicious`
+
+### Enhancements
+
+* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
+ [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
+* [`doc_markdown`]:
+ * Now detects unbalanced ticks
+ [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
+ * Add `FreeBSD` to the default configuration as an allowed identifier
+ [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
+* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
+ or hidden variants
+ [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
+* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
+ [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
+* [`blacklisted_name`]: Now allows blacklisted names in test code
+ [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
+* [`redundant_closure`]: Suggests `&mut` for `FnMut`
+ [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
+* [`disallowed_method`], [`disallowed_type`]: The configuration values `disallowed-method` and `disallowed-type`
+ no longer require fully qualified paths
+ [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
+* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
+ [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
+
+### False Positive Fixes
+
+* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
+ [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
+* [`use_self`]: No longer lints on type parameters
+ [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
+* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
+ [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
+* [`branches_sharing_code`]: Now always checks for block expressions
+ [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
+* [`field_reassign_with_default`]: No longer triggers in macros
+ [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
+* [`redundant_clone`]: No longer lints on required clones for borrowed data
+ [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
+* [`default_numeric_fallback`]: No longer triggers in external macros
+ [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
+* [`needless_bool`]: No longer lints in macros
+ [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
+* [`useless_format`]: No longer triggers when additional text is being appended
+ [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
+* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
+ [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
+
+### Suggestion Fixes/Improvements
+
+* [`needless_collect`]: Now show correct lint messages for shadowed values
+ [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
+* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
+ [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
+* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
+ [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
+* [`redundant_pattern_matching`]
+ [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
+* [`modulo_one`]
+ [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
+* [`use_self`]
+ [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
+
+## Rust 1.54
+
+Released 2021-07-29
+
+[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
+
+### New Lints
+
+- [`ref_binding_to_reference`]
+ [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
+- [`needless_bitwise_bool`]
+ [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
+- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
+- [`manual_str_repeat`]
+ [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
+- [`suspicious_splitn`]
+ [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
+
+### Moves and Deprecations
+
+- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
+ the new `avoid-breaking-exported-api` config option (see
+ [Enhancements](#1-54-enhancements))
+ [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
+- Move [`inconsistent_struct_constructor`] to `pedantic`
+ [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
+- Move [`needless_borrow`] to `style` (now warn-by-default)
+ [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
+- Move [`suspicious_operation_groupings`] to `nursery`
+ [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
+- Move [`semicolon_if_nothing_returned`] to `pedantic`
+ [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
+
+### Enhancements <a name="1-54-enhancements"></a>
+
+- [`while_let_on_iterator`]: Now also lints in nested loops
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
+ [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
+- [`needless_collect`]: Now also lints on assignments with type annotations
+ [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
+- [`if_then_some_else_none`]: Now works with the MSRV config
+ [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
+- Add `avoid-breaking-exported-api` config option for the lints
+ [`enum_variant_names`], [`large_types_passed_by_value`],
+ [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
+ [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
+ this configuration option to `false` before a major release (1.0/2.0/...) to
+ clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
+- [`needless_collect`]: Now lints on even more data structures
+ [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
+- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
+ attributes as sufficient documentation
+ [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
+- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
+ Now work as expected when used with `allow`
+ [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
+
+### False Positive Fixes
+
+- [`implicit_return`]: Now takes all diverging functions in account to avoid
+ false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
+- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
+ and the struct is used in the loop
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`multiple_inherent_impl`]: No longer lints with generic arguments
+ [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
+- [`comparison_chain`]: No longer lints in a `const` context
+ [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
+- [`while_immutable_condition`]: Fix false positive where mutation in the loop
+ variable wasn't picked up
+ [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
+- [`default_trait_access`]: No longer lints in macros
+ [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
+- [`needless_question_mark`]: No longer lints when the inner value is implicitly
+ dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
+- [`unused_unit`]: No longer lints when multiple macro contexts are involved
+ [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
+- [`eval_order_dependence`]: Fix false positive in async context
+ [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
+- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
+ type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
+- [`wrong_self_convention`]: No longer lints in trait implementations of
+ non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
+- [`suboptimal_flops`]: No longer lints on `powi(2)`
+ [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
+- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
+ [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
+- [`option_if_let_else`]: No longer lints on `else if let` pattern
+ [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
+- [`use_self`], [`useless_conversion`]: Fix false positives when generic
+ arguments are involved
+ [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
+- [`manual_unwrap_or`]: Fix false positive with deref coercion
+ [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
+- [`similar_names`]: No longer lints on `wparam`/`lparam`
+ [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
+- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
+ closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
+
+### Suggestion Fixes/Improvements
+
+- [`implicit_return`]
+ [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
+ - Fix suggestion for async functions
+ - Improve suggestion with macros
+ - Suggest to change `break` to `return` when appropriate
+- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`match_single_binding`]: Improve suggestion when match scrutinee has side
+ effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
+- [`needless_borrow`]: Now suggests to also change usage sites as needed
+ [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
+- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
+ buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
+- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
+ when a `<_ as Trait>::_` is involved
+ [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
+- [`not_unsafe_ptr_arg_deref`]: Improved error message
+ [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
+
+### ICE Fixes
+
+- Fix ICE when running Clippy on `libstd`
+ [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
+- [`implicit_return`]
+ [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
+
+## Rust 1.53
+
+Released 2021-06-17
+
+[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
+
+### New Lints
+
+* [`option_filter_map`]
+ [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
+* [`branches_sharing_code`]
+ [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
+* [`needless_for_each`]
+ [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
+* [`if_then_some_else_none`]
+ [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
+* [`non_octal_unix_permissions`]
+ [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
+* [`unnecessary_self_imports`]
+ [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
+* [`bool_assert_comparison`]
+ [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
+* [`cloned_instead_of_copied`]
+ [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
+* [`flat_map_option`]
+ [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
+
+### Moves and Deprecations
+
+* Deprecate [`filter_map`] lint
+ [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
+* Move [`transmute_ptr_to_ptr`] to `pedantic`
+ [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
+
+### Enhancements
+
+* [`mem_replace_with_default`]: Also lint on common std constructors
+ [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
+* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
+ [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
+* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
+ [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
+ * Attempt to find a common path prefix in suggestion
+ * Don't lint on `Option` and `Result`
+ * Consider `Self` prefix
+* [`explicit_deref_methods`]: Also lint on chained `deref` calls
+ [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
+* [`or_fun_call`]: Also lint on `unsafe` blocks
+ [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
+* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
+ `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
+* [`search_is_some`]: Also check for `is_none`
+ [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
+* [`string_lit_as_bytes`]: Also lint on `into_bytes`
+ [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
+* [`len_without_is_empty`]: Also lint if function signatures of `len` and
+ `is_empty` don't match
+ [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
+* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
+ [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
+* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
+ [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
+* [`needless_return`]: Also lint in `async` functions
+ [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
+* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
+ [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
+* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
+ now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
+
+### False Positive Fixes
+
+* [`upper_case_acronyms`]: No longer lints on public items
+ [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
+* [`suspicious_map`]: No longer lints when side effects may occur inside the
+ `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
+* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
+ [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
+* [`wrong_self_convention`]: Now respects `Copy` types
+ [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
+* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
+ from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
+* [`map_entry`]: Better detect if the entry API can be used
+ [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
+* [`or_fun_call`]: No longer lints on some `len` function calls
+ [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
+* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
+ generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
+* [`upper_case_acronyms`]: No longer lints on public items
+ [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
+* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
+ of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
+* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
+ suggesting to use `derive` instead
+ [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
+* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
+ [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
+* [`clone_on_copy`]: Only lint when using the `Clone` trait
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`wrong_self_convention`]: No longer lints inside a trait implementation
+ [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
+* [`redundant_clone`]: No longer lints when the cloned value is modified while
+ the clone is in use
+ [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
+* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
+ [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
+* [`cargo_common_metadata`]: Remove author requirement
+ [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
+* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
+ [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
+* [`panic`]: No longer wrongfully lints on `debug_assert` with message
+ [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
+* [`wrong_self_convention`]: No longer lints in trait implementations where no
+ `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
+* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
+ involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
+* [`suspicious_else_formatting`]: Allow Allman style braces
+ [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
+* [`inconsistent_struct_constructor`]: No longer lints in macros
+ [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
+* [`single_component_path_imports`]: No longer lints on macro re-exports
+ [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
+
+### Suggestion Fixes/Improvements
+
+* [`redundant_pattern_matching`]: Add a note when applying this lint would
+ change the drop order
+ [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
+* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
+ [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
+* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
+ [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
+* [`inconsistent_struct_constructor`]: Make lint description and message clearer
+ [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
+* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
+ as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
+* [`manual_flatten`]: Suggest to insert `copied` if necessary
+ [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
+* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
+ when the value is from a macro call
+ [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
+* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
+ [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
+* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`manual_map`]: Fix suggestion at the end of an if chain
+ [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
+* Fix needless parenthesis output in multiple lint suggestions
+ [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
+* [`needless_collect`]: Better explanation in the lint message
+ [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
+* [`useless_vec`]: Now considers mutability
+ [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
+* [`useless_format`]: Wrap the content in braces if necessary
+ [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
+* [`single_match`]: Don't suggest an equality check for types which don't
+ implement `PartialEq`
+ [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
+* [`from_over_into`]: Mention type in help message
+ [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
+* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
+ [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
+
+### ICE Fixes
+
+* [`macro_use_imports`]
+ [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
+* [`missing_panics_doc`]
+ [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
+* [`tabs_in_doc_comments`]
+ [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
+* [`missing_const_for_fn`]
+ [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
+
+### Others
+
+* [Clippy's lint
+ list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
+ themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
+* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
+ deprecation warning
+ [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
+
+## Rust 1.52
+
+Released 2021-05-06
+
+[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
+
+### New Lints
+
+* [`from_str_radix_10`]
+ [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
+* [`implicit_clone`]
+ [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
+* [`semicolon_if_nothing_returned`]
+ [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
+* [`manual_flatten`]
+ [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
+* [`inconsistent_struct_constructor`]
+ [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
+* [`iter_count`]
+ [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
+* [`default_numeric_fallback`]
+ [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
+* [`bytes_nth`]
+ [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
+* [`filter_map_identity`]
+ [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
+* [`manual_map`]
+ [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
+
+### Moves and Deprecations
+
+* Moved [`upper_case_acronyms`] to `pedantic`
+ [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
+* Moved [`manual_map`] to `nursery`
+ [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
+* Moved [`unnecessary_wraps`] to `pedantic`
+ [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
+* Moved [`trivial_regex`] to `nursery`
+ [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
+* Moved [`naive_bytecount`] to `pedantic`
+ [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
+* Moved [`upper_case_acronyms`] to `style`
+ [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
+* Moved [`manual_map`] to `style`
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+
+### Enhancements
+
+* [`disallowed_method`]: Now supports functions in addition to methods
+ [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
+* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
+ trigger the lint if there is more than one uppercase character next to each other
+ [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
+* [`collapsible_match`]: Now supports block comparison with different value names
+ [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
+* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
+ [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
+* Improved value usage detection in closures
+ [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
+
+### False Positive Fixes
+
+* [`use_self`]: No longer lints in macros
+ [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
+* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
+ [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
+* [`missing_inline_in_public_items`]: No longer lints for procedural macros
+ [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
+* [`inherent_to_string`]: No longer lints on functions with function generics
+ [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
+* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
+ [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
+* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
+ [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
+* [`collapsible_if`]: No longer lints on if statements with attributes
+ [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
+* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
+ [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
+* [`redundant_closure`]: Now ignores macros
+ [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
+* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+* [`vec_init_then_push`]: Fixed false positives for loops and if statements
+ [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
+* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
+ the `len` method as well as the type definition.
+ [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
+* [`let_underscore_drop`]: Only lints on types which implement `Drop`
+ [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
+* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
+ [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
+* [`cargo_common_metadata`]: No longer lints if
+ [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
+ is defined in the manifest
+ [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
+
+### Suggestion Fixes/Improvements
+
+* [`collapsible_match`]: Fixed lint message capitalization
+ [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
+* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
+ [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
+* [`manual_map`]: No longer expands macros in the suggestions
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+* Aligned Clippy's lint messages with the rustc dev guide
+ [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
+
+### Documentation Improvements
+
+* [`useless_format`]: Improved the documentation example
+ [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
+* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
+ [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
+
+### Others
+* Running `cargo clippy` after `cargo check` now works as expected
+ (`cargo clippy` and `cargo check` no longer shares the same build cache)
+ [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
+* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
+ [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
+* Extracted Clippy's `utils` module into the new `clippy_utils` crate
+ [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
+* Clippy lintcheck tool improvements
+ [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
+ [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
+ [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
+ [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
+ [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
+ [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
+
+## Rust 1.51
+
+Released 2021-03-25
+
+[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
+
+### New Lints
+
+* [`upper_case_acronyms`]
+ [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
+* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
+* [`case_sensitive_file_extension_comparisons`]
+ [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
+* [`needless_question_mark`]
+ [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
+* [`missing_panics_doc`]
+ [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
+* [`redundant_slicing`]
+ [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
+* [`vec_init_then_push`]
+ [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
+* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
+* [`collapsible_else_if`] (split out from `collapsible_if`)
+ [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
+* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
+* [`manual_filter_map`]
+ [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* [`exhaustive_enums`]
+ [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+* [`exhaustive_structs`]
+ [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+
+### Moves and Deprecations
+
+* Replace [`find_map`] with [`manual_find_map`]
+ [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
+ [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
+
+### Enhancements
+
+* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
+ [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
+* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
+ [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
+* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
+ scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
+
+### False Positive Fixes
+
+* [`similar_names`] Ignore underscore prefixed names
+ [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
+* [`print_literal`] and [`write_literal`] No longer lint numeric literals
+ [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
+* [`large_enum_variant`] No longer lints in external macros
+ [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
+* [`empty_enum`] Only lint if `never_type` feature is enabled
+ [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
+* [`field_reassign_with_default`] No longer lints in macros
+ [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
+* [`size_of_in_element_count`] No longer lints when dividing by element size
+ [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
+* [`needless_return`] No longer lints in macros
+ [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
+* [`match_overlapping_arm`] No longer lint when first arm is completely included
+ in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
+* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
+ identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
+
+### Suggestion Fixes/Improvements
+
+* [`field_reassign_with_default`] Don't expand macro in lint suggestion
+ [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
+* [`match_like_matches_macro`] Strip references in suggestion
+ [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
+* [`single_match`] Suggest `if` over `if let` when possible
+ [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
+* [`ref_in_deref`] Use parentheses correctly in suggestion
+ [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
+* [`stable_sort_primitive`] Clarify error message
+ [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
+
+### Documentation Improvements
+
+* Improve search performance on the Clippy website and make it possible to
+ directly search for lints on the GitHub issue tracker
+ [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
+* Clean up `README.md` by removing outdated paragraph
+ [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
+* [`await_holding_refcell_ref`] and [`await_holding_lock`]
+ [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
+* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
+
+### Others
+
+* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
+ project, take a look at the [Roadmap project page]. All issues listed there
+ are actively mentored
+ [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
+* The Clippy version number now corresponds to the Rust version number
+ [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
+* Fix oversight which caused Clippy to lint deps in some environments, where
+ `CLIPPY_TESTS=true` was set somewhere
+ [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
+* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
+ [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
+
+[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
+[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
+
+## Rust 1.50
+
+Released 2021-02-11
+
+[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
+
+### New Lints
+
+* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
+* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
+* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
+* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
+* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
+* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
+* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
+* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
+* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
+
+### Moves and Deprecations
+
+* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
+ as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
+ [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
+* Move [`map_err_ignore`] to `restriction`
+ [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
+* Move [`await_holding_refcell_ref`] to `pedantic`
+ [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
+* Move [`await_holding_lock`] to `pedantic`
+ [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
+
+### Enhancements
+
+* Add the `unreadable-literal-lint-fractions` configuration to disable
+ the `unreadable_literal` lint for fractions
+ [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
+* [`clone_on_copy`]: Now shows the type in the lint message
+ [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
+* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
+ [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
+* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
+ [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
+* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
+ [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
+* [`clone_double_ref`]: Now prints the reference type in the lint message
+ [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
+* [`modulo_one`]: Now also lints on -1.
+ [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
+* [`empty_loop`]: Now lints no_std crates, too
+ [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
+* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
+ [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
+* [`wrong_self_convention`]: Now also lints in trait definitions
+ [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
+* [`needless_borrow`]: Print the type in the lint message
+ [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
+
+[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
+
+### False Positive Fixes
+
+* [`manual_range_contains`]: No longer lints in `const fn`
+ [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
+* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
+ [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
+* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
+ [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
+* [`match_like_matches_macro`]: No longer lints on arms with attributes
+ [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
+* [`map_clone`]: No longer lints with deref and clone
+ [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
+* [`map_clone`]: No longer lints in the case of &mut
+ [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
+* [`needless_update`]: Now ignores `non_exhaustive` structs
+ [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
+* [`needless_collect`]: No longer lints when a collect is needed multiple times
+ [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
+* [`unnecessary_cast`] No longer lints cfg-dependent types
+ [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
+* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
+ Both now ignore enums with frozen variants
+ [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
+* [`field_reassign_with_default`] No longer lint for private fields
+ [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
+
+
+### Suggestion Fixes/Improvements
+
+* [`vec_box`]: Provide correct type scope suggestion
+ [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
+* [`manual_range_contains`]: Give correct suggestion when using floats
+ [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
+* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
+ [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
+* [`manual_async_fn`]: Improve suggestion formatting
+ [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
+* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
+ [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
+
+### ICE Fixes
+
+* Fix a crash in [`from_iter_instead_of_collect`]
+ [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
+* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
+ [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
+
+### Documentation Improvements
+
+* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
+ * Searching for lints with dashes and spaces is possible now. For example
+ `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
+ * Improved fuzzy search in lint descriptions
+* Various README improvements
+ [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
+* Add known problems to [`comparison_chain`] documentation
+ [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
+* Fix example used in [`cargo_common_metadata`]
+ [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
+* Improve [`map_clone`] documentation
+ [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
+
+### Others
+
+* You can now tell Clippy about the MSRV your project supports. Please refer to
+ the specific README section to learn more about MSRV support [here][msrv_readme]
+ [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
+* Add `--no-deps` option to avoid running on path dependencies in workspaces
+ [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
+
+## Rust 1.49
+
+Released 2020-12-31
+
+[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
+
+### New Lints
+
+* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
+* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
+* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
+* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
+* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
+* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
+* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
+* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
+* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
+* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
+* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
+* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
+* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
+* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
+* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
+* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
+* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
+
+### Moves and Deprecations
+
+* Rename `single_char_push_str` to [`single_char_add_str`]
+ [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
+* Rename `zero_width_space` to [`invisible_characters`]
+ [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
+* Deprecate `drop_bounds` (uplifted)
+ [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
+* Move [`string_lit_as_bytes`] to `nursery`
+ [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
+* Move [`rc_buffer`] to `restriction`
+ [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
+
+### Enhancements
+
+* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
+ reliable suggestion)
+ [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
+* [`single_char_add_str`]: Also lint on `String::insert_str`
+ [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
+* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
+ [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
+* [`eq_op`]: Also lint on the `assert_*!` macro family
+ [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
+* [`items_after_statements`]: Also lint in local macro expansions
+ [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
+* [`unnecessary_cast`]: Also lint casts on integer and float literals
+ [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
+* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
+ [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
+* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
+ [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
+* [`integer_arithmetic`]: Better handle `/` an `%` operators
+ [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
+
+### False Positive Fixes
+
+* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
+ lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
+* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
+ is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
+* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
+ [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
+* [`needless_range_loop`]: No longer lints, when the iterable is used in the
+ range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
+* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
+ [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
+* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
+ float (e.g. `713.32_64`)
+ [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
+* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
+ [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
+* [`boxed_local`]: No longer lints on `extern fn` arguments
+ [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
+* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
+ clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
+
+### Suggestion Fixes/Improvements
+
+* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
+ [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
+* [`needless_arbitrary_self_type`]: Correctly handle expanded code
+ [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
+* [`useless_format`]: Preserve raw strings in suggestion
+ [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
+* [`empty_loop`]: Suggest alternatives
+ [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
+* [`borrowed_box`]: Correctly add parentheses in suggestion
+ [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
+* [`unused_unit`]: Improve suggestion formatting
+ [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
+
+### Documentation Improvements
+
+* Some doc improvements:
+ * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
+ * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
+* [`doc_markdown`]: Document problematic link text style
+ [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
+
+## Rust 1.48
+
+Released 2020-11-19
+
+[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
+
+### New lints
+
+* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
+* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
+* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
+* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
+* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
+* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
+* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
+
+### Moves and Deprecations
+
+* Downgrade [`verbose_bit_mask`] to pedantic
+ [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
+
+### Enhancements
+
+* Extend [`precedence`] to handle chains of methods combined with unary negation
+ [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
+* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
+ [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
+* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
+ [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
+* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
+ [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
+* Avoid [`redundant_pattern_matching`] triggering in macros
+ [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
+* [`option_if_let_else`]: distinguish pure from impure `else` expressions
+ [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
+* [`needless_doctest_main`]: parse doctests instead of using textual search
+ [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
+* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
+ [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
+* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
+ [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
+* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
+ [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
+
+### False Positive Fixes
+
+* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
+ [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
+* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
+ [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
+* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
+ [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
+* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
+ [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
+* [`doc_markdown`]: allow using "GraphQL" without backticks
+ [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
+* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self`
+ [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
+* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
+ [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
+* [`should_implement_trait`]: ignore methods with lifetime parameters
+ [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
+* [`needless_return`]: avoid linting if a temporary borrows a local variable
+ [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
+* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
+ [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
+* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
+ [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
+* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
+ [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
+
+### Suggestion Fixes/Improvements
+
+* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
+ [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
+* [`useless_conversion`]: show the type in the error message
+ [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
+* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
+ [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
+* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
+ [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
+* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
+ [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
+* [`collapsible_if`]: don't use expanded code in the suggestion
+ [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
+* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
+ [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
+* [`unit_arg`]: improve the readability of the suggestion
+ [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
+* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
+ [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
+* Show line count and max lines in [`too_many_lines`] lint message
+ [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
+* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
+ [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
+* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
+ [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
+* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
+ [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
+* Make lint messages adhere to rustc dev guide conventions
+ [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
+
+### ICE Fixes
+
+* Fix ICE in [`repeat_once`]
+ [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
+
+### Documentation Improvements
+
+* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
+ [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
+* [`unnecessary_mut_passed`]: fix typo
+ [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
+* Add example of false positive to [`ptr_arg`] docs.
+ [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
+* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
+ [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
+
+## Rust 1.47
+
+Released 2020-10-08
+
+[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
+
+### New lints
+
+* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
+* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
+* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
+* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
+* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
+* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
+* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
+* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
+* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
+* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
+* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
+* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
+
+### Moves and Deprecations
+
+* Deprecate [`regex_macro`] lint
+ [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
+* Move [`range_minus_one`] to `pedantic`
+ [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
+
+### Enhancements
+
+* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
+ [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
+* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
+ [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
+* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
+ [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
+* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
+ [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
+* Make it possible to allow [`unsafe_derive_deserialize`]
+ [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
+* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
+ [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
+* Make [`clone_on_copy`] suggestion machine applicable
+ [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
+* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
+ [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
+
+### False Positive Fixes
+
+* Avoid triggering [`or_fun_call`] with const fns that take no arguments
+ [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
+* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
+ [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
+* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
+ [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
+* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
+ [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
+* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
+ [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
+* Avoid linting if key borrows in [`unnecessary_sort_by`]
+ [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
+* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
+ [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
+* Take input lifetimes into account in `manual_async_fn`
+ [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
+* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
+ [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
+* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
+ [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
+
+### Suggestion Fixes/Improvements
+
+* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
+ [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
+* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
+ [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
+* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
+ [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
+* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
+ [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
+* Add suggestion for [`iter_skip_next`]
+ [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
+* Improve [`collapsible_if`] fix suggestion
+ [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
+
+### ICE Fixes
+
+* Fix ICE caused by [`needless_collect`]
+ [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
+* Fix ICE caused by [`unnested_or_patterns`]
+ [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
+
+### Documentation Improvements
+
+* Fix grammar of [`await_holding_lock`] documentation
+ [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
+
+### Others
+
+* Make lints adhere to the rustc dev guide
+ [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
+
+## Rust 1.46
+
+Released 2020-08-27
+
+[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
+
+### New lints
+
+* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
+* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
+* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
+* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
+
+### Moves and Deprecations
+
+* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
+
+### Enhancements
+
+* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
+
+### False Positive Fixes
+
+* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
+ [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
+* [`let_and_return`]: Don't lint if a temporary borrow is involved
+ [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
+* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
+ [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
+* [`if_same_then_else`]: Don't assume multiplication is always commutative
+ [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
+* [`blacklisted_name`]: Remove `bar` from the default configuration
+ [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
+* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
+ [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
+
+### Suggestion Fixes/Improvements
+
+* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
+ [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
+* Add auto applicable suggestion to [`macro_use_imports`]
+ [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
+
+### ICE Fixes
+
+* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
+
+### Documentation Improvements
+
+* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
+
+### Others
+
+* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
+ into `rustc` and passes all the given arguments to `rustc`. This is especially
+ useful for tools that need the `rustc` version Clippy was compiled with,
+ instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
+ print the output of `rustc --version`.
+ [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
+* New issue templates now make it easier to complain if Clippy is too annoying
+ or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
+
+## Rust 1.45
+
+Released 2020-07-16
+
+[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
+
+### New lints
+
+* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
+* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
+* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
+* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
+* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
+* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
+* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
+* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
+* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
+
+### Moves and Deprecations
+
+* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
+* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
+* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
+* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
+* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
+* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+
+### Enhancements
+
+* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
+* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
+* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
+* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
+
+### False Positive Fixes
+
+* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
+* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
+* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
+* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
+* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
+* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
+* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
+* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
+* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
+* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
+* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
+* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
+and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
+* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
+
+### Suggestion Improvements
+
+* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
+* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
+* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
+* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
+
+### ICE Fixes
+
+* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
+* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
+
+### Documentation
+
+* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
+* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
+
+## Rust 1.44
+
+Released 2020-06-04
+
+[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
+
+### New lints
+
+* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
+* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
+* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
+* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
+* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
+* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
+* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
+* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
+* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
+* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+
+
+### Moves and Deprecations
+
+* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
+* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
+* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
+* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
+* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
+* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
+* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
+* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
+* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
+
+### Enhancements
+
+* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
+ auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
+* Make [`redundant_clone`] also trigger on cases where the cloned value is not
+ consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
+* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
+* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
+* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
+* [`bool_comparison`] now also checks for inequality comparisons that can be
+ written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
+* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
+* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
+* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
+* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
+* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
+
+### False Positive Fixes
+
+* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
+* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
+* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
+* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
+* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
+* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
+* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
+* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
+* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
+
+
+### Suggestion Improvements
+
+* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
+* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
+* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
+
+### ICE Fixes
+
+* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
+* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
+
+### Documentation
+
+* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
+* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
+* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
+* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
+* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
+ not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
+
+## Rust 1.43
+
+Released 2020-04-23
+
+[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
+
+### New lints
+
+* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
+* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
+* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
+* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
+* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
+* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
+* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
+
+### Moves and Deprecations
+
+* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
+
+### Enhancements
+
+* Make [`missing_errors_doc`] lint also trigger on `async` functions
+ [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
+* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
+* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
+
+### False Positive Fixes
+
+* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
+* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
+* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
+* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
+
+### Suggestion Improvements
+
+* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
+
+### ICE Fixes
+
+* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
+* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
+* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
+
+### Documentation
+
+* Improve documentation of [`iter_nth_zero`]
+* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
+
+### Others
+
+* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
+
+
+## Rust 1.42
+
+Released 2020-03-12
+
+[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
+
+### New lints
+
+* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
+* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
+* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
+* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
+* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
+* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
+* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
+* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
+* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
+* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
+
+### Moves and Deprecations
+
+* Move [`transmute_float_to_int`] from nursery to complexity group
+ [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
+* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
+* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
+
+### Enhancements
+
+* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
+* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
+* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
+* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
+* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
+
+### False Positive Fixes
+
+* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
+* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
+* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
+* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
+* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
+* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Don't trigger [`let_underscore_must_use`] in external macros
+ [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
+* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
+
+### Suggestion Improvements
+
+* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
+* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
+* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
+* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
+* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
+* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
+* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
+* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
+
+### ICE fixes
+
+* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
+
+### Documentation
+
+* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
+
+
+## Rust 1.41
+
+Released 2020-01-30
+
+[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
+
+* New Lints:
+ * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
+ * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
+ * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
+ * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
+ * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
+ * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
+ * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
+ * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
+ * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
+* Remove plugin interface, see
+ [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
+ details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
+* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
+* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
+* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
+ [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
+* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
+* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
+* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
+* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
+* Fix false positive in `print_with_newline` and `write_with_newline`
+ [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
+* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
+* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
+* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
+* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
+* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
+* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
+* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
+* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
+* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
+* Display help when running `clippy-driver` without arguments, instead of ICEing
+ [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
+* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
+* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
+* Improve Documentation by adding positive examples to some lints
+ [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
+
+## Rust 1.40
+
+Released 2019-12-19
+
+[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
+
+* New Lints:
+ * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
+ * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
+ * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
+ * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
+ * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
+ * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
+ * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
+ * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
+ * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
+* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
+* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
+* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
+* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
+* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
+* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
+* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
+* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
+* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
+* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
+* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
+* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
+* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
+* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
+* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
+
+## Rust 1.39
+
+Released 2019-11-07
+
+[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
+
+* New Lints:
+ * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
+ * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
+ * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
+ * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
+ * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
+ * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
+ * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
+* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
+* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
+* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
+* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
+* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
+* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
+* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
+* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
+* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
+* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
+* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
+* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
+* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
+* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
+* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
+* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
+* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
+* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
+* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
+* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
+* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
+* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
+* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
+* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
+* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
+* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
+* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
+* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
+* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
+
+## Rust 1.38
+
+Released 2019-09-26
+
+[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
+
+* New Lints:
+ * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
+ * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
+ * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
+* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
+* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
+* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
+* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
+* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
+* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
+* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
+* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
+* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
+* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
+* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
+* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
+* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
+* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
+* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
+
+## Rust 1.37
+
+Released 2019-08-15
+
+[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
+
+* New Lints:
+ * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
+ * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
+ * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
+* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
+ The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
+* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
+* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
+* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
+* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
+* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
+* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
+* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
+* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
+* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
+* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
+* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
+
+## Rust 1.36
+
+Released 2019-07-04
+
+[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
+
+* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
+* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
+* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
+* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
+* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
+* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
+* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
+* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
+* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
+* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
+* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
+* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
+* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
+* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
+* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
+* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
+* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
+* Add macro check for [`unnecessary_cast`] [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
+* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
+* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
+* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
+
+
+## Rust 1.35
+
+Released 2019-05-20
+
+[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
+
+* New lint: `drop_bounds` to detect `T: Drop` bounds
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
+* Move [`get_unwrap`] to the restriction category
+* Improve suggestions for [`iter_cloned_collect`]
+* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
+* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
+* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
+* Fix false positive in [`bool_comparison`] pertaining to non-bool types
+* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
+* Fix false positive in `option_map_unwrap_or` on non-copy types
+* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
+* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
+* Fix false positive in [`needless_continue`] pertaining to loop labels
+* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
+* Fix false positive for [`use_self`] in nested functions
+* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
+* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
+* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
+* Avoid triggering [`redundant_closure`] in macros
+* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
+
+## Rust 1.34
+
+Released 2019-04-10
+
+[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
+
+* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
+* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
+* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
+* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
+ configured using the `too-many-lines-threshold` configuration.
+* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
+* Expand `redundant_closure` to also work for methods (not only functions)
+* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
+* Fix false positive in `cast_sign_loss`
+* Fix false positive in `integer_arithmetic`
+* Fix false positive in `unit_arg`
+* Fix false positives in `implicit_return`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `cast_lossless`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `needless_bool`
+* Fix incorrect suggestion for `needless_range_loop`
+* Fix incorrect suggestion for `use_self`
+* Fix incorrect suggestion for `while_let_on_iterator`
+* Clippy is now slightly easier to invoke in non-cargo contexts. See
+ [#3665][pull3665] for more details.
+* We now have [improved documentation][adding_lints] on how to add new lints
+
+## Rust 1.33
+
+Released 2019-02-26
+
+[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
+
+* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
+* The `rust-clippy` repository is now part of the `rust-lang` org.
+* Rename `stutter` to `module_name_repetitions`
+* Merge `new_without_default_derive` into `new_without_default` lint
+* Move `large_digit_groups` from `style` group to `pedantic`
+* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
+ comparisons against booleans
+* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
+* Expand `redundant_clone` to work on struct fields
+* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
+* Expand `use_self` to work on tuple structs and also in local macros
+* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
+* Fix false positives in `implicit_return`
+* Fix false positives in `use_self`
+* Fix false negative in `clone_on_copy`
+* Fix false positive in `doc_markdown`
+* Fix false positive in `empty_loop`
+* Fix false positive in `if_same_then_else`
+* Fix false positive in `infinite_iter`
+* Fix false positive in `question_mark`
+* Fix false positive in `useless_asref`
+* Fix false positive in `wildcard_dependencies`
+* Fix false positive in `write_with_newline`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `get_unwrap`
+
+## Rust 1.32
+
+Released 2019-01-17
+
+[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
+
+* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
+ [`redundant_clone`], [`wildcard_dependencies`],
+ [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
+ [`cargo_common_metadata`]
+* Add support for `u128` and `i128` to integer related lints
+* Add float support to `mistyped_literal_suffixes`
+* Fix false positives in `use_self`
+* Fix false positives in `missing_comma`
+* Fix false positives in `new_ret_no_self`
+* Fix false positives in `possible_missing_comma`
+* Fix false positive in `integer_arithmetic` in constant items
+* Fix false positive in `needless_borrow`
+* Fix false positive in `out_of_bounds_indexing`
+* Fix false positive in `new_without_default_derive`
+* Fix false positive in `string_lit_as_bytes`
+* Fix false negative in `out_of_bounds_indexing`
+* Fix false negative in `use_self`. It will now also check existential types
+* Fix incorrect suggestion for `redundant_closure_call`
+* Fix various suggestions that contained expanded macros
+* Fix `bool_comparison` triggering 3 times on on on the same code
+* Expand `trivially_copy_pass_by_ref` to work on trait methods
+* Improve suggestion for `needless_range_loop`
+* Move `needless_pass_by_value` from `pedantic` group to `style`
+
+## Rust 1.31
+
+Released 2018-12-06
+
+[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
+
+* Clippy has been relicensed under a dual MIT / Apache license.
+ See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
+ information.
+* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
+ installation method is via `rustup component add clippy`.
+* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
+ [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
+* Fix ICE in `if_let_redundant_pattern_matching`
+* Fix ICE in `needless_pass_by_value` when encountering a generic function
+ argument with a lifetime parameter
+* Fix ICE in `needless_range_loop`
+* Fix ICE in `single_char_pattern` when encountering a constant value
+* Fix false positive in `assign_op_pattern`
+* Fix false positive in `boxed_local` on trait implementations
+* Fix false positive in `cmp_owned`
+* Fix false positive in `collapsible_if` when conditionals have comments
+* Fix false positive in `double_parens`
+* Fix false positive in `excessive_precision`
+* Fix false positive in `explicit_counter_loop`
+* Fix false positive in `fn_to_numeric_cast_with_truncation`
+* Fix false positive in `map_clone`
+* Fix false positive in `new_ret_no_self`
+* Fix false positive in `new_without_default` when `new` is unsafe
+* Fix false positive in `type_complexity` when using extern types
+* Fix false positive in `useless_format`
+* Fix false positive in `wrong_self_convention`
+* Fix incorrect suggestion for `excessive_precision`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `get_unwrap`
+* Fix incorrect suggestion for `useless_format`
+* `fn_to_numeric_cast_with_truncation` lint can be disabled again
+* Improve suggestions for `manual_memcpy`
+* Improve help message for `needless_lifetimes`
+
+## Rust 1.30
+
+Released 2018-10-25
+
+[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
+
+* Deprecate `assign_ops` lint
+* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
+ [`needless_collect`], [`copy_iterator`]
+* `cargo clippy -V` now includes the Clippy commit hash of the Rust
+ Clippy component
+* Fix ICE in `implicit_hasher`
+* Fix ICE when encountering `println!("{}" a);`
+* Fix ICE when encountering a macro call in match statements
+* Fix false positive in `default_trait_access`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `similar_names`
+* Fix false positive in `redundant_field_name`
+* Fix false positive in `expect_fun_call`
+* Fix false negative in `identity_conversion`
+* Fix false negative in `explicit_counter_loop`
+* Fix `range_plus_one` suggestion and false negative
+* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
+* Fix `useless_attribute` to also whitelist `unused_extern_crates`
+* Fix incorrect suggestion for `single_char_pattern`
+* Improve suggestion for `identity_conversion` lint
+* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
+* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
+* Move `shadow_unrelated` from `restriction` group to `pedantic`
+* Move `indexing_slicing` from `pedantic` group to `restriction`
+
+## Rust 1.29
+
+Released 2018-09-13
+
+[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
+
+* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
+ :tada:
+ You can now run `rustup component add clippy-preview` and then `cargo
+ clippy` to run Clippy. This should put an end to the continuous nightly
+ upgrades for Clippy users.
+* Clippy now follows the Rust versioning scheme instead of its own
+* Fix ICE when encountering a `while let (..) = x.iter()` construct
+* Fix false positives in `use_self`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `useless_attribute` lint
+* Fix false positive in `print_literal`
+* Fix `use_self` regressions
+* Improve lint message for `neg_cmp_op_on_partial_ord`
+* Improve suggestion highlight for `single_char_pattern`
+* Improve suggestions for various print/write macro lints
+* Improve website header
+
+## 0.0.212 (2018-07-10)
+* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
+
+## 0.0.211
+* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
+
+## 0.0.210
+* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
+
+## 0.0.209
+* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
+
+## 0.0.208
+* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
+
+## 0.0.207
+* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
+
+## 0.0.206
+* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
+
+## 0.0.205
+* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
+* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
+
+## 0.0.204
+* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
+
+## 0.0.203
+* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
+* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
+
+## 0.0.202
+* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
+
+## 0.0.201
+* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
+
+## 0.0.200
+* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
+
+## 0.0.199
+* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
+
+## 0.0.198
+* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
+
+## 0.0.197
+* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
+
+## 0.0.196
+* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
+
+## 0.0.195
+* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
+
+## 0.0.194
+* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
+* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
+
+## 0.0.193
+* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
+
+## 0.0.192
+* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
+* New lint: [`print_literal`]
+
+## 0.0.191
+* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
+* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
+
+## 0.0.190
+* Fix a bunch of intermittent cargo bugs
+
+## 0.0.189
+* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
+
+## 0.0.188
+* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
+* New lint: [`while_immutable_condition`]
+
+## 0.0.187
+* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
+* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
+
+## 0.0.186
+* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
+* Various false positive fixes
+
+## 0.0.185
+* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
+* New lint: [`question_mark`]
+
+## 0.0.184
+* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
+* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
+
+## 0.0.183
+* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
+* New lint: [`misaligned_transmute`]
+
+## 0.0.182
+* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
+* New lint: [`decimal_literal_representation`]
+
+## 0.0.181
+* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
+* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
+* Removed `unit_expr`
+* Various false positive fixes for [`needless_pass_by_value`]
+
+## 0.0.180
+* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
+
+## 0.0.179
+* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
+
+## 0.0.178
+* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
+
+## 0.0.177
+* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
+* New lint: [`match_as_ref`]
+
+## 0.0.176
+* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
+
+## 0.0.175
+* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
+
+## 0.0.174
+* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
+
+## 0.0.173
+* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
+
+## 0.0.172
+* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
+
+## 0.0.171
+* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
+
+## 0.0.170
+* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
+
+## 0.0.169
+* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
+* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
+
+## 0.0.168
+* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
+
+## 0.0.167
+* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
+* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
+
+## 0.0.166
+* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
+* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
+ [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
+ [`transmute_int_to_float`]
+
+## 0.0.165
+* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
+* New lint: [`mut_range_bound`]
+
+## 0.0.164
+* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
+* New lint: [`int_plus_one`]
+
+## 0.0.163
+* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
+
+## 0.0.162
+* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
+* New lint: [`chars_last_cmp`]
+* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
+
+## 0.0.161
+* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
+
+## 0.0.160
+* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
+
+## 0.0.159
+* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
+* New lint: [`clone_on_ref_ptr`]
+
+## 0.0.158
+* New lint: [`manual_memcpy`]
+* [`cast_lossless`] no longer has redundant parentheses in its suggestions
+* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
+
+## 0.0.157 - 2017-09-04
+* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
+* New lint: `unit_expr`
+
+## 0.0.156 - 2017-09-03
+* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
+
+## 0.0.155
+* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
+* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
+
+## 0.0.154
+* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
+* Fix [`use_self`] triggering inside derives
+* Add support for linting an entire workspace with `cargo clippy --all`
+* New lint: [`naive_bytecount`]
+
+## 0.0.153
+* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
+* New lint: [`use_self`]
+
+## 0.0.152
+* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
+
+## 0.0.151
+* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
+
+## 0.0.150
+* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
+
+## 0.0.148
+* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
+* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
+
+## 0.0.147
+* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
+
+## 0.0.146
+* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
+* Fixes false positives in `inline_always`
+* Fixes false negatives in `panic_params`
+
+## 0.0.145
+* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
+
+## 0.0.144
+* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
+
+## 0.0.143
+* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
+* Fix `cargo clippy` crashing on `dylib` projects
+* Fix false positives around `nested_while_let` and `never_loop`
+
+## 0.0.142
+* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
+
+## 0.0.141
+* Rewrite of the `doc_markdown` lint.
+* Deprecated [`range_step_by_zero`]
+* New lint: [`iterator_step_by_zero`]
+* New lint: [`needless_borrowed_reference`]
+* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
+
+## 0.0.140 - 2017-06-16
+* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
+
+## 0.0.139 — 2017-06-10
+* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
+* Fix bugs with for loop desugaring
+* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
+
+## 0.0.138 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
+
+## 0.0.137 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
+
+## 0.0.136 — 2017—05—26
+* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
+
+## 0.0.135 — 2017—05—24
+* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
+
+## 0.0.134 — 2017—05—19
+* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
+
+## 0.0.133 — 2017—05—14
+* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
+
+## 0.0.132 — 2017—05—05
+* Fix various bugs and some ices
+
+## 0.0.131 — 2017—05—04
+* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
+
+## 0.0.130 — 2017—05—03
+* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
+
+## 0.0.129 — 2017-05-01
+* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
+
+## 0.0.128 — 2017-04-28
+* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
+
+## 0.0.127 — 2017-04-27
+* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
+* New lint: [`needless_continue`]
+
+## 0.0.126 — 2017-04-24
+* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
+
+## 0.0.125 — 2017-04-19
+* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
+
+## 0.0.124 — 2017-04-16
+* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
+
+## 0.0.123 — 2017-04-07
+* Fix various false positives
+
+## 0.0.122 — 2017-04-07
+* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
+* New lint: [`op_ref`]
+
+## 0.0.121 — 2017-03-21
+* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
+
+## 0.0.120 — 2017-03-17
+* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
+
+## 0.0.119 — 2017-03-13
+* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
+
+## 0.0.118 — 2017-03-05
+* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
+
+## 0.0.117 — 2017-03-01
+* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
+
+## 0.0.116 — 2017-02-28
+* Fix `cargo clippy` on 64 bit windows systems
+
+## 0.0.115 — 2017-02-27
+* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
+* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
+
+## 0.0.114 — 2017-02-08
+* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
+* Tests are now ui tests (testing the exact output of rustc)
+
+## 0.0.113 — 2017-02-04
+* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
+* New lint: [`large_enum_variant`]
+* `explicit_into_iter_loop` provides suggestions
+
+## 0.0.112 — 2017-01-27
+* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
+
+## 0.0.111 — 2017-01-21
+* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
+
+## 0.0.110 — 2017-01-20
+* Add badges and categories to `Cargo.toml`
+
+## 0.0.109 — 2017-01-19
+* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
+
+## 0.0.108 — 2017-01-12
+* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
+
+## 0.0.107 — 2017-01-11
+* Update regex dependency
+* Fix FP when matching `&&mut` by `&ref`
+* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
+* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
+
+## 0.0.106 — 2017-01-04
+* Fix FP introduced by rustup in [`wrong_self_convention`]
+
+## 0.0.105 — 2017-01-04
+* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
+* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
+* Fix suggestion in [`new_without_default`]
+* FP fix in [`absurd_extreme_comparisons`]
+
+## 0.0.104 — 2016-12-15
+* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
+
+## 0.0.103 — 2016-11-25
+* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
+
+## 0.0.102 — 2016-11-24
+* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
+
+## 0.0.101 — 2016-11-23
+* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
+* New lint: [`string_extend_chars`]
+
+## 0.0.100 — 2016-11-20
+* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
+
+## 0.0.99 — 2016-11-18
+* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
+* New lint: [`get_unwrap`]
+
+## 0.0.98 — 2016-11-08
+* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
+
+## 0.0.97 — 2016-11-03
+* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
+ previously added for a short time under the name `clippy` but removed for
+ compatibility.
+* `cargo clippy --help` is more helping (and less helpful :smile:)
+* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
+* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
+
+## 0.0.96 — 2016-10-22
+* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
+* New lint: [`iter_skip_next`]
+
+## 0.0.95 — 2016-10-06
+* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
+
+## 0.0.94 — 2016-10-04
+* Fixes bustage on Windows due to forbidden directory name
+
+## 0.0.93 — 2016-10-03
+* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
+* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
+ allowed by default.
+* New lint: [`explicit_into_iter_loop`]
+
+## 0.0.92 — 2016-09-30
+* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
+
+## 0.0.91 — 2016-09-28
+* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
+
+## 0.0.90 — 2016-09-09
+* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
+
+## 0.0.89 — 2016-09-06
+* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
+
+## 0.0.88 — 2016-09-04
+* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
+* The following lints are not new but were only usable through the `clippy`
+ lint groups: [`filter_next`], `for_loop_over_option`,
+ `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
+ able to `#[allow/deny]` them individually and they are available directly
+ through `cargo clippy`.
+
+## 0.0.87 — 2016-08-31
+* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
+* New lints: [`builtin_type_shadow`]
+* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
+
+## 0.0.86 — 2016-08-28
+* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
+* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
+
+## 0.0.85 — 2016-08-19
+* Fix ICE with [`useless_attribute`]
+* [`useless_attribute`] ignores `unused_imports` on `use` statements
+
+## 0.0.84 — 2016-08-18
+* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
+
+## 0.0.83 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
+* New lints: [`print_with_newline`], [`useless_attribute`]
+
+## 0.0.82 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
+* New lint: [`module_inception`]
+
+## 0.0.81 — 2016-08-14
+* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
+* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
+* False positive fix in [`too_many_arguments`]
+* Addition of functionality to [`needless_borrow`]
+* Suggestions for [`clone_on_copy`]
+* Bug fix in [`wrong_self_convention`]
+* Doc improvements
+
+## 0.0.80 — 2016-07-31
+* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
+* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
+
+## 0.0.79 — 2016-07-10
+* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
+* Major suggestions refactoring
+
+## 0.0.78 — 2016-07-02
+* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
+* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
+* For compatibility, `cargo clippy` does not defines the `clippy` feature
+ introduced in 0.0.76 anymore
+* [`collapsible_if`] now considers `if let`
+
+## 0.0.77 — 2016-06-21
+* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
+* New lints: `stutter` and [`iter_nth`]
+
+## 0.0.76 — 2016-06-10
+* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
+* `cargo clippy` now automatically defines the `clippy` feature
+* New lint: [`not_unsafe_ptr_arg_deref`]
+
+## 0.0.75 — 2016-06-08
+* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
+
+## 0.0.74 — 2016-06-07
+* Fix bug with `cargo-clippy` JSON parsing
+* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
+ “for further information visit *lint-link*” message.
+
+## 0.0.73 — 2016-06-05
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.72 — 2016-06-04
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.71 — 2016-05-31
+* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
+* New lint: [`useless_let_if_seq`]
+
+## 0.0.70 — 2016-05-28
+* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
+* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
+ `RegexBuilder::new` and byte regexes
+
+## 0.0.69 — 2016-05-20
+* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
+* [`used_underscore_binding`] has been made `Allow` temporarily
+
+## 0.0.68 — 2016-05-17
+* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
+* New lint: [`unnecessary_operation`]
+
+## 0.0.67 — 2016-05-12
+* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
+
+## 0.0.66 — 2016-05-11
+* New `cargo clippy` subcommand
+* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
+
+## 0.0.65 — 2016-05-08
+* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
+* New lints: [`float_arithmetic`], [`integer_arithmetic`]
+
+## 0.0.64 — 2016-04-26
+* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
+* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
+
+## 0.0.63 — 2016-04-08
+* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
+
+## 0.0.62 — 2016-04-07
+* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
+
+## 0.0.61 — 2016-04-03
+* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
+* New lint: [`invalid_upcast_comparisons`]
+
+## 0.0.60 — 2016-04-01
+* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
+
+## 0.0.59 — 2016-03-31
+* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
+* New lints: [`logic_bug`], [`nonminimal_bool`]
+* Fixed: [`match_same_arms`] now ignores arms with guards
+* Improved: [`useless_vec`] now warns on `for … in vec![…]`
+
+## 0.0.58 — 2016-03-27
+* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
+* New lint: [`doc_markdown`]
+
+## 0.0.57 — 2016-03-27
+* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
+* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
+* New lint: [`crosspointer_transmute`]
+
+## 0.0.56 — 2016-03-23
+* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
+* New lints: [`many_single_char_names`] and [`similar_names`]
+
+## 0.0.55 — 2016-03-21
+* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
+
+## 0.0.54 — 2016-03-16
+* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
+
+## 0.0.53 — 2016-03-15
+* Add a [configuration file]
+
+## ~~0.0.52~~
+
+## 0.0.51 — 2016-03-13
+* Add `str` to types considered by [`len_zero`]
+* New lints: [`indexing_slicing`]
+
+## 0.0.50 — 2016-03-11
+* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
+
+## 0.0.49 — 2016-03-09
+* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
+* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
+
+## 0.0.48 — 2016-03-07
+* Fixed: ICE in [`needless_range_loop`] with globals
+
+## 0.0.47 — 2016-03-07
+* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
+* New lint: [`redundant_closure_call`]
+
+[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
+[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
+[configuration file]: ./rust-clippy#configuration
+[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
+
+<!-- lint disable no-unused-definitions -->
+<!-- begin autogenerated links to lint list -->
+[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
+[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
+[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
+[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
+[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
+[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
+[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
+[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
+[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
+[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
+[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
+[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
+[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
+[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
+[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
+[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
+[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
+[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
+[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
+[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
+[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
+[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
+[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
+[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
+[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
+[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
+[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
+[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
+[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
+[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
+[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
+[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
+[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
+[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
+[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
+[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
+[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
+[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
+[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
+[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
+[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
+[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
+[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
+[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
+[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
+[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
+[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
+[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
+[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
+[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
+[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
+[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
+[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
+[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
+[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
+[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
+[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
+[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
+[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
+[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
+[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
+[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
+[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
+[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
+[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
+[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
+[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
+[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
+[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
+[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
+[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
+[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
+[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
+[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
+[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
+[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
+[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
+[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
+[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
+[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
+[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
+[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
+[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
+[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
+[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
+[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
+[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
+[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
+[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
+[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
+[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
+[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
+[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
+[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
+[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
+[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
+[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
+[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
+[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
+[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
+[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
+[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
+[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
+[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
+[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
+[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
+[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
+[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
+[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
+[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
+[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
+[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
+[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
+[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
+[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
+[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
+[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
+[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
+[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
+[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
+[`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
+[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
+[`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_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_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
+[`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_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
+[`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_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
+[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
+[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
+[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
+[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
+[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
+[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
+[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
+[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
+[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
+[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
+[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
+[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
+[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
+[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
+[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
+[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
+[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
+[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
+[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
+[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
+[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
+[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
+[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
+[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
+[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
+[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
+[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
+[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
+[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
+[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
+[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
+[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
+[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
+[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
+[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
+[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
+[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
+[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
+[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
+[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
+[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
+[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
+[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
+[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
+[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
+[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
+[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
+[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
+[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
+[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
+[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
+[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
+[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
+[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
+[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
+[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
+[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
+[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
+[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
+[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
+[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
+[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
+[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
+[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
+[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
+[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
+[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
+[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
+[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
+[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
+[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
+[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
+[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
+[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
+[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
+[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
++[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
+[`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_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
+[`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
+[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
+[`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
+[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
+[`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_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
+[`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
+[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
+[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
+[`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
+[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
+[`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_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
+[`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
- # Make sure to change `your-github-name` to your github name in the following command
+# Contributing to Clippy
+
+Hello fellow Rustacean! Great to see your interest in compiler internals and lints!
+
+**First**: if you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyway. You won't be
+yelled at for giving it your best effort. The worst that can happen is that you'll be politely asked to change
+something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
+
+Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
+explains how you can contribute and how to get started. If you have any questions about contributing or need help with
+anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip].
+
+All contributors are expected to follow the [Rust Code of Conduct].
+
+- [Contributing to Clippy](#contributing-to-clippy)
+ - [Getting started](#getting-started)
+ - [High level approach](#high-level-approach)
+ - [Finding something to fix/improve](#finding-something-to-fiximprove)
+ - [Writing code](#writing-code)
+ - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
+ - [IntelliJ Rust](#intellij-rust)
+ - [Rust Analyzer](#rust-analyzer)
+ - [How Clippy works](#how-clippy-works)
+ - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
+ - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
+ - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
+ - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
+ - [Defining remotes](#defining-remotes)
+ - [Issue and PR triage](#issue-and-pr-triage)
+ - [Bors and Homu](#bors-and-homu)
+ - [Contributions](#contributions)
+
+[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
+[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
+
+## Getting started
+
+**Note: If this is your first time contributing to Clippy, you should
+first read the [Basics docs](doc/basics.md).**
+
+### High level approach
+
+1. Find something to fix/improve
+2. Change code (likely some file in `clippy_lints/src/`)
+3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up
+4. Run `cargo test` in the root directory and wiggle code until it passes
+5. Open a PR (also can be done after 2. if you run into problems)
+
+### Finding something to fix/improve
+
+All issues on Clippy are mentored, if you want help simply ask @Manishearth, @flip1995, @phansch
+or @llogiq directly by mentioning them in the issue or over on [Zulip]. This list may be out of date.
+All currently active mentors can be found [here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
+
+Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy
+issues. You can use `@rustbot claim` to assign the issue to yourself.
+
+There are also some abandoned PRs, marked with [`S-inactive-closed`].
+Pretty often these PRs are nearly completed and just need some extra steps
+(formatting, addressing review comments, ...) to be merged. If you want to
+complete such a PR, please leave a comment in the PR and open a new one based
+on it.
+
+Issues marked [`T-AST`] involve simple matching of the syntax tree structure,
+and are generally easier than [`T-middle`] issues, which involve types
+and resolved paths.
+
+[`T-AST`] issues will generally need you to match against a predefined syntax structure.
+To figure out how this syntax structure is encoded in the AST, it is recommended to run
+`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs].
+Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
+But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
+
+[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`]
+first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
+Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
+debugging to find the actual problem behind the issue.
+
+[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
+lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
+an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
+
+[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
+[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
+[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
+[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
+[`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium
+[`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty
+[nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/
+[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43
+[if_chain]: https://docs.rs/if_chain/*/if_chain
+[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150
+
+## Writing code
+
+Have a look at the [docs for writing lints][adding_lints] for more details.
+
+If you want to add a new lint or change existing ones apart from bugfixing, it's
+also a good idea to give the [stability guarantees][rfc_stability] and
+[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a
+quick read.
+
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md
+[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees
+[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories
+
+## Getting code-completion for rustc internals to work
+
+### IntelliJ Rust
+Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals
+using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
+available via a `rustup` component at the time of writing.
+To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
+`git clone https://github.com/rust-lang/rust/`.
+Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
+which `IntelliJ Rust` will be able to understand.
+Run `cargo dev setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
+you just cloned.
+The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
+Clippys `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
+Just make sure to remove the dependencies again before finally making a pull request!
+
+[rustc_repo]: https://github.com/rust-lang/rust/
+[IntelliJ_rust_homepage]: https://intellij-rust.github.io/
+
+### Rust Analyzer
+As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals
+using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippys `Cargo.toml.`
+You will required a `nightly` toolchain with the `rustc-dev` component installed.
+Make sure that in the `rust-analyzer` configuration, you set
+```
+{ "rust-analyzer.rustcSource": "discover" }
+```
+and
+```
+{ "rust-analyzer.updates.channel": "nightly" }
+```
+You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
+a lot more type hints.
+This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later.
+
+[ra_homepage]: https://rust-analyzer.github.io/
+[6869]: https://github.com/rust-lang/rust-clippy/pull/6869
+
+## How Clippy works
+
+[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`].
+For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this:
+
+```rust
+// ./clippy_lints/src/lib.rs
+
+// ...
+pub mod else_if_without_else;
+// ...
+
+pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
+ // ...
+ store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
+ // ...
+
+ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
+ // ...
+ LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+ // ...
+ ]);
+}
+```
+
+The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints:
+[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object
+that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in
+every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev
+update_lints`. When you are writing your own lint, you can use that script to save you some time.
+
+```rust
+// ./clippy_lints/src/else_if_without_else.rs
+
+use rustc_lint::{EarlyLintPass, EarlyContext};
+
+// ...
+
+pub struct ElseIfWithoutElse;
+
+// ...
+
+impl EarlyLintPass for ElseIfWithoutElse {
+ // ... the functions needed, to make the lint work
+}
+```
+
+The difference between `EarlyLintPass` and `LateLintPass` is that the methods of the `EarlyLintPass` trait only provide
+AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information
+via the `LateContext` parameter.
+
+That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the
+[actual lint logic][else_if_without_else] does not depend on any type information.
+
+[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
+[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs
+[`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
+[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass
+[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass
+[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
+[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+
+## Syncing changes between Clippy and [`rust-lang/rust`]
+
+Clippy currently gets built with a pinned nightly version.
+
+In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy
+that compiler hackers modify from time to time to adapt to changes in the unstable
+API of the compiler.
+
+We need to sync these changes back to this repository periodically, and the changes
+made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository.
+
+To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done
+in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
+the Rust stable release and then every other week. That way we guarantee that we keep
+this repo up to date with the latest compiler API, and every feature in Clippy is available
+for 2 weeks in nightly, before it can get to beta. For reference, the first sync
+following this cadence was performed the 2020-08-27.
+
+This process is described in detail in the following sections. For general information
+about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree].
+
+### Patching git-subtree to work with big repos
+
+Currently there's a bug in `git-subtree` that prevents it from working properly
+with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale.
+Before continuing with the following steps, we need to manually apply that fix to
+our local copy of `git-subtree`.
+
+You can get the patched version of `git-subtree` from [here][gitgitgadget-pr].
+Put this file under `/usr/lib/git-core` (taking a backup of the previous file)
+and make sure it has the proper permissions:
+
+```bash
+sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
+sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
+sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
+```
+
+_Note:_ The first time running `git subtree push` a cache has to be built. This
+involves going through the complete Clippy history once. For this you have to
+increase the stack limit though, which you can do with `ulimit -s 60000`.
+Make sure to run the `ulimit` command from the same session you call git subtree.
+
+_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`.
+This shell has a hardcoded recursion limit set to 1000. In order to make this process work,
+you need to force the script to run `bash` instead. You can do this by editing the first
+line of the `git-subtree` script and changing `sh` to `bash`.
+
+### Performing the sync from [`rust-lang/rust`] to Clippy
+
+Here is a TL;DR version of the sync process (all of the following commands have
+to be run inside the `rust` directory):
+
+1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
+2. Checkout the commit from the latest available nightly. You can get it using `rustup check`.
+3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
+ ```bash
++ # Make sure to change `your-github-name` to your github name in the following command. Also be
++ # sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand
++ # because changes cannot be fast forwarded
+ git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
+ ```
+
+ _Note:_ This will directly push to the remote repository. You can also push
+ to your local copy by replacing the remote address with `/path/to/rust-clippy`
+ directory.
+
+ _Note:_ Most of the time you have to create a merge commit in the
+ `rust-clippy` repo (this has to be done in the Clippy repo, not in the
+ rust-copy of Clippy):
+ ```bash
+ git fetch origin && git fetch upstream
+ git checkout sync-from-rust
+ git merge upstream/master
+ ```
+4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
+ accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
+ ~~annoy~~ ask them in the [Zulip] stream.)
+
+### Performing the sync from Clippy to [`rust-lang/rust`]
+
+All of the following commands have to be run inside the `rust` directory.
+
+1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous
+section if necessary.
+
+2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
+ ```bash
+ git checkout -b sync-from-clippy
+ git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master
+ ```
+3. Open a PR to [`rust-lang/rust`]
+
+### Defining remotes
+
+You may want to define remotes, so you don't have to type out the remote
+addresses on every sync. You can do this with the following commands (these
+commands still have to be run inside the `rust` directory):
+
+```bash
+# Set clippy-upstream remote for pulls
+$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy
+# Make sure to not push to the upstream repo
+$ git remote set-url --push clippy-upstream DISABLED
+# Set clippy-origin remote to your fork for pushes
+$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy
+# Set a local remote
+$ git remote add clippy-local /path/to/rust-clippy
+```
+
+You can then sync with the remote names from above, e.g.:
+
+```bash
+$ git subtree push -P src/tools/clippy clippy-local sync-from-rust
+```
+
+[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
+[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
+[`rust-lang/rust`]: https://github.com/rust-lang/rust
+
+## Issue and PR triage
+
+Clippy is following the [Rust triage procedure][triage] for issues and pull
+requests.
+
+However, we are a smaller project with all contributors being volunteers
+currently. Between writing new lints, fixing issues, reviewing pull requests and
+responding to issues there may not always be enough time to stay on top of it
+all.
+
+Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
+an ICE in a popular crate that many other crates depend on. We don't
+want Clippy to crash on your code and we want it to be as reliable as the
+suggestions from Rust compiler errors.
+
+We have prioritization labels and a sync-blocker label, which are described below.
+- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent.
+- [P-medium][p-medium]: Should be addressed by a team member until the next sync.
+- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport.
+- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync.
+Or rather: before the sync this should be addressed,
+e.g. by removing a lint again, so it doesn't hit beta/stable.
+
+## Bors and Homu
+
+We use a bot powered by [Homu][homu] to help automate testing and landing of pull
+requests in Clippy. The bot's username is @bors.
+
+You can find the Clippy bors queue [here][homu_queue].
+
+If you have @bors permissions, you can find an overview of the available
+commands [here][homu_instructions].
+
+[triage]: https://forge.rust-lang.org/release/triage-procedure.html
+[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
+[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
+[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
+[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
+[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
+[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker
+[homu]: https://github.com/rust-lang/homu
+[homu_instructions]: https://bors.rust-lang.org/
+[homu_queue]: https://bors.rust-lang.org/queue/clippy
+
+## Contributions
+
+Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will
+be reviewed by a core contributor (someone with permission to land patches) and either landed in the
+main tree or given feedback for changes that would be required.
+
+All code in this repository is under the [Apache-2.0] or the [MIT] license.
+
+<!-- adapted from https://github.com/servo/servo/blob/master/CONTRIBUTING.md -->
+
+[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
+[MIT]: https://opensource.org/licenses/MIT
--- /dev/null
- compiletest_rs = { version = "0.7", features = ["tmp"] }
+[package]
+name = "clippy"
+version = "0.1.58"
+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 = "1.0"
+rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
+tempfile = { version = "3.2", optional = true }
+
+[dev-dependencies]
+cargo_metadata = "0.14"
++compiletest_rs = { version = "0.7.1", 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
- create_test(&lint).context("Unable to create a test for the new lint")
+use crate::clippy_project_root;
+use indoc::indoc;
+use std::fs::{self, OpenOptions};
+use std::io::prelude::*;
+use std::io::{self, ErrorKind};
+use std::path::{Path, PathBuf};
+
+struct LintData<'a> {
+ pass: &'a str,
+ name: &'a str,
+ category: &'a str,
+ project_root: PathBuf,
+}
+
+trait Context {
+ fn context<C: AsRef<str>>(self, text: C) -> Self;
+}
+
+impl<T> Context for io::Result<T> {
+ fn context<C: AsRef<str>>(self, text: C) -> Self {
+ match self {
+ Ok(t) => Ok(t),
+ Err(e) => {
+ let message = format!("{}: {}", text.as_ref(), e);
+ Err(io::Error::new(ErrorKind::Other, message))
+ },
+ }
+ }
+}
+
+/// Creates the files required to implement and test a new lint and runs `update_lints`.
+///
+/// # Errors
+///
+/// This function errors out if the files couldn't be created or written to.
+pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> {
+ let lint = LintData {
+ pass: pass.expect("`pass` argument is validated by clap"),
+ name: lint_name.expect("`name` argument is validated by clap"),
+ category: category.expect("`category` argument is validated by clap"),
+ project_root: clippy_project_root(),
+ };
+
+ create_lint(&lint, msrv).context("Unable to create lint implementation")?;
- let pass_name = lint.pass;
++ create_test(&lint).context("Unable to create a test for the new lint")?;
++ add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")
+}
+
+fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
+ let lint_contents = get_lint_file_contents(lint, enable_msrv);
+
+ let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
+ write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())
+}
+
+fn create_test(lint: &LintData<'_>) -> io::Result<()> {
+ fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
+ let mut path = location.into().join(case);
+ fs::create_dir(&path)?;
+ write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
+
+ path.push("src");
+ fs::create_dir(&path)?;
+ let header = format!("// compile-flags: --crate-name={}", lint_name);
+ write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
+
+ Ok(())
+ }
+
+ if lint.category == "cargo" {
+ let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
+ let test_dir = lint.project_root.join(relative_test_dir);
+ fs::create_dir(&test_dir)?;
+
+ create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
+ create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")
+ } else {
+ let test_path = format!("tests/ui/{}.rs", lint.name);
+ let test_contents = get_test_file_contents(lint.name, None);
+ write_file(lint.project_root.join(test_path), test_contents)
+ }
+}
+
++fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
++ let path = "clippy_lints/src/lib.rs";
++ let mut lib_rs = fs::read_to_string(path).context("reading")?;
++
++ let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
++
++ let new_lint = if enable_msrv {
++ format!(
++ "store.register_{lint_pass}_pass(move || Box::new({module_name}::{camel_name}::new(msrv)));\n ",
++ lint_pass = lint.pass,
++ module_name = lint.name,
++ camel_name = to_camel_case(lint.name),
++ )
++ } else {
++ format!(
++ "store.register_{lint_pass}_pass(|| Box::new({module_name}::{camel_name}));\n ",
++ lint_pass = lint.pass,
++ module_name = lint.name,
++ camel_name = to_camel_case(lint.name),
++ )
++ };
++
++ lib_rs.insert_str(comment_start, &new_lint);
++
++ fs::write(path, lib_rs).context("writing")
++}
++
+fn write_file<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
+ fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
+ OpenOptions::new()
+ .write(true)
+ .create_new(true)
+ .open(path)?
+ .write_all(contents)
+ }
+
+ inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display()))
+}
+
+fn to_camel_case(name: &str) -> String {
+ name.split('_')
+ .map(|s| {
+ if s.is_empty() {
+ String::from("")
+ } else {
+ [&s[0..1].to_uppercase(), &s[1..]].concat()
+ }
+ })
+ .collect()
+}
+
+fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
+ let mut contents = format!(
+ indoc! {"
+ #![warn(clippy::{})]
+
+ fn main() {{
+ // test code goes here
+ }}
+ "},
+ lint_name
+ );
+
+ if let Some(header) = header_commands {
+ contents = format!("{}\n{}", header, contents);
+ }
+
+ contents
+}
+
+fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
+ format!(
+ indoc! {r#"
+ # {}
+
+ [package]
+ name = "{}"
+ version = "0.1.0"
+ publish = false
+
+ [workspace]
+ "#},
+ hint, lint_name
+ )
+}
+
+fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
+ let mut result = String::new();
+
+ let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
+ "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
+ "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
+ _ => {
+ unreachable!("`pass_type` should only ever be `early` or `late`!");
+ },
+ };
+
+ let lint_name = lint.name;
- // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
- // e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv)));
+ let category = lint.category;
+ let name_camel = to_camel_case(lint.name);
+ let name_upper = lint_name.to_uppercase();
+
+ result.push_str(&if enable_msrv {
+ format!(
+ indoc! {"
+ use clippy_utils::msrvs;
+ {pass_import}
+ use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
+ use rustc_semver::RustcVersion;
+ use rustc_session::{{declare_tool_lint, impl_lint_pass}};
+
+ "},
+ pass_type = pass_type,
+ pass_import = pass_import,
+ context_import = context_import,
+ )
+ } else {
+ format!(
+ indoc! {"
+ {pass_import}
+ use rustc_lint::{{{context_import}, {pass_type}}};
+ use rustc_session::{{declare_lint_pass, declare_tool_lint}};
+
+ "},
+ pass_import = pass_import,
+ pass_type = pass_type,
+ context_import = context_import
+ )
+ });
+
+ result.push_str(&format!(
+ indoc! {"
+ declare_clippy_lint! {{
+ /// ### What it does
+ ///
+ /// ### Why is this bad?
+ ///
+ /// ### Example
+ /// ```rust
+ /// // example code where clippy issues a warning
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// // example code which does not raise clippy warning
+ /// ```
+ pub {name_upper},
+ {category},
+ \"default lint description\"
+ }}
+ "},
+ name_upper = name_upper,
+ category = category,
+ ));
+
+ result.push_str(&if enable_msrv {
+ format!(
+ indoc! {"
+ pub struct {name_camel} {{
+ msrv: Option<RustcVersion>,
+ }}
+
+ impl {name_camel} {{
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {{
+ Self {{ msrv }}
+ }}
+ }}
+
+ impl_lint_pass!({name_camel} => [{name_upper}]);
+
+ impl {pass_type}{pass_lifetimes} for {name_camel} {{
+ extract_msrv_attr!({context_import});
+ }}
+
- pass_name = pass_name,
+ // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
+ // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
+ // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
+ "},
+ pass_type = pass_type,
+ pass_lifetimes = pass_lifetimes,
- module_name = lint_name,
+ name_upper = name_upper,
+ name_camel = name_camel,
- //
- // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
- // e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel}));
+ context_import = context_import,
+ )
+ } else {
+ format!(
+ indoc! {"
+ declare_lint_pass!({name_camel} => [{name_upper}]);
+
+ impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
- pass_name = pass_name,
+ "},
+ pass_type = pass_type,
+ pass_lifetimes = pass_lifetimes,
- module_name = lint_name,
+ name_upper = name_upper,
+ name_camel = name_camel,
+ )
+ });
+
+ result
+}
+
+#[test]
+fn test_camel_case() {
+ let s = "a_lint";
+ let s2 = to_camel_case(s);
+ assert_eq!(s2, "ALint");
+
+ let name = "a_really_long_new_lint";
+ let name2 = to_camel_case(name);
+ assert_eq!(name2, "AReallyLongNewLint");
+
+ let name3 = "lint__name";
+ let name4 = to_camel_case(name3);
+ assert_eq!(name4, "LintName");
+}
--- /dev/null
- use rustc_hir::Expr;
++use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint;
++use clippy_utils::expr_or_init;
+use clippy_utils::ty::is_isize_or_usize;
- pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, FloatTy, Ty};
+
+use super::{utils, CAST_POSSIBLE_TRUNCATION};
+
- let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
++fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
++ if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
++ Some(c)
++ } else {
++ None
++ }
++}
++
++fn get_constant_bits(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u64> {
++ constant_int(cx, expr).map(|c| u64::from(128 - c.leading_zeros()))
++}
++
++fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: bool) -> u64 {
++ match expr_or_init(cx, expr).kind {
++ ExprKind::Cast(inner, _) => apply_reductions(cx, nbits, inner, signed),
++ ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
++ ExprKind::Binary(op, left, right) => match op.node {
++ BinOpKind::Div => {
++ apply_reductions(cx, nbits, left, signed)
++ - (if signed {
++ 0 // let's be conservative here
++ } else {
++ // by dividing by 1, we remove 0 bits, etc.
++ get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
++ })
++ },
++ BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
++ .unwrap_or(u64::max_value())
++ .min(apply_reductions(cx, nbits, left, signed)),
++ BinOpKind::Shr => {
++ apply_reductions(cx, nbits, left, signed)
++ - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
++ },
++ _ => nbits,
++ },
++ ExprKind::MethodCall(method, _, [left, right], _) => {
++ if signed {
++ return nbits;
++ }
++ let max_bits = if method.ident.as_str() == "min" {
++ get_constant_bits(cx, right)
++ } else {
++ None
++ };
++ apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
++ },
++ ExprKind::MethodCall(method, _, [_, lo, hi], _) => {
++ if method.ident.as_str() == "clamp" {
++ //FIXME: make this a diagnostic item
++ if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
++ return lo_bits.max(hi_bits);
++ }
++ }
++ nbits
++ },
++ ExprKind::MethodCall(method, _, [_value], _) => {
++ if method.ident.name.as_str() == "signum" {
++ 0 // do not lint if cast comes from a `signum` function
++ } else {
++ nbits
++ }
++ },
++ _ => nbits,
++ }
++}
++
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+ let msg = match (cast_from.is_integral(), cast_to.is_integral()) {
+ (true, true) => {
++ let from_nbits = apply_reductions(
++ cx,
++ utils::int_ty_to_nbits(cast_from, cx.tcx),
++ cast_expr,
++ cast_from.is_signed(),
++ );
+ let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
+
+ let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
+ (true, true) | (false, false) => (to_nbits < from_nbits, ""),
+ (true, false) => (
+ to_nbits <= 32,
+ if to_nbits == 32 {
+ " on targets with 64-bit wide pointers"
+ } else {
+ ""
+ },
+ ),
+ (false, true) => (from_nbits == 64, " on targets with 32-bit wide pointers"),
+ };
+
+ if !should_lint {
+ return;
+ }
+
+ format!(
+ "casting `{}` to `{}` may truncate the value{}",
+ cast_from, cast_to, suffix,
+ )
+ },
+
+ (false, true) => {
+ format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to)
+ },
+
+ (_, _) => {
+ if matches!(cast_from.kind(), &ty::Float(FloatTy::F64))
+ && matches!(cast_to.kind(), &ty::Float(FloatTy::F32))
+ {
+ "casting `f64` to `f32` may truncate the value".to_string()
+ } else {
+ return;
+ }
+ },
+ };
+
+ span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
+}
--- /dev/null
- cast_possible_truncation::check(cx, expr, cast_from, cast_to);
+mod cast_lossless;
+mod cast_possible_truncation;
+mod cast_possible_wrap;
+mod cast_precision_loss;
+mod cast_ptr_alignment;
+mod cast_ref_to_mut;
+mod cast_sign_loss;
+mod char_lit_as_u8;
+mod fn_to_numeric_cast;
+mod fn_to_numeric_cast_any;
+mod fn_to_numeric_cast_with_truncation;
+mod ptr_as_ptr;
+mod unnecessary_cast;
+mod utils;
+
+use clippy_utils::is_hir_ty_cfg_dependant;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts from any numerical to a float type where
+ /// the receiving type cannot store all values from the original type without
+ /// rounding errors. This possible rounding is to be expected, so this lint is
+ /// `Allow` by default.
+ ///
+ /// Basically, this warns on casting any integer with 32 or more bits to `f32`
+ /// or any 64-bit integer to `f64`.
+ ///
+ /// ### Why is this bad?
+ /// It's not bad at all. But in some applications it can be
+ /// helpful to know where precision loss can take place. This lint can help find
+ /// those places in the code.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = u64::MAX;
+ /// x as f64;
+ /// ```
+ pub CAST_PRECISION_LOSS,
+ pedantic,
+ "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts from a signed to an unsigned numerical
+ /// type. In this case, negative values wrap around to large positive values,
+ /// which can be quite surprising in practice. However, as the cast works as
+ /// defined, this lint is `Allow` by default.
+ ///
+ /// ### Why is this bad?
+ /// Possibly surprising results. You can activate this lint
+ /// as a one-time check to see where numerical wrapping can arise.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let y: i8 = -1;
+ /// y as u128; // will return 18446744073709551615
+ /// ```
+ pub CAST_SIGN_LOSS,
+ pedantic,
+ "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts between numerical types that may
+ /// truncate large values. This is expected behavior, so the cast is `Allow` by
+ /// default.
+ ///
+ /// ### Why is this bad?
+ /// In some problem domains, it is good practice to avoid
+ /// truncation. This lint can be activated to help assess where additional
+ /// checks could be beneficial.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn as_u8(x: u64) -> u8 {
+ /// x as u8
+ /// }
+ /// ```
+ pub CAST_POSSIBLE_TRUNCATION,
+ pedantic,
+ "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts from an unsigned type to a signed type of
+ /// the same size. Performing such a cast is a 'no-op' for the compiler,
+ /// i.e., nothing is changed at the bit level, and the binary representation of
+ /// the value is reinterpreted. This can cause wrapping if the value is too big
+ /// for the target signed type. However, the cast works as defined, so this lint
+ /// is `Allow` by default.
+ ///
+ /// ### Why is this bad?
+ /// While such a cast is not bad in itself, the results can
+ /// be surprising when this is not the intended behavior, as demonstrated by the
+ /// example below.
+ ///
+ /// ### Example
+ /// ```rust
+ /// u32::MAX as i32; // will yield a value of `-1`
+ /// ```
+ pub CAST_POSSIBLE_WRAP,
+ pedantic,
+ "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts between numerical types that may
+ /// be replaced by safe conversion functions.
+ ///
+ /// ### Why is this bad?
+ /// Rust's `as` keyword will perform many kinds of
+ /// conversions, including silently lossy conversions. Conversion functions such
+ /// as `i32::from` will only perform lossless conversions. Using the conversion
+ /// functions prevents conversions from turning into silent lossy conversions if
+ /// the types of the input expressions ever change, and make it easier for
+ /// people reading the code to know that the conversion is lossless.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn as_u64(x: u8) -> u64 {
+ /// x as u64
+ /// }
+ /// ```
+ ///
+ /// Using `::from` would look like this:
+ ///
+ /// ```rust
+ /// fn as_u64(x: u8) -> u64 {
+ /// u64::from(x)
+ /// }
+ /// ```
+ pub CAST_LOSSLESS,
+ pedantic,
+ "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts to the same type, casts of int literals to integer types
+ /// and casts of float literals to float types.
+ ///
+ /// ### Why is this bad?
+ /// It's just unnecessary.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = 2i32 as i32;
+ /// let _ = 0.5 as f32;
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust
+ /// let _ = 2_i32;
+ /// let _ = 0.5_f32;
+ /// ```
+ pub UNNECESSARY_CAST,
+ complexity,
+ "cast to the same type, e.g., `x as i32` where `x: i32`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts, using `as` or `pointer::cast`,
+ /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
+ ///
+ /// ### Why is this bad?
+ /// Dereferencing the resulting pointer may be undefined
+ /// behavior.
+ ///
+ /// ### Known problems
+ /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
+ /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
+ /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = (&1u8 as *const u8) as *const u16;
+ /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
+ ///
+ /// (&1u8 as *const u8).cast::<u16>();
+ /// (&mut 1u8 as *mut u8).cast::<u16>();
+ /// ```
+ pub CAST_PTR_ALIGNMENT,
+ pedantic,
+ "cast from a pointer to a more-strictly-aligned pointer"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts of function pointers to something other than usize
+ ///
+ /// ### Why is this bad?
+ /// Casting a function pointer to anything other than usize/isize is not portable across
+ /// architectures, because you end up losing bits if the target type is too small or end up with a
+ /// bunch of extra bits that waste space and add more instructions to the final binary than
+ /// strictly necessary for the problem
+ ///
+ /// Casting to isize also doesn't make sense since there are no signed addresses.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// fn fun() -> i32 { 1 }
+ /// let a = fun as i64;
+ ///
+ /// // Good
+ /// fn fun2() -> i32 { 1 }
+ /// let a = fun2 as usize;
+ /// ```
+ pub FN_TO_NUMERIC_CAST,
+ style,
+ "casting a function pointer to a numeric type other than usize"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts of a function pointer to a numeric type not wide enough to
+ /// store address.
+ ///
+ /// ### Why is this bad?
+ /// Such a cast discards some bits of the function's address. If this is intended, it would be more
+ /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
+ /// a comment) to perform the truncation.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// fn fn1() -> i16 {
+ /// 1
+ /// };
+ /// let _ = fn1 as i32;
+ ///
+ /// // Better: Cast to usize first, then comment with the reason for the truncation
+ /// fn fn2() -> i16 {
+ /// 1
+ /// };
+ /// let fn_ptr = fn2 as usize;
+ /// let fn_ptr_truncated = fn_ptr as i32;
+ /// ```
+ pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
+ style,
+ "casting a function pointer to a numeric type not wide enough to store the address"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts of a function pointer to any integer type.
+ ///
+ /// ### Why is this bad?
+ /// Casting a function pointer to an integer can have surprising results and can occur
+ /// accidentally if parantheses are omitted from a function call. If you aren't doing anything
+ /// low-level with function pointers then you can opt-out of casting functions to integers in
+ /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
+ /// pointer casts in your code.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad: fn1 is cast as `usize`
+ /// fn fn1() -> u16 {
+ /// 1
+ /// };
+ /// let _ = fn1 as usize;
+ ///
+ /// // Good: maybe you intended to call the function?
+ /// fn fn2() -> u16 {
+ /// 1
+ /// };
+ /// let _ = fn2() as usize;
+ ///
+ /// // Good: maybe you intended to cast it to a function type?
+ /// fn fn3() -> u16 {
+ /// 1
+ /// }
+ /// let _ = fn3 as fn() -> u16;
+ /// ```
+ pub FN_TO_NUMERIC_CAST_ANY,
+ restriction,
+ "casting a function pointer to any integer type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for casts of `&T` to `&mut T` anywhere in the code.
+ ///
+ /// ### Why is this bad?
+ /// It’s basically guaranteed to be undefined behaviour.
+ /// `UnsafeCell` is the only way to obtain aliasable data that is considered
+ /// mutable.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// fn x(r: &i32) {
+ /// unsafe {
+ /// *(r as *const _ as *mut _) += 1;
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Instead consider using interior mutability types.
+ ///
+ /// ```rust
+ /// use std::cell::UnsafeCell;
+ ///
+ /// fn x(r: &UnsafeCell<i32>) {
+ /// unsafe {
+ /// *r.get() += 1;
+ /// }
+ /// }
+ /// ```
+ pub CAST_REF_TO_MUT,
+ correctness,
+ "a cast of reference to a mutable pointer"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for expressions where a character literal is cast
+ /// to `u8` and suggests using a byte literal instead.
+ ///
+ /// ### Why is this bad?
+ /// In general, casting values to smaller types is
+ /// error-prone and should be avoided where possible. In the particular case of
+ /// converting a character literal to u8, it is easy to avoid by just using a
+ /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
+ /// than `'a' as u8`.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// 'x' as u8
+ /// ```
+ ///
+ /// A better version, using the byte literal:
+ ///
+ /// ```rust,ignore
+ /// b'x'
+ /// ```
+ pub CHAR_LIT_AS_U8,
+ complexity,
+ "casting a character literal to `u8` truncates"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `as` casts between raw pointers without changing its mutability,
+ /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
+ ///
+ /// ### Why is this bad?
+ /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
+ /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let ptr: *const u32 = &42_u32;
+ /// let mut_ptr: *mut u32 = &mut 42_u32;
+ /// let _ = ptr as *const i32;
+ /// let _ = mut_ptr as *mut i32;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let ptr: *const u32 = &42_u32;
+ /// let mut_ptr: *mut u32 = &mut 42_u32;
+ /// let _ = ptr.cast::<i32>();
+ /// let _ = mut_ptr.cast::<i32>();
+ /// ```
+ pub PTR_AS_PTR,
+ pedantic,
+ "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
+}
+
+pub struct Casts {
+ msrv: Option<RustcVersion>,
+}
+
+impl Casts {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self { msrv }
+ }
+}
+
+impl_lint_pass!(Casts => [
+ CAST_PRECISION_LOSS,
+ CAST_SIGN_LOSS,
+ CAST_POSSIBLE_TRUNCATION,
+ CAST_POSSIBLE_WRAP,
+ CAST_LOSSLESS,
+ CAST_REF_TO_MUT,
+ CAST_PTR_ALIGNMENT,
+ UNNECESSARY_CAST,
+ FN_TO_NUMERIC_CAST_ANY,
+ FN_TO_NUMERIC_CAST,
+ FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
+ CHAR_LIT_AS_U8,
+ PTR_AS_PTR,
+]);
+
+impl<'tcx> LateLintPass<'tcx> for Casts {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
+ if is_hir_ty_cfg_dependant(cx, cast_to) {
+ return;
+ }
+ let (cast_from, cast_to) = (
+ cx.typeck_results().expr_ty(cast_expr),
+ cx.typeck_results().expr_ty(expr),
+ );
+
+ if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
+ return;
+ }
+
+ fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
+ fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
+ fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
+ if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
++ cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
+ cast_possible_wrap::check(cx, expr, cast_from, cast_to);
+ cast_precision_loss::check(cx, expr, cast_from, cast_to);
+ cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
+ cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+ }
+ }
+
+ cast_ref_to_mut::check(cx, expr);
+ cast_ptr_alignment::check(cx, expr);
+ char_lit_as_u8::check(cx, expr);
+ ptr_as_ptr::check(cx, expr, &self.msrv);
+ }
+
+ extract_msrv_attr!(LateContext);
+}
--- /dev/null
++// NOTE: if you add a deprecated lint in this file, please add a corresponding test in
++// tests/ui/deprecated.rs
++
+/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This
+/// enables the simple extraction of the metadata without changing the current deprecation
+/// declaration.
+pub struct ClippyDeprecatedLint;
+
+macro_rules! declare_deprecated_lint {
+ { $(#[$attr:meta])* pub $name: ident, $_reason: expr} => {
+ $(#[$attr])*
+ #[allow(dead_code)]
+ pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {};
+ }
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This used to check for `assert!(a == b)` and recommend
+ /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
+ pub SHOULD_ASSERT_EQ,
+ "`assert!()` will be more flexible with RFC 2011"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This used to check for `Vec::extend`, which was slower than
+ /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
+ pub EXTEND_FROM_SLICE,
+ "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// `Range::step_by(0)` used to be linted since it's
+ /// an infinite iterator, which is better expressed by `iter::repeat`,
+ /// but the method has been removed for `Iterator::step_by` which panics
+ /// if given a zero
+ pub RANGE_STEP_BY_ZERO,
+ "`iterator.step_by(0)` panics nowadays"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This used to check for `Vec::as_slice`, which was unstable with good
+ /// stable alternatives. `Vec::as_slice` has now been stabilized.
+ pub UNSTABLE_AS_SLICE,
+ "`Vec::as_slice` has been stabilized in 1.7"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This used to check for `Vec::as_mut_slice`, which was unstable with good
+ /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
+ pub UNSTABLE_AS_MUT_SLICE,
+ "`Vec::as_mut_slice` has been stabilized in 1.7"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This lint should never have applied to non-pointer types, as transmuting
+ /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
+ /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
+ /// cast_ptr_alignment and transmute_ptr_to_ptr.
+ pub MISALIGNED_TRANSMUTE,
+ "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This lint is too subjective, not having a good reason for being in clippy.
+ /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
+ /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
+ pub ASSIGN_OPS,
+ "using compound assignment operators (e.g., `+=`) is harmless"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// The original rule will only lint for `if let`. After
+ /// making it support to lint `match`, naming as `if let` is not suitable for it.
+ /// So, this lint is deprecated.
+ pub IF_LET_REDUNDANT_PATTERN_MATCHING,
+ "this lint has been changed to redundant_pattern_matching"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This lint used to suggest replacing `let mut vec =
+ /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
+ /// replacement has very different performance characteristics so the lint is
+ /// deprecated.
+ pub UNSAFE_VECTOR_INITIALIZATION,
+ "the replacement suggested by this lint had substantially different behavior"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This lint has been superseded by #[must_use] in rustc.
+ pub UNUSED_COLLECT,
+ "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// Associated-constants are now preferred.
+ pub REPLACE_CONSTS,
+ "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// The regex! macro does not exist anymore.
+ pub REGEX_MACRO,
+ "the regex! macro has been removed from the regex crate in 2018"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This lint has been replaced by `manual_find_map`, a
+ /// more specific lint.
+ pub FIND_MAP,
+ "this lint has been replaced by `manual_find_map`, a more specific lint"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This lint has been replaced by `manual_filter_map`, a
+ /// more specific lint.
+ pub FILTER_MAP,
+ "this lint has been replaced by `manual_filter_map`, a more specific lint"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// The `avoid_breaking_exported_api` config option was added, which
+ /// enables the `enum_variant_names` lint for public items.
+ /// ```
+ pub PUB_ENUM_VARIANT_NAMES,
+ "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// The `avoid_breaking_exported_api` config option was added, which
+ /// enables the `wrong_self_conversion` lint for public items.
+ pub WRONG_PUB_SELF_CONVENTION,
+ "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
+}
--- /dev/null
- }
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::peel_mid_ty_refs;
+use clippy_utils::{get_parent_node, in_macro, is_lint_allowed};
+use rustc_ast::util::parser::PREC_PREFIX;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{symbol::sym, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for explicit `deref()` or `deref_mut()` method calls.
+ ///
+ /// ### Why is this bad?
+ /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
+ /// when not part of a method chain.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::ops::Deref;
+ /// let a: &mut String = &mut String::from("foo");
+ /// let b: &str = a.deref();
+ /// ```
+ /// Could be written as:
+ /// ```rust
+ /// let a: &mut String = &mut String::from("foo");
+ /// let b = &*a;
+ /// ```
+ ///
+ /// This lint excludes
+ /// ```rust,ignore
+ /// let _ = d.unwrap().deref();
+ /// ```
+ pub EXPLICIT_DEREF_METHODS,
+ pedantic,
+ "Explicit use of deref or deref_mut method while not in a method chain."
+}
+
+impl_lint_pass!(Dereferencing => [
+ EXPLICIT_DEREF_METHODS,
+]);
+
+#[derive(Default)]
+pub struct Dereferencing {
+ state: Option<(State, StateData)>,
+
+ // While parsing a `deref` method call in ufcs form, the path to the function is itself an
+ // expression. This is to store the id of that expression so it can be skipped when
+ // `check_expr` is called for it.
+ skip_expr: Option<HirId>,
+}
+
+struct StateData {
+ /// Span of the top level expression
+ span: Span,
+ /// The required mutability
+ target_mut: Mutability,
+}
+
+enum State {
+ // Any number of deref method calls.
+ DerefMethod {
+ // The number of calls in a sequence which changed the referenced type
+ ty_changed_count: usize,
+ is_final_ufcs: bool,
+ },
+}
+
+// A reference operation considered by this lint pass
+enum RefOp {
+ Method(Mutability),
+ Deref,
+ AddrOf,
+}
+
+impl<'tcx> LateLintPass<'tcx> for Dereferencing {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
+ if Some(expr.hir_id) == self.skip_expr.take() {
+ return;
+ }
+
+ // Stop processing sub expressions when a macro call is seen
+ if in_macro(expr.span) {
+ if let Some((state, data)) = self.state.take() {
+ report(cx, expr, state, data);
+ }
+ return;
+ }
+
+ let typeck = cx.typeck_results();
+ let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
+ x
+ } else {
+ // The whole chain of reference operations has been seen
+ if let Some((state, data)) = self.state.take() {
+ report(cx, expr, state, data);
+ }
+ return;
+ };
+
+ match (self.state.take(), kind) {
+ (None, kind) => {
+ let parent = get_parent_node(cx.tcx, expr.hir_id);
+ let expr_ty = typeck.expr_ty(expr);
+
+ match kind {
+ RefOp::Method(target_mut)
+ if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
+ && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
+ {
+ self.state = Some((
+ State::DerefMethod {
+ ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
+ 0
+ } else {
+ 1
+ },
+ is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
+ },
+ StateData {
+ span: expr.span,
+ target_mut,
+ },
+ ));
++ },
+ _ => (),
+ }
+ },
+ (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => {
+ self.state = Some((
+ State::DerefMethod {
+ ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
+ ty_changed_count
+ } else {
+ ty_changed_count + 1
+ },
+ is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
+ },
+ data,
+ ));
+ },
+
+ (Some((state, data)), _) => report(cx, expr, state, data),
+ }
+ }
+}
+
+fn try_parse_ref_op(
+ tcx: TyCtxt<'tcx>,
+ typeck: &'tcx TypeckResults<'_>,
+ expr: &'tcx Expr<'_>,
+) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
+ let (def_id, arg) = match expr.kind {
+ ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(path),
+ hir_id,
+ ..
+ },
+ [arg],
+ ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
+ ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
+ return Some((RefOp::Deref, sub_expr));
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
+ _ => return None,
+ };
+ if tcx.is_diagnostic_item(sym::deref_method, def_id) {
+ Some((RefOp::Method(Mutability::Not), arg))
+ } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
+ Some((RefOp::Method(Mutability::Mut), arg))
+ } else {
+ None
+ }
+}
+
+// Checks whether the type for a deref call actually changed the type, not just the mutability of
+// the reference.
+fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
+ match (result_ty.kind(), arg_ty.kind()) {
+ (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty),
+
+ // The result type for a deref method is always a reference
+ // Not matching the previous pattern means the argument type is not a reference
+ // This means that the type did change
+ _ => false,
+ }
+}
+
+// Checks whether the parent node is a suitable context for switching from a deref method to the
+// deref operator.
+fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
+ let parent = match parent {
+ Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e,
+ _ => return true,
+ };
+ match parent.kind {
+ // Leave deref calls in the middle of a method chain.
+ // e.g. x.deref().foo()
+ ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false,
+
+ // Leave deref calls resulting in a called function
+ // e.g. (x.deref())()
+ ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false,
+
+ // Makes an ugly suggestion
+ // e.g. *x.deref() => *&*x
+ ExprKind::Unary(UnOp::Deref, _)
+ // Postfix expressions would require parens
+ | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+ | ExprKind::Field(..)
+ | ExprKind::Index(..)
+ | ExprKind::Err => false,
+
+ ExprKind::Box(..)
+ | ExprKind::ConstBlock(..)
+ | ExprKind::Array(_)
+ | ExprKind::Call(..)
+ | ExprKind::MethodCall(..)
+ | ExprKind::Tup(..)
+ | ExprKind::Binary(..)
+ | ExprKind::Unary(..)
+ | ExprKind::Lit(..)
+ | ExprKind::Cast(..)
+ | ExprKind::Type(..)
+ | ExprKind::DropTemps(..)
+ | ExprKind::If(..)
+ | ExprKind::Loop(..)
+ | ExprKind::Match(..)
+ | ExprKind::Let(..)
+ | ExprKind::Closure(..)
+ | ExprKind::Block(..)
+ | ExprKind::Assign(..)
+ | ExprKind::AssignOp(..)
+ | ExprKind::Path(..)
+ | ExprKind::AddrOf(..)
+ | ExprKind::Break(..)
+ | ExprKind::Continue(..)
+ | ExprKind::Ret(..)
+ | ExprKind::InlineAsm(..)
+ | ExprKind::LlvmInlineAsm(..)
+ | ExprKind::Struct(..)
+ | ExprKind::Repeat(..)
+ | ExprKind::Yield(..) => true,
+ }
+}
+
+#[allow(clippy::needless_pass_by_value)]
+fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
+ match state {
+ State::DerefMethod {
+ ty_changed_count,
+ is_final_ufcs,
+ } => {
+ let mut app = Applicability::MachineApplicable;
+ let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+ let ty = cx.typeck_results().expr_ty(expr);
+ let (_, ref_count) = peel_mid_ty_refs(ty);
+ let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
+ // a deref call changing &T -> &U requires two deref operators the first time
+ // this occurs. One to remove the reference, a second to call the deref impl.
+ "*".repeat(ty_changed_count + 1)
+ } else {
+ "*".repeat(ty_changed_count)
+ };
+ let addr_of_str = if ty_changed_count < ref_count {
+ // Check if a reborrow from &mut T -> &T is required.
+ if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+ "&*"
+ } else {
+ ""
+ }
+ } else if data.target_mut == Mutability::Mut {
+ "&mut "
+ } else {
+ "&"
+ };
+
+ let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
+ format!("({})", expr_str)
+ } else {
+ expr_str.into_owned()
+ };
+
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_DEREF_METHODS,
+ data.span,
+ match data.target_mut {
+ Mutability::Not => "explicit `deref` method call",
+ Mutability::Mut => "explicit `deref_mut` method call",
+ },
+ "try this",
+ format!("{}{}{}", addr_of_str, deref_str, expr_str),
+ app,
+ );
+ },
+ }
+}
--- /dev/null
- use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note};
- use clippy_utils::source::first_line_of_span;
+use clippy_utils::attrs::is_doc_hidden;
- use rustc_errors::Handler;
++use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg};
++use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
+use if_chain::if_chain;
+use itertools::Itertools;
+use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind};
+use rustc_ast::token::CommentKind;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::emitter::EmitterWriter;
- /// inside a link text would trip the parser. Therfore, documenting link with
++use rustc_errors::{Applicability, Handler};
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_parse::maybe_new_parser_from_source_str;
+use rustc_parse::parser::ForceCollect;
+use rustc_session::parse::ParseSess;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
+use rustc_span::{sym, FileName, Pos};
+use std::io;
+use std::ops::Range;
+use std::thread;
+use url::Url;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the presence of `_`, `::` or camel-case words
+ /// outside ticks in documentation.
+ ///
+ /// ### Why is this bad?
+ /// *Rustdoc* supports markdown formatting, `_`, `::` and
+ /// camel-case probably indicates some code which should be included between
+ /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
+ /// consider that.
+ ///
+ /// ### Known problems
+ /// Lots of bad docs won’t be fixed, what the lint checks
+ /// for is limited, and there are still false positives. HTML elements and their
+ /// content are not linted.
+ ///
+ /// In addition, when writing documentation comments, including `[]` brackets
- headers.safety |= in_heading && text.trim() == "Safety";
- headers.errors |= in_heading && text.trim() == "Errors";
- headers.panics |= in_heading && text.trim() == "Panics";
++ /// inside a link text would trip the parser. Therefore, documenting link with
+ /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
+ /// would fail.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// /// Do something with the foo_bar parameter. See also
+ /// /// that::other::module::foo.
+ /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
+ /// fn doit(foo_bar: usize) {}
+ /// ```
+ ///
+ /// ```rust
+ /// // Link text with `[]` brackets should be written as following:
+ /// /// Consume the array and return the inner
+ /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
+ /// /// [SmallVec]: SmallVec
+ /// fn main() {}
+ /// ```
+ pub DOC_MARKDOWN,
+ pedantic,
+ "presence of `_`, `::` or camel-case outside backticks in documentation"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the doc comments of publicly visible
+ /// unsafe functions and warns if there is no `# Safety` section.
+ ///
+ /// ### Why is this bad?
+ /// Unsafe functions should document their safety
+ /// preconditions, so that users can be sure they are using them safely.
+ ///
+ /// ### Examples
+ /// ```rust
+ ///# type Universe = ();
+ /// /// This function should really be documented
+ /// pub unsafe fn start_apocalypse(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ ///
+ /// At least write a line about safety:
+ ///
+ /// ```rust
+ ///# type Universe = ();
+ /// /// # Safety
+ /// ///
+ /// /// This function should not be called before the horsemen are ready.
+ /// pub unsafe fn start_apocalypse(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ pub MISSING_SAFETY_DOC,
+ style,
+ "`pub unsafe fn` without `# Safety` docs"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks the doc comments of publicly visible functions that
+ /// return a `Result` type and warns if there is no `# Errors` section.
+ ///
+ /// ### Why is this bad?
+ /// Documenting the type of errors that can be returned from a
+ /// function can help callers write code to handle the errors appropriately.
+ ///
+ /// ### Examples
+ /// Since the following function returns a `Result` it has an `# Errors` section in
+ /// its doc comment:
+ ///
+ /// ```rust
+ ///# use std::io;
+ /// /// # Errors
+ /// ///
+ /// /// Will return `Err` if `filename` does not exist or the user does not have
+ /// /// permission to read it.
+ /// pub fn read(filename: String) -> io::Result<String> {
+ /// unimplemented!();
+ /// }
+ /// ```
+ pub MISSING_ERRORS_DOC,
+ pedantic,
+ "`pub fn` returns `Result` without `# Errors` in doc comment"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks the doc comments of publicly visible functions that
+ /// may panic and warns if there is no `# Panics` section.
+ ///
+ /// ### Why is this bad?
+ /// Documenting the scenarios in which panicking occurs
+ /// can help callers who do not want to panic to avoid those situations.
+ ///
+ /// ### Examples
+ /// Since the following function may panic it has a `# Panics` section in
+ /// its doc comment:
+ ///
+ /// ```rust
+ /// /// # Panics
+ /// ///
+ /// /// Will panic if y is 0
+ /// pub fn divide_by(x: i32, y: i32) -> i32 {
+ /// if y == 0 {
+ /// panic!("Cannot divide by 0")
+ /// } else {
+ /// x / y
+ /// }
+ /// }
+ /// ```
+ pub MISSING_PANICS_DOC,
+ pedantic,
+ "`pub fn` may panic without `# Panics` in doc comment"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `fn main() { .. }` in doctests
+ ///
+ /// ### Why is this bad?
+ /// The test can be shorter (and likely more readable)
+ /// if the `fn main()` is left implicit.
+ ///
+ /// ### Examples
+ /// ``````rust
+ /// /// An example of a doctest with a `main()` function
+ /// ///
+ /// /// # Examples
+ /// ///
+ /// /// ```
+ /// /// fn main() {
+ /// /// // this needs not be in an `fn`
+ /// /// }
+ /// /// ```
+ /// fn needless_main() {
+ /// unimplemented!();
+ /// }
+ /// ``````
+ pub NEEDLESS_DOCTEST_MAIN,
+ style,
+ "presence of `fn main() {` in code examples"
+}
+
+#[allow(clippy::module_name_repetitions)]
+#[derive(Clone)]
+pub struct DocMarkdown {
+ valid_idents: FxHashSet<String>,
+ in_trait_impl: bool,
+}
+
+impl DocMarkdown {
+ pub fn new(valid_idents: FxHashSet<String>) -> Self {
+ Self {
+ valid_idents,
+ in_trait_impl: false,
+ }
+ }
+}
+
+impl_lint_pass!(DocMarkdown =>
+ [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
+);
+
+impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
+ fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+ let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
+ check_attrs(cx, &self.valid_idents, attrs);
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let headers = check_attrs(cx, &self.valid_idents, attrs);
+ match item.kind {
+ hir::ItemKind::Fn(ref sig, _, body_id) => {
+ if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
+ let body = cx.tcx.hir().body(body_id);
+ let mut fpu = FindPanicUnwrap {
+ cx,
+ typeck_results: cx.tcx.typeck(item.def_id),
+ panic_span: None,
+ };
+ fpu.visit_expr(&body.value);
+ lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
+ }
+ },
+ hir::ItemKind::Impl(ref impl_) => {
+ self.in_trait_impl = impl_.of_trait.is_some();
+ },
+ hir::ItemKind::Trait(_, unsafety, ..) => {
+ if !headers.safety && unsafety == hir::Unsafety::Unsafe {
+ span_lint(
+ cx,
+ MISSING_SAFETY_DOC,
+ item.span,
+ "docs for unsafe trait missing `# Safety` section",
+ );
+ }
+ },
+ _ => (),
+ }
+ }
+
+ fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ if let hir::ItemKind::Impl { .. } = item.kind {
+ self.in_trait_impl = false;
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let headers = check_attrs(cx, &self.valid_idents, attrs);
+ if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
+ if !in_external_macro(cx.tcx.sess, item.span) {
+ lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, None, None);
+ }
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let headers = check_attrs(cx, &self.valid_idents, attrs);
+ if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
+ return;
+ }
+ if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
+ let body = cx.tcx.hir().body(body_id);
+ let mut fpu = FindPanicUnwrap {
+ cx,
+ typeck_results: cx.tcx.typeck(item.def_id),
+ panic_span: None,
+ };
+ fpu.visit_expr(&body.value);
+ lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
+ }
+ }
+}
+
+fn lint_for_missing_headers<'tcx>(
+ cx: &LateContext<'tcx>,
+ def_id: LocalDefId,
+ span: impl Into<MultiSpan> + Copy,
+ sig: &hir::FnSig<'_>,
+ headers: DocHeaders,
+ body_id: Option<hir::BodyId>,
+ panic_span: Option<Span>,
+) {
+ if !cx.access_levels.is_exported(def_id) {
+ return; // Private functions do not require doc comments
+ }
+
+ // do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
+ if cx
+ .tcx
+ .hir()
+ .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id))
+ .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
+ {
+ return;
+ }
+
+ if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
+ span_lint(
+ cx,
+ MISSING_SAFETY_DOC,
+ span,
+ "unsafe function's docs miss `# Safety` section",
+ );
+ }
+ if !headers.panics && panic_span.is_some() {
+ span_lint_and_note(
+ cx,
+ MISSING_PANICS_DOC,
+ span,
+ "docs for function which may panic missing `# Panics` section",
+ panic_span,
+ "first possible panic found here",
+ );
+ }
+ if !headers.errors {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
+ if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
+ span_lint(
+ cx,
+ MISSING_ERRORS_DOC,
+ span,
+ "docs for function returning `Result` missing `# Errors` section",
+ );
+ } else {
+ if_chain! {
+ if let Some(body_id) = body_id;
+ if let Some(future) = cx.tcx.lang_items().future_trait();
+ let typeck = cx.tcx.typeck_body(body_id);
+ let body = cx.tcx.hir().body(body_id);
+ let ret_ty = typeck.expr_ty(&body.value);
+ if implements_trait(cx, ret_ty, future, &[]);
+ if let ty::Opaque(_, subs) = ret_ty.kind();
+ if let Some(gen) = subs.types().next();
+ if let ty::Generator(_, subs, _) = gen.kind();
+ if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
+ then {
+ span_lint(
+ cx,
+ MISSING_ERRORS_DOC,
+ span,
+ "docs for function returning `Result` missing `# Errors` section",
+ );
+ }
+ }
+ }
+ }
+}
+
+/// Cleanup documentation decoration.
+///
+/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
+/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
+/// need to keep track of
+/// the spans but this function is inspired from the later.
+#[allow(clippy::cast_possible_truncation)]
+#[must_use]
+pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
+ // one-line comments lose their prefix
+ if comment_kind == CommentKind::Line {
+ let mut doc = doc.to_owned();
+ doc.push('\n');
+ let len = doc.len();
+ // +3 skips the opening delimiter
+ return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
+ }
+
+ let mut sizes = vec![];
+ let mut contains_initial_stars = false;
+ for line in doc.lines() {
+ let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
+ debug_assert_eq!(offset as u32 as usize, offset);
+ contains_initial_stars |= line.trim_start().starts_with('*');
+ // +1 adds the newline, +3 skips the opening delimiter
+ sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
+ }
+ if !contains_initial_stars {
+ return (doc.to_string(), sizes);
+ }
+ // remove the initial '*'s if any
+ let mut no_stars = String::with_capacity(doc.len());
+ for line in doc.lines() {
+ let mut chars = line.chars();
+ for c in &mut chars {
+ if c.is_whitespace() {
+ no_stars.push(c);
+ } else {
+ no_stars.push(if c == '*' { ' ' } else { c });
+ break;
+ }
+ }
+ no_stars.push_str(chars.as_str());
+ no_stars.push('\n');
+ }
+
+ (no_stars, sizes)
+}
+
+#[derive(Copy, Clone)]
+struct DocHeaders {
+ safety: bool,
+ errors: bool,
+ panics: bool,
+}
+
+fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
+ use pulldown_cmark::{BrokenLink, CowStr, Options};
+ /// We don't want the parser to choke on intra doc links. Since we don't
+ /// actually care about rendering them, just pretend that all broken links are
+ /// point to a fake address.
+ #[allow(clippy::unnecessary_wraps)] // we're following a type signature
+ fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
+ Some(("fake".into(), "fake".into()))
+ }
+
+ let mut doc = String::new();
+ let mut spans = vec![];
+
+ for attr in attrs {
+ if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
+ let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
+ spans.extend_from_slice(¤t_spans);
+ doc.push_str(&comment);
+ } else if attr.has_name(sym::doc) {
+ // ignore mix of sugared and non-sugared doc
+ // don't trigger the safety or errors check
+ return DocHeaders {
+ safety: true,
+ errors: true,
+ panics: true,
+ };
+ }
+ }
+
+ let mut current = 0;
+ for &mut (ref mut offset, _) in &mut spans {
+ let offset_copy = *offset;
+ *offset = current;
+ current += offset_copy;
+ }
+
+ if doc.is_empty() {
+ return DocHeaders {
+ safety: false,
+ errors: false,
+ panics: false,
+ };
+ }
+
+ let mut cb = fake_broken_link_callback;
+
+ let parser =
+ pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
+ // Iterate over all `Events` and combine consecutive events into one
+ let events = parser.coalesce(|previous, current| {
+ use pulldown_cmark::Event::Text;
+
+ let previous_range = previous.1;
+ let current_range = current.1;
+
+ match (previous.0, current.0) {
+ (Text(previous), Text(current)) => {
+ let mut previous = previous.to_string();
+ previous.push_str(¤t);
+ Ok((Text(previous.into()), previous_range))
+ },
+ (previous, current) => Err(((previous, previous_range), (current, current_range))),
+ }
+ });
+ check_doc(cx, valid_idents, events, &spans)
+}
+
+const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
+
+fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
+ cx: &LateContext<'_>,
+ valid_idents: &FxHashSet<String>,
+ events: Events,
+ spans: &[(usize, Span)],
+) -> DocHeaders {
+ // true if a safety header was found
+ use pulldown_cmark::Event::{
+ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
+ };
+ use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
+ use pulldown_cmark::{CodeBlockKind, CowStr};
+
+ let mut headers = DocHeaders {
+ safety: false,
+ errors: false,
+ panics: false,
+ };
+ let mut in_code = false;
+ let mut in_link = None;
+ let mut in_heading = false;
+ let mut is_rust = false;
+ let mut edition = None;
+ let mut ticks_unbalanced = false;
+ let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
+ let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
+ for (event, range) in events {
+ match event {
+ Start(CodeBlock(ref kind)) => {
+ in_code = true;
+ if let CodeBlockKind::Fenced(lang) = kind {
+ for item in lang.split(',') {
+ if item == "ignore" {
+ is_rust = false;
+ break;
+ }
+ if let Some(stripped) = item.strip_prefix("edition") {
+ is_rust = true;
+ edition = stripped.parse::<Edition>().ok();
+ } else if item.is_empty() || RUST_CODE.contains(&item) {
+ is_rust = true;
+ }
+ }
+ }
+ },
+ End(CodeBlock(_)) => {
+ in_code = false;
+ is_rust = false;
+ },
+ Start(Link(_, url, _)) => in_link = Some(url),
+ End(Link(..)) => in_link = None,
+ Start(Heading(_) | Paragraph | Item) => {
+ if let Start(Heading(_)) = event {
+ in_heading = true;
+ }
+ ticks_unbalanced = false;
+ let (_, span) = get_current_span(spans, range.start);
+ paragraph_span = first_line_of_span(cx, span);
+ },
+ End(Heading(_) | Paragraph | Item) => {
+ if let End(Heading(_)) = event {
+ in_heading = false;
+ }
+ if ticks_unbalanced {
+ span_lint_and_help(
+ cx,
+ DOC_MARKDOWN,
+ paragraph_span,
+ "backticks are unbalanced",
+ None,
+ "a backtick may be missing a pair",
+ );
+ } else {
+ for (text, span) in text_to_check {
+ check_text(cx, valid_idents, &text, span);
+ }
+ }
+ text_to_check = Vec::new();
+ },
+ Start(_tag) | End(_tag) => (), // We don't care about other tags
+ Html(_html) => (), // HTML is weird, just ignore it
+ SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
+ FootnoteReference(text) | Text(text) => {
+ let (begin, span) = get_current_span(spans, range.start);
+ paragraph_span = paragraph_span.with_hi(span.hi());
+ ticks_unbalanced |= text.contains('`') && !in_code;
+ if Some(&text) == in_link.as_ref() || ticks_unbalanced {
+ // Probably a link of the form `<http://example.com>`
+ // Which are represented as a link to "http://example.com" with
+ // text "http://example.com" by pulldown-cmark
+ continue;
+ }
- // Or even as in `_foo bar_` which is emphasized.
- let word = word.trim_matches(|c: char| !c.is_alphanumeric());
++ let trimmed_text = text.trim();
++ headers.safety |= in_heading && trimmed_text == "Safety";
++ headers.safety |= in_heading && trimmed_text == "Implementation safety";
++ headers.safety |= in_heading && trimmed_text == "Implementation Safety";
++ headers.errors |= in_heading && trimmed_text == "Errors";
++ headers.panics |= in_heading && trimmed_text == "Panics";
+ if in_code {
+ if is_rust {
+ let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
+ check_code(cx, &text, edition, span);
+ }
+ } else {
+ // Adjust for the beginning of the current `Event`
+ let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
+ text_to_check.push((text, span));
+ }
+ },
+ }
+ }
+ headers
+}
+
+fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
+ let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
+ Ok(o) => o,
+ Err(e) => e - 1,
+ };
+ spans[index]
+}
+
+fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
+ fn has_needless_main(code: String, edition: Edition) -> bool {
+ rustc_driver::catch_fatal_errors(|| {
+ rustc_span::create_session_globals_then(edition, || {
+ let filename = FileName::anon_source_code(&code);
+
+ let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+ let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false);
+ let handler = Handler::with_emitter(false, None, Box::new(emitter));
+ let sess = ParseSess::with_span_handler(handler, sm);
+
+ let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
+ Ok(p) => p,
+ Err(errs) => {
+ for mut err in errs {
+ err.cancel();
+ }
+ return false;
+ },
+ };
+
+ let mut relevant_main_found = false;
+ loop {
+ match parser.parse_item(ForceCollect::No) {
+ Ok(Some(item)) => match &item.kind {
+ // Tests with one of these items are ignored
+ ItemKind::Static(..)
+ | ItemKind::Const(..)
+ | ItemKind::ExternCrate(..)
+ | ItemKind::ForeignMod(..) => return false,
+ // We found a main function ...
+ ItemKind::Fn(box FnKind(_, sig, _, Some(block))) if item.ident.name == sym::main => {
+ let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
+ let returns_nothing = match &sig.decl.output {
+ FnRetTy::Default(..) => true,
+ FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
+ FnRetTy::Ty(_) => false,
+ };
+
+ if returns_nothing && !is_async && !block.stmts.is_empty() {
+ // This main function should be linted, but only if there are no other functions
+ relevant_main_found = true;
+ } else {
+ // This main function should not be linted, we're done
+ return false;
+ }
+ },
+ // Another function was found; this case is ignored too
+ ItemKind::Fn(..) => return false,
+ _ => {},
+ },
+ Ok(None) => break,
+ Err(mut e) => {
+ e.cancel();
+ return false;
+ },
+ }
+ }
+
+ relevant_main_found
+ })
+ })
+ .ok()
+ .unwrap_or_default()
+ }
+
+ // Because of the global session, we need to create a new session in a different thread with
+ // the edition we need.
+ let text = text.to_owned();
+ if thread::spawn(move || has_needless_main(text, edition))
+ .join()
+ .expect("thread::spawn failed")
+ {
+ span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
+ }
+}
+
+fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
+ for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
+ // Trim punctuation as in `some comment (see foo::bar).`
+ // ^^
- if valid_idents.contains(word) {
++ // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
++ let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
+
- // We assume that mixed-case words are not meant to be put inside bacticks. (Issue #2343)
++ // Remove leading or trailing single `:` which may be part of a sentence.
++ if word.starts_with(':') && !word.starts_with("::") {
++ word = word.trim_start_matches(':');
++ }
++ if word.ends_with(':') && !word.ends_with("::") {
++ word = word.trim_end_matches(':');
++ }
++
++ if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
+ continue;
+ }
+
+ // Adjust for the current word
+ let offset = word.as_ptr() as usize - text.as_ptr() as usize;
+ let span = Span::new(
+ span.lo() + BytePos::from_usize(offset),
+ span.lo() + BytePos::from_usize(offset + word.len()),
+ span.ctxt(),
+ span.parent(),
+ );
+
+ check_word(cx, word, span);
+ }
+}
+
+fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
+ /// Checks if a string is camel-case, i.e., contains at least two uppercase
+ /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
+ /// Plurals are also excluded (`IDs` is ok).
+ fn is_camel_case(s: &str) -> bool {
+ if s.starts_with(|c: char| c.is_digit(10)) {
+ return false;
+ }
+
+ let s = s.strip_suffix('s').unwrap_or(s);
+
+ s.chars().all(char::is_alphanumeric)
+ && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
+ && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
+ }
+
+ fn has_underscore(s: &str) -> bool {
+ s != "_" && !s.contains("\\_") && s.contains('_')
+ }
+
+ fn has_hyphen(s: &str) -> bool {
+ s != "-" && s.contains('-')
+ }
+
+ if let Ok(url) = Url::parse(word) {
+ // try to get around the fact that `foo::bar` parses as a valid URL
+ if !url.cannot_be_a_base() {
+ span_lint(
+ cx,
+ DOC_MARKDOWN,
+ span,
+ "you should put bare URLs between `<`/`>` or make a proper Markdown link",
+ );
+
+ return;
+ }
+ }
+
- span_lint(
++ // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
+ if has_underscore(word) && has_hyphen(word) {
+ return;
+ }
+
+ if has_underscore(word) || word.contains("::") || is_camel_case(word) {
- &format!("you should put `{}` between ticks in the documentation", word),
++ let mut applicability = Applicability::MachineApplicable;
++
++ span_lint_and_sugg(
+ cx,
+ DOC_MARKDOWN,
+ span,
- let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
- if is_type_diagnostic_item(self.cx, reciever_ty, sym::Option)
- || is_type_diagnostic_item(self.cx, reciever_ty, sym::Result)
++ "item in documentation is missing backticks",
++ "try",
++ format!("`{}`", snippet_with_applicability(cx, span, "..", &mut applicability)),
++ applicability,
+ );
+ }
+}
+
+struct FindPanicUnwrap<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ panic_span: Option<Span>,
+ typeck_results: &'tcx ty::TypeckResults<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.panic_span.is_some() {
+ return;
+ }
+
+ // check for `begin_panic`
+ if_chain! {
+ if let ExprKind::Call(func_expr, _) = expr.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
+ if let Some(path_def_id) = path.res.opt_def_id();
+ if match_panic_def_id(self.cx, path_def_id);
+ if is_expn_of(expr.span, "unreachable").is_none();
+ if !is_expn_of_debug_assertions(expr.span);
+ then {
+ self.panic_span = Some(expr.span);
+ }
+ }
+
+ // check for `assert_eq` or `assert_ne`
+ if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() {
+ self.panic_span = Some(expr.span);
+ }
+
+ // check for `unwrap`
+ if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
++ let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
++ if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
++ || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
+ {
+ self.panic_span = Some(expr.span);
+ }
+ }
+
+ // and check sub-expressions
+ intravisit::walk_expr(self, expr);
+ }
+
+ // Panics in const blocks will cause compilation to fail.
+ fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+}
+
+fn is_expn_of_debug_assertions(span: Span) -> bool {
+ const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
+ MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some())
+}
--- /dev/null
- [map, Expr {
- kind: ExprKind::AddrOf(_, _, key),
- span: key_span,
- ..
- }],
+use clippy_utils::higher;
+use clippy_utils::{
+ can_move_expr_to_closure_no_visit,
+ diagnostics::span_lint_and_sugg,
+ is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
+ source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
+ SpanlessEq,
+};
+use core::fmt::Write;
+use rustc_errors::Applicability;
+use rustc_hir::{
+ hir_id::HirIdSet,
+ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+ Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{Span, SyntaxContext, DUMMY_SP};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for uses of `contains_key` + `insert` on `HashMap`
+ /// or `BTreeMap`.
+ ///
+ /// ### Why is this bad?
+ /// Using `entry` is more efficient.
+ ///
+ /// ### Known problems
+ /// The suggestion may have type inference errors in some cases. e.g.
+ /// ```rust
+ /// let mut map = std::collections::HashMap::new();
+ /// let _ = if !map.contains_key(&0) {
+ /// map.insert(0, 0)
+ /// } else {
+ /// None
+ /// };
+ /// ```
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::collections::HashMap;
+ /// # let mut map = HashMap::new();
+ /// # let k = 1;
+ /// # let v = 1;
+ /// if !map.contains_key(&k) {
+ /// map.insert(k, v);
+ /// }
+ /// ```
+ /// can both be rewritten as:
+ /// ```rust
+ /// # use std::collections::HashMap;
+ /// # let mut map = HashMap::new();
+ /// # let k = 1;
+ /// # let v = 1;
+ /// map.entry(k).or_insert(v);
+ /// ```
+ pub MAP_ENTRY,
+ perf,
+ "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
+}
+
+declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
+
+impl<'tcx> LateLintPass<'tcx> for HashMapPass {
+ #[allow(clippy::too_many_lines)]
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) {
+ Some(higher::If { cond, then, r#else }) => (cond, then, r#else),
+ _ => return,
+ };
+
+ let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
+ Some(x) => x,
+ None => return,
+ };
+
+ let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
+ Some(x) => x,
+ None => return,
+ };
+
+ let mut app = Applicability::MachineApplicable;
+ let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
+ let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
+ let sugg = if let Some(else_expr) = else_expr {
+ let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
+ Some(search) => search,
+ None => return,
+ };
+
+ if then_search.edits.is_empty() && else_search.edits.is_empty() {
+ // No insertions
+ return;
+ } else if then_search.edits.is_empty() || else_search.edits.is_empty() {
+ // if .. { insert } else { .. } or if .. { .. } else { insert }
+ let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
+ (true, true) => (
+ then_search.snippet_vacant(cx, then_expr.span, &mut app),
+ snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+ ),
+ (true, false) => (
+ then_search.snippet_occupied(cx, then_expr.span, &mut app),
+ snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+ ),
+ (false, true) => (
+ else_search.snippet_occupied(cx, else_expr.span, &mut app),
+ snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+ ),
+ (false, false) => (
+ else_search.snippet_vacant(cx, else_expr.span, &mut app),
+ snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+ ),
+ };
+ format!(
+ "if let {}::{} = {}.entry({}) {} else {}",
+ map_ty.entry_path(),
+ entry_kind,
+ map_str,
+ key_str,
+ then_str,
+ else_str,
+ )
+ } else {
+ // if .. { insert } else { insert }
+ let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
+ (
+ then_search.snippet_vacant(cx, then_expr.span, &mut app),
+ else_search.snippet_occupied(cx, else_expr.span, &mut app),
+ )
+ } else {
+ (
+ then_search.snippet_occupied(cx, then_expr.span, &mut app),
+ else_search.snippet_vacant(cx, else_expr.span, &mut app),
+ )
+ };
+ let indent_str = snippet_indent(cx, expr.span);
+ let indent_str = indent_str.as_deref().unwrap_or("");
+ format!(
+ "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\
+ {indent} {entry}::{} => {}\n{indent}}}",
+ map_str,
+ key_str,
+ then_entry,
+ reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
+ else_entry,
+ reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
+ entry = map_ty.entry_path(),
+ indent = indent_str,
+ )
+ }
+ } else {
+ if then_search.edits.is_empty() {
+ // no insertions
+ return;
+ }
+
+ // if .. { insert }
+ if !then_search.allow_insert_closure {
+ let (body_str, entry_kind) = if contains_expr.negated {
+ then_search.snippet_vacant(cx, then_expr.span, &mut app)
+ } else {
+ then_search.snippet_occupied(cx, then_expr.span, &mut app)
+ };
+ format!(
+ "if let {}::{} = {}.entry({}) {}",
+ map_ty.entry_path(),
+ entry_kind,
+ map_str,
+ key_str,
+ body_str,
+ )
+ } else if let Some(insertion) = then_search.as_single_insertion() {
+ let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
+ if contains_expr.negated {
+ if insertion.value.can_have_side_effects() {
+ format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
+ } else {
+ format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
+ }
+ } else {
+ // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+ // This would need to be a different lint.
+ return;
+ }
+ } else {
+ let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
+ if contains_expr.negated {
+ format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str)
+ } else {
+ // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+ // This would need to be a different lint.
+ return;
+ }
+ }
+ };
+
+ span_lint_and_sugg(
+ cx,
+ MAP_ENTRY,
+ expr.span,
+ &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
+ "try this",
+ sugg,
+ app,
+ );
+ }
+}
+
+#[derive(Clone, Copy)]
+enum MapType {
+ Hash,
+ BTree,
+}
+impl MapType {
+ fn name(self) -> &'static str {
+ match self {
+ Self::Hash => "HashMap",
+ Self::BTree => "BTreeMap",
+ }
+ }
+ fn entry_path(self) -> &'static str {
+ match self {
+ Self::Hash => "std::collections::hash_map::Entry",
+ Self::BTree => "std::collections::btree_map::Entry",
+ }
+ }
+}
+
+struct ContainsExpr<'tcx> {
+ negated: bool,
+ map: &'tcx Expr<'tcx>,
+ key: &'tcx Expr<'tcx>,
+ call_ctxt: SyntaxContext,
+}
+fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
+ let mut negated = false;
+ let expr = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::Unary(UnOp::Not, e) => {
+ negated = !negated;
+ Some(e)
+ },
+ _ => None,
+ });
+ match expr.kind {
+ ExprKind::MethodCall(
+ _,
+ _,
++ [
++ map,
++ Expr {
++ kind: ExprKind::AddrOf(_, _, key),
++ span: key_span,
++ ..
++ },
++ ],
+ _,
+ ) if key_span.ctxt() == expr.span.ctxt() => {
+ let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+ let expr = ContainsExpr {
+ negated,
+ map,
+ key,
+ call_ctxt: expr.span.ctxt(),
+ };
+ if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) {
+ Some((MapType::BTree, expr))
+ } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) {
+ Some((MapType::Hash, expr))
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+}
+
+struct InsertExpr<'tcx> {
+ map: &'tcx Expr<'tcx>,
+ key: &'tcx Expr<'tcx>,
+ value: &'tcx Expr<'tcx>,
+}
+fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
+ if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
+ let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+ if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
+ Some(InsertExpr { map, key, value })
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+/// An edit that will need to be made to move the expression to use the entry api
+#[derive(Clone, Copy)]
+enum Edit<'tcx> {
+ /// A semicolon that needs to be removed. Used to create a closure for `insert_with`.
+ RemoveSemi(Span),
+ /// An insertion into the map.
+ Insertion(Insertion<'tcx>),
+}
+impl Edit<'tcx> {
+ fn as_insertion(self) -> Option<Insertion<'tcx>> {
+ if let Self::Insertion(i) = self { Some(i) } else { None }
+ }
+}
+#[derive(Clone, Copy)]
+struct Insertion<'tcx> {
+ call: &'tcx Expr<'tcx>,
+ value: &'tcx Expr<'tcx>,
+}
+
+/// This visitor needs to do a multiple things:
+/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
+/// * Determine if there's an insertion using the same key. There's no need for the entry api
+/// otherwise.
+/// * Determine if the final statement executed is an insertion. This is needed to use
+/// `or_insert_with`.
+/// * Determine if there's any sub-expression that can't be placed in a closure.
+/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
+#[allow(clippy::struct_excessive_bools)]
+struct InsertSearcher<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ /// The map expression used in the contains call.
+ map: &'tcx Expr<'tcx>,
+ /// The key expression used in the contains call.
+ key: &'tcx Expr<'tcx>,
+ /// The context of the top level block. All insert calls must be in the same context.
+ ctxt: SyntaxContext,
+ /// Whether this expression can be safely moved into a closure.
+ allow_insert_closure: bool,
+ /// Whether this expression can use the entry api.
+ can_use_entry: bool,
+ /// Whether this expression is the final expression in this code path. This may be a statement.
+ in_tail_pos: bool,
+ // Is this expression a single insert. A slightly better suggestion can be made in this case.
+ is_single_insert: bool,
+ /// If the visitor has seen the map being used.
+ is_map_used: bool,
+ /// The locations where changes need to be made for the suggestion.
+ edits: Vec<Edit<'tcx>>,
+ /// A stack of loops the visitor is currently in.
+ loops: Vec<HirId>,
+ /// Local variables created in the expression. These don't need to be captured.
+ locals: HirIdSet,
+}
+impl<'tcx> InsertSearcher<'_, 'tcx> {
+ /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
+ /// only if they are on separate code paths. This will return whether the map was used in the
+ /// given expression.
+ fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool {
+ let is_map_used = self.is_map_used;
+ let in_tail_pos = self.in_tail_pos;
+ self.visit_expr(e);
+ let res = self.is_map_used;
+ self.is_map_used = is_map_used;
+ self.in_tail_pos = in_tail_pos;
+ res
+ }
+
+ /// Visits an expression which is not itself in a tail position, but other sibling expressions
+ /// may be. e.g. if conditions
+ fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
+ let in_tail_pos = self.in_tail_pos;
+ self.in_tail_pos = false;
+ self.visit_expr(e);
+ self.in_tail_pos = in_tail_pos;
+ }
+}
+impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+ match stmt.kind {
+ StmtKind::Semi(e) => {
+ self.visit_expr(e);
+
+ if self.in_tail_pos && self.allow_insert_closure {
+ // The spans are used to slice the top level expression into multiple parts. This requires that
+ // they all come from the same part of the source code.
+ if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt {
+ self.edits
+ .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP)));
+ } else {
+ self.allow_insert_closure = false;
+ }
+ }
+ },
+ StmtKind::Expr(e) => self.visit_expr(e),
+ StmtKind::Local(l) => {
+ self.visit_pat(l.pat);
+ if let Some(e) = l.init {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ self.in_tail_pos = false;
+ self.is_single_insert = false;
+ self.visit_expr(e);
+ }
+ },
+ StmtKind::Item(_) => {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ self.is_single_insert = false;
+ },
+ }
+ }
+
+ fn visit_block(&mut self, block: &'tcx Block<'_>) {
+ // If the block is in a tail position, then the last expression (possibly a statement) is in the
+ // tail position. The rest, however, are not.
+ match (block.stmts, block.expr) {
+ ([], None) => {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ },
+ ([], Some(expr)) => self.visit_expr(expr),
+ (stmts, Some(expr)) => {
+ let in_tail_pos = self.in_tail_pos;
+ self.in_tail_pos = false;
+ for stmt in stmts {
+ self.visit_stmt(stmt);
+ }
+ self.in_tail_pos = in_tail_pos;
+ self.visit_expr(expr);
+ },
+ ([stmts @ .., stmt], None) => {
+ let in_tail_pos = self.in_tail_pos;
+ self.in_tail_pos = false;
+ for stmt in stmts {
+ self.visit_stmt(stmt);
+ }
+ self.in_tail_pos = in_tail_pos;
+ self.visit_stmt(stmt);
+ },
+ }
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if !self.can_use_entry {
+ return;
+ }
+
+ match try_parse_insert(self.cx, expr) {
+ Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
+ // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
+ if self.is_map_used
+ || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
+ || expr.span.ctxt() != self.ctxt
+ {
+ self.can_use_entry = false;
+ return;
+ }
+
+ self.edits.push(Edit::Insertion(Insertion {
+ call: expr,
+ value: insert_expr.value,
+ }));
+ self.is_map_used = true;
+ self.allow_insert_closure &= self.in_tail_pos;
+
+ // The value doesn't affect whether there is only a single insert expression.
+ let is_single_insert = self.is_single_insert;
+ self.visit_non_tail_expr(insert_expr.value);
+ self.is_single_insert = is_single_insert;
+ },
+ _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => {
+ self.is_map_used = true;
+ },
+ _ => match expr.kind {
+ ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
+ self.is_single_insert = false;
+ self.visit_non_tail_expr(cond_expr);
+ // Each branch may contain it's own insert expression.
+ let mut is_map_used = self.visit_cond_arm(then_expr);
+ is_map_used |= self.visit_cond_arm(else_expr);
+ self.is_map_used = is_map_used;
+ },
+ ExprKind::Match(scrutinee_expr, arms, _) => {
+ self.is_single_insert = false;
+ self.visit_non_tail_expr(scrutinee_expr);
+ // Each branch may contain it's own insert expression.
+ let mut is_map_used = self.is_map_used;
+ for arm in arms {
+ self.visit_pat(arm.pat);
+ if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
+ self.visit_non_tail_expr(guard);
+ }
+ is_map_used |= self.visit_cond_arm(arm.body);
+ }
+ self.is_map_used = is_map_used;
+ },
+ ExprKind::Loop(block, ..) => {
+ self.loops.push(expr.hir_id);
+ self.is_single_insert = false;
+ self.allow_insert_closure &= !self.in_tail_pos;
+ // Don't allow insertions inside of a loop.
+ let edit_len = self.edits.len();
+ self.visit_block(block);
+ if self.edits.len() != edit_len {
+ self.can_use_entry = false;
+ }
+ self.loops.pop();
+ },
+ ExprKind::Block(block, _) => self.visit_block(block),
+ ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => {
+ self.can_use_entry = false;
+ },
+ _ => {
+ self.allow_insert_closure &= !self.in_tail_pos;
+ self.allow_insert_closure &=
+ can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops, &self.locals);
+ // Sub expressions are no longer in the tail position.
+ self.is_single_insert = false;
+ self.in_tail_pos = false;
+ walk_expr(self, expr);
+ },
+ },
+ }
+ }
+
+ fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
+ p.each_binding_or_first(&mut |_, id, _, _| {
+ self.locals.insert(id);
+ });
+ }
+}
+
+struct InsertSearchResults<'tcx> {
+ edits: Vec<Edit<'tcx>>,
+ allow_insert_closure: bool,
+ is_single_insert: bool,
+}
+impl InsertSearchResults<'tcx> {
+ fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
+ self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
+ }
+
+ fn snippet(
+ &self,
+ cx: &LateContext<'_>,
+ mut span: Span,
+ app: &mut Applicability,
+ write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability),
+ ) -> String {
+ let ctxt = span.ctxt();
+ let mut res = String::new();
+ for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) {
+ res.push_str(&snippet_with_applicability(
+ cx,
+ span.until(insertion.call.span),
+ "..",
+ app,
+ ));
+ if is_expr_used_or_unified(cx.tcx, insertion.call) {
+ write_wrapped(&mut res, insertion, ctxt, app);
+ } else {
+ let _ = write!(
+ res,
+ "e.insert({})",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+ );
+ }
+ span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
+ }
+ res.push_str(&snippet_with_applicability(cx, span, "..", app));
+ res
+ }
+
+ fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+ (
+ self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+ // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
+ let _ = write!(
+ res,
+ "Some(e.insert({}))",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+ );
+ }),
+ "Occupied(mut e)",
+ )
+ }
+
+ fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+ (
+ self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+ // Insertion into a map would return `None`, but the entry returns a mutable reference.
+ let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
+ write!(
+ res,
+ "e.insert({});\n{}None",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+ snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
+ )
+ } else {
+ write!(
+ res,
+ "{{ e.insert({}); None }}",
+ snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+ )
+ };
+ }),
+ "Vacant(e)",
+ )
+ }
+
+ fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String {
+ let ctxt = span.ctxt();
+ let mut res = String::new();
+ for edit in &self.edits {
+ match *edit {
+ Edit::Insertion(insertion) => {
+ // Cut out the value from `map.insert(key, value)`
+ res.push_str(&snippet_with_applicability(
+ cx,
+ span.until(insertion.call.span),
+ "..",
+ app,
+ ));
+ res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0);
+ span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
+ },
+ Edit::RemoveSemi(semi_span) => {
+ // Cut out the semicolon. This allows the value to be returned from the closure.
+ res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app));
+ span = span.trim_start(semi_span).unwrap_or(DUMMY_SP);
+ },
+ }
+ }
+ res.push_str(&snippet_with_applicability(cx, span, "..", app));
+ res
+ }
+}
+
+fn find_insert_calls(
+ cx: &LateContext<'tcx>,
+ contains_expr: &ContainsExpr<'tcx>,
+ expr: &'tcx Expr<'_>,
+) -> Option<InsertSearchResults<'tcx>> {
+ let mut s = InsertSearcher {
+ cx,
+ map: contains_expr.map,
+ key: contains_expr.key,
+ ctxt: expr.span.ctxt(),
+ edits: Vec::new(),
+ is_map_used: false,
+ allow_insert_closure: true,
+ can_use_entry: true,
+ in_tail_pos: true,
+ is_single_insert: true,
+ loops: Vec::new(),
+ locals: HirIdSet::default(),
+ };
+ s.visit_expr(expr);
+ let allow_insert_closure = s.allow_insert_closure;
+ let is_single_insert = s.is_single_insert;
+ let edits = s.edits;
+ s.can_use_entry.then(|| InsertSearchResults {
+ edits,
+ allow_insert_closure,
+ is_single_insert,
+ })
+}
--- /dev/null
- use clippy_utils::camel_case;
+//! lint on enum variants that are prefixed or suffixed by the same characters
+
- /// Returns the number of chars that match from the start
- #[must_use]
- fn partial_match(pre: &str, name: &str) -> usize {
- let mut name_iter = name.chars();
- let _ = name_iter.next_back(); // make sure the name is never fully matched
- pre.chars().zip(name_iter).take_while(|&(l, r)| l == r).count()
- }
-
- /// Returns the number of chars that match from the end
- #[must_use]
- fn partial_rmatch(post: &str, name: &str) -> usize {
- let mut name_iter = name.chars();
- let _ = name_iter.next(); // make sure the name is never fully matched
- post.chars()
- .rev()
- .zip(name_iter.rev())
- .take_while(|&(l, r)| l == r)
- .count()
- }
-
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::source::is_present_in_source;
++use clippy_utils::str_utils::{self, count_match_end, count_match_start};
+use rustc_hir::{EnumDef, Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+use rustc_span::symbol::Symbol;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects enumeration variants that are prefixed or suffixed
+ /// by the same characters.
+ ///
+ /// ### Why is this bad?
+ /// Enumeration variant names should specify their variant,
+ /// not repeat the enumeration name.
+ ///
+ /// ### Example
+ /// ```rust
+ /// enum Cake {
+ /// BlackForestCake,
+ /// HummingbirdCake,
+ /// BattenbergCake,
+ /// }
+ /// ```
+ /// Could be written as:
+ /// ```rust
+ /// enum Cake {
+ /// BlackForest,
+ /// Hummingbird,
+ /// Battenberg,
+ /// }
+ /// ```
+ pub ENUM_VARIANT_NAMES,
+ style,
+ "enums where all variants share a prefix/postfix"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects type names that are prefixed or suffixed by the
+ /// containing module's name.
+ ///
+ /// ### Why is this bad?
+ /// It requires the user to type the module name twice.
+ ///
+ /// ### Example
+ /// ```rust
+ /// mod cake {
+ /// struct BlackForestCake;
+ /// }
+ /// ```
+ /// Could be written as:
+ /// ```rust
+ /// mod cake {
+ /// struct BlackForest;
+ /// }
+ /// ```
+ pub MODULE_NAME_REPETITIONS,
+ pedantic,
+ "type names prefixed/postfixed with their containing module's name"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for modules that have the same name as their
+ /// parent module
+ ///
+ /// ### Why is this bad?
+ /// A typical beginner mistake is to have `mod foo;` and
+ /// again `mod foo { ..
+ /// }` in `foo.rs`.
+ /// The expectation is that items inside the inner `mod foo { .. }` are then
+ /// available
+ /// through `foo::x`, but they are only available through
+ /// `foo::foo::x`.
+ /// If this is done on purpose, it would be better to choose a more
+ /// representative module name.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// // lib.rs
+ /// mod foo;
+ /// // foo.rs
+ /// mod foo {
+ /// ...
+ /// }
+ /// ```
+ pub MODULE_INCEPTION,
+ style,
+ "modules that have the same name as their parent module"
+}
+
+pub struct EnumVariantNames {
+ modules: Vec<(Symbol, String)>,
+ threshold: u64,
+ avoid_breaking_exported_api: bool,
+}
+
+impl EnumVariantNames {
+ #[must_use]
+ pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self {
+ Self {
+ modules: Vec::new(),
+ threshold,
+ avoid_breaking_exported_api,
+ }
+ }
+}
+
+impl_lint_pass!(EnumVariantNames => [
+ ENUM_VARIANT_NAMES,
+ MODULE_NAME_REPETITIONS,
+ MODULE_INCEPTION
+]);
+
- if partial_match(item_name, &name) == item_name_chars
+fn check_variant(
+ cx: &LateContext<'_>,
+ threshold: u64,
+ def: &EnumDef<'_>,
+ item_name: &str,
+ item_name_chars: usize,
+ span: Span,
+) {
+ if (def.variants.len() as u64) < threshold {
+ return;
+ }
+ for var in def.variants {
+ let name = var.ident.name.as_str();
- if partial_rmatch(item_name, &name) == item_name_chars {
++ if count_match_start(item_name, &name).char_count == item_name_chars
+ && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
+ && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
+ {
+ span_lint(
+ cx,
+ ENUM_VARIANT_NAMES,
+ var.span,
+ "variant name starts with the enum's name",
+ );
+ }
- let mut pre = &first[..camel_case::until(&*first)];
- let mut post = &first[camel_case::from(&*first)..];
++ if count_match_end(item_name, &name).char_count == item_name_chars {
+ span_lint(
+ cx,
+ ENUM_VARIANT_NAMES,
+ var.span,
+ "variant name ends with the enum's name",
+ );
+ }
+ }
+ let first = &def.variants[0].ident.name.as_str();
- let pre_match = partial_match(pre, &name);
++ let mut pre = &first[..str_utils::camel_case_until(&*first).byte_index];
++ let mut post = &first[str_utils::camel_case_start(&*first).byte_index..];
+ for var in def.variants {
+ let name = var.ident.name.as_str();
+
- let pre_camel = camel_case::until(pre);
++ let pre_match = count_match_start(pre, &name).byte_count;
+ pre = &pre[..pre_match];
- let last_camel = camel_case::until(&pre[..last]);
- pre = &pre[..last_camel];
++ let pre_camel = str_utils::camel_case_until(pre).byte_index;
+ pre = &pre[..pre_camel];
+ while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
+ if next.is_numeric() {
+ return;
+ }
+ if next.is_lowercase() {
+ let last = pre.len() - last.len_utf8();
- let post_match = partial_rmatch(post, &name);
- let post_end = post.len() - post_match;
++ let last_camel = str_utils::camel_case_until(&pre[..last]);
++ pre = &pre[..last_camel.byte_index];
+ } else {
+ break;
+ }
+ }
+
- let post_camel = camel_case::from(post);
- post = &post[post_camel..];
++ let post_match = count_match_end(post, &name);
++ let post_end = post.len() - post_match.byte_count;
+ post = &post[post_end..];
- if item.vis.node.is_pub() {
- let matching = partial_match(mod_camel, &item_camel);
- let rmatching = partial_rmatch(mod_camel, &item_camel);
++ let post_camel = str_utils::camel_case_start(post);
++ post = &post[post_camel.byte_index..];
+ }
+ let (what, value) = match (pre.is_empty(), post.is_empty()) {
+ (true, true) => return,
+ (false, _) => ("pre", pre),
+ (true, false) => ("post", post),
+ };
+ span_lint_and_help(
+ cx,
+ ENUM_VARIANT_NAMES,
+ span,
+ &format!("all variants have the same {}fix: `{}`", what, value),
+ None,
+ &format!(
+ "remove the {}fixes and use full paths to \
+ the variants instead of glob imports",
+ what
+ ),
+ );
+}
+
+#[must_use]
+fn to_camel_case(item_name: &str) -> String {
+ let mut s = String::new();
+ let mut up = true;
+ for c in item_name.chars() {
+ if c.is_uppercase() {
+ // we only turn snake case text into CamelCase
+ return item_name.to_string();
+ }
+ if c == '_' {
+ up = true;
+ continue;
+ }
+ if up {
+ up = false;
+ s.extend(c.to_uppercase());
+ } else {
+ s.push(c);
+ }
+ }
+ s
+}
+
+impl LateLintPass<'_> for EnumVariantNames {
+ fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) {
+ let last = self.modules.pop();
+ assert!(last.is_some());
+ }
+
+ #[allow(clippy::similar_names)]
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+ let item_name = item.ident.name.as_str();
+ let item_name_chars = item_name.chars().count();
+ let item_camel = to_camel_case(&item_name);
+ if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
+ if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() {
+ // constants don't have surrounding modules
+ if !mod_camel.is_empty() {
+ if mod_name == &item.ident.name {
+ if let ItemKind::Mod(..) = item.kind {
+ span_lint(
+ cx,
+ MODULE_INCEPTION,
+ item.span,
+ "module has the same name as its containing module",
+ );
+ }
+ }
- if matching == nchars {
++ // The `module_name_repetitions` lint should only trigger if the item has the module in its
++ // name. Having the same name is accepted.
++ if item.vis.node.is_pub() && item_camel.len() > mod_camel.len() {
++ let matching = count_match_start(mod_camel, &item_camel);
++ let rmatching = count_match_end(mod_camel, &item_camel);
+ let nchars = mod_camel.chars().count();
+
+ let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
+
- if rmatching == nchars {
++ if matching.char_count == nchars {
+ match item_camel.chars().nth(nchars) {
+ Some(c) if is_word_beginning(c) => span_lint(
+ cx,
+ MODULE_NAME_REPETITIONS,
+ item.span,
+ "item name starts with its containing module's name",
+ ),
+ _ => (),
+ }
+ }
++ if rmatching.char_count == nchars {
+ span_lint(
+ cx,
+ MODULE_NAME_REPETITIONS,
+ item.span,
+ "item name ends with its containing module's name",
+ );
+ }
+ }
+ }
+ }
+ }
+ if let ItemKind::Enum(ref def, _) = item.kind {
+ if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.def_id)) {
+ check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span);
+ }
+ }
+ self.modules.push((item.ident.name, item_camel));
+ }
+}
--- /dev/null
- [Adjustment {
- kind: Adjust::Deref(None),
- ..
- }, Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
- ..
- }] => {
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher::VecArgs;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::usage::UsedAfterExprVisitor;
+use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for closures which just call another function where
+ /// the function can be called directly. `unsafe` functions or calls where types
+ /// get adjusted are ignored.
+ ///
+ /// ### Why is this bad?
+ /// Needlessly creating a closure adds code for no benefit
+ /// and gives the optimizer more work.
+ ///
+ /// ### Known problems
+ /// If creating the closure inside the closure has a side-
+ /// effect then moving the closure creation out will change when that side-
+ /// effect runs.
+ /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// // Bad
+ /// xs.map(|x| foo(x))
+ ///
+ /// // Good
+ /// xs.map(foo)
+ /// ```
+ /// where `foo(_)` is a plain function that takes the exact argument type of
+ /// `x`.
+ pub REDUNDANT_CLOSURE,
+ style,
+ "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for closures which only invoke a method on the closure
+ /// argument and can be replaced by referencing the method directly.
+ ///
+ /// ### Why is this bad?
+ /// It's unnecessary to create the closure.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// Some('a').map(|s| s.to_uppercase());
+ /// ```
+ /// may be rewritten as
+ /// ```rust,ignore
+ /// Some('a').map(char::to_uppercase);
+ /// ```
+ pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+ pedantic,
+ "redundant closures for method calls"
+}
+
+declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
+
+impl<'tcx> LateLintPass<'tcx> for EtaReduction {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+ let body = match expr.kind {
+ ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id),
+ _ => return,
+ };
+ if body.value.span.from_expansion() {
+ if body.params.is_empty() {
+ if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
+ // replace `|| vec![]` with `Vec::new`
+ span_lint_and_sugg(
+ cx,
+ REDUNDANT_CLOSURE,
+ expr.span,
+ "redundant closure",
+ "replace the closure with `Vec::new`",
+ "std::vec::Vec::new".into(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ // skip `foo(|| macro!())`
+ return;
+ }
+
+ let closure_ty = cx.typeck_results().expr_ty(expr);
+
+ if_chain!(
+ if let ExprKind::Call(callee, args) = body.value.kind;
+ if let ExprKind::Path(_) = callee.kind;
+ if check_inputs(cx, body.params, args);
+ let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
+ let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
+ .map_or(callee_ty, |id| cx.tcx.type_of(id));
+ if check_sig(cx, closure_ty, call_ty);
+ let substs = cx.typeck_results().node_substs(callee.hir_id);
+ // This fixes some false positives that I don't entirely understand
+ if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
+ // A type param function ref like `T::f` is not 'static, however
+ // it is if cast like `T::f as fn()`. This seems like a rustc bug.
+ if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
+ then {
+ span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
+ if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+ if_chain! {
+ if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
+ if substs.as_closure().kind() == ClosureKind::FnMut;
+ if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
+ || UsedAfterExprVisitor::is_found(cx, callee);
+
+ then {
+ // Mutable closure is used after current expr; we cannot consume it.
+ snippet = format!("&mut {}", snippet);
+ }
+ }
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the function itself",
+ snippet,
+ Applicability::MachineApplicable,
+ );
+ }
+ });
+ }
+ );
+
+ if_chain!(
+ if let ExprKind::MethodCall(path, _, args, _) = body.value.kind;
+ if check_inputs(cx, body.params, args);
+ let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
+ let substs = cx.typeck_results().node_substs(body.value.hir_id);
+ let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
+ if check_sig(cx, closure_ty, call_ty);
+ then {
+ span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
+ let name = get_ufcs_type_name(cx, method_def_id);
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the method itself",
+ format!("{}::{}", name, path.ident.name),
+ Applicability::MachineApplicable,
+ );
+ })
+ }
+ );
+ }
+}
+
+fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
+ if params.len() != call_args.len() {
+ return false;
+ }
+ std::iter::zip(params, call_args).all(|(param, arg)| {
+ match param.pat.kind {
+ PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
+ _ => return false,
+ }
+ match *cx.typeck_results().expr_adjustments(arg) {
+ [] => true,
++ [
++ Adjustment {
++ kind: Adjust::Deref(None),
++ ..
++ },
++ Adjustment {
++ kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
++ ..
++ },
++ ] => {
+ // re-borrow with the same mutability is allowed
+ let ty = cx.typeck_results().expr_ty(arg);
+ matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
+ },
+ _ => false,
+ }
+ })
+}
+
+fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
+ let call_sig = call_ty.fn_sig(cx.tcx);
+ if call_sig.unsafety() == Unsafety::Unsafe {
+ return false;
+ }
+ if !closure_ty.has_late_bound_regions() {
+ return true;
+ }
+ let substs = match closure_ty.kind() {
+ ty::Closure(_, substs) => substs,
+ _ => return false,
+ };
+ let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
+ cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
+}
+
+fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
+ match cx.tcx.associated_item(method_def_id).container {
+ ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id),
+ ty::ImplContainer(def_id) => {
+ let ty = cx.tcx.type_of(def_id);
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did),
+ _ => ty.to_string(),
+ }
+ },
+ }
+}
--- /dev/null
- if_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);
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::FormatExpn;
+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::{Expr, ExprKind};
+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 format_args.format_string_parts.is_empty() {
++ span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability);
++ } else {
++ if_chain! {
++ if let [e] = &*format_args.format_string_parts;
++ if let ExprKind::Lit(lit) = &e.kind;
++ if let Some(s_src) = snippet_opt(cx, lit.span);
++ then {
++ // Simulate macro expansion, converting {{ and }} to { and }.
++ let s_expand = s_src.replace("{{", "{").replace("}}", "}");
++ let sugg = format!("{}.to_string()", s_expand);
++ span_useless_format(cx, call_site, sugg, applicability);
++ }
+ }
+ }
+ } else if let [value] = *format_args.value_args {
+ if_chain! {
+ if format_args.format_string_symbols == [kw::Empty];
+ if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did),
+ ty::Str => true,
+ _ => false,
+ };
+ if let Some(args) = format_args.args();
+ if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting());
+ 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_empty(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
++ span_lint_and_sugg(
++ cx,
++ USELESS_FORMAT,
++ span,
++ "useless use of `format!`",
++ "consider using `String::new()`",
++ sugg,
++ applicability,
++ );
++}
++
+fn span_useless_format(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
+ span_lint_and_sugg(
+ cx,
+ USELESS_FORMAT,
+ span,
+ "useless use of `format!`",
+ "consider using `.to_string()`",
+ sugg,
+ applicability,
+ );
+}
--- /dev/null
- use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp};
- use rustc_lint::{EarlyContext, EarlyLintPass};
- use rustc_middle::lint::in_external_macro;
+//! lint on if branches that could be swapped so no `!` operation is necessary
+//! on the condition
+
+use clippy_utils::diagnostics::span_lint_and_help;
- impl EarlyLintPass for IfNotElse {
- fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
- if in_external_macro(cx.sess, item.span) {
++use clippy_utils::is_else_clause;
++use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `!` or `!=` in an if condition with an
+ /// else branch.
+ ///
+ /// ### Why is this bad?
+ /// Negations reduce the readability of statements.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let v: Vec<usize> = vec![];
+ /// # fn a() {}
+ /// # fn b() {}
+ /// if !v.is_empty() {
+ /// a()
+ /// } else {
+ /// b()
+ /// }
+ /// ```
+ ///
+ /// Could be written:
+ ///
+ /// ```rust
+ /// # let v: Vec<usize> = vec![];
+ /// # fn a() {}
+ /// # fn b() {}
+ /// if v.is_empty() {
+ /// b()
+ /// } else {
+ /// a()
+ /// }
+ /// ```
+ pub IF_NOT_ELSE,
+ pedantic,
+ "`if` branches that could be swapped so no negation operation is necessary on the condition"
+}
+
+declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
+
- if let ExprKind::If(ref cond, _, Some(ref els)) = item.kind {
++impl LateLintPass<'_> for IfNotElse {
++ fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
++ // While loops will be desugared to ExprKind::If. This will cause the lint to fire.
++ // To fix this, return early if this span comes from a macro or desugaring.
++ if item.span.from_expansion() {
+ return;
+ }
- match cond.kind {
++ if let ExprKind::If(cond, _, Some(els)) = item.kind {
+ if let ExprKind::Block(..) = els.kind {
++ // Disable firing the lint in "else if" expressions.
++ if is_else_clause(cx.tcx, item) {
++ return;
++ }
++
++ match cond.peel_drop_temps().kind {
+ ExprKind::Unary(UnOp::Not, _) => {
+ span_lint_and_help(
+ cx,
+ IF_NOT_ELSE,
+ item.span,
+ "unnecessary boolean `not` operation",
+ None,
+ "remove the `!` and swap the blocks of the `if`/`else`",
+ );
+ },
+ ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => {
+ span_lint_and_help(
+ cx,
+ IF_NOT_ELSE,
+ item.span,
+ "unnecessary `!=` operation",
+ None,
+ "change to `==` and swap the blocks of the `if`/`else`",
+ );
+ },
+ _ => (),
+ }
+ }
+ }
+ }
+}
--- /dev/null
- }
+//! lint on blocks unnecessarily using >= with a + 1 or - 1
+
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
+ ///
+ /// ### Why is this bad?
+ /// Readability -- better to use `> y` instead of `>= y + 1`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = 1;
+ /// # let y = 1;
+ /// if x >= y + 1 {}
+ /// ```
+ ///
+ /// Could be written as:
+ ///
+ /// ```rust
+ /// # let x = 1;
+ /// # let y = 1;
+ /// if x > y {}
+ /// ```
+ pub INT_PLUS_ONE,
+ complexity,
+ "instead of using `x >= y + 1`, use `x > y`"
+}
+
+declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]);
+
+// cases:
+// BinOpKind::Ge
+// x >= y + 1
+// x - 1 >= y
+//
+// BinOpKind::Le
+// x + 1 <= y
+// x <= y - 1
+
+#[derive(Copy, Clone)]
+enum Side {
+ Lhs,
+ Rhs,
+}
+
+impl IntPlusOne {
+ #[allow(clippy::cast_sign_loss)]
+ fn check_lit(lit: &Lit, target_value: i128) -> bool {
+ if let LitKind::Int(value, ..) = lit.kind {
+ return value == (target_value as u128);
+ }
+ false
+ }
+
+ fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
+ match (binop, &lhs.kind, &rhs.kind) {
+ // case where `x - 1 >= ...` or `-1 + x >= ...`
+ (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
+ match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
+ // `-1 + x`
+ (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
+ Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
+ },
+ // `x - 1`
+ (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+ Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
+ },
+ _ => None,
+ }
+ },
+ // case where `... >= y + 1` or `... >= 1 + y`
+ (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs))
+ if rhskind.node == BinOpKind::Add =>
+ {
+ match (&rhslhs.kind, &rhsrhs.kind) {
+ // `y + 1` and `1 + y`
+ (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
+ Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
+ },
+ (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+ Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
+ },
+ _ => None,
+ }
- }
++ },
+ // case where `x + 1 <= ...` or `1 + x <= ...`
+ (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _)
+ if lhskind.node == BinOpKind::Add =>
+ {
+ match (&lhslhs.kind, &lhsrhs.kind) {
+ // `1 + x` and `x + 1`
+ (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
+ Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
+ },
+ (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+ Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
+ },
+ _ => None,
+ }
++ },
+ // case where `... >= y - 1` or `... >= -1 + y`
+ (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
+ match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
+ // `-1 + y`
+ (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
+ Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
+ },
+ // `y - 1`
+ (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+ Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
+ },
+ _ => None,
+ }
+ },
+ _ => None,
+ }
+ }
+
+ fn generate_recommendation(
+ cx: &EarlyContext<'_>,
+ binop: BinOpKind,
+ node: &Expr,
+ other_side: &Expr,
+ side: Side,
+ ) -> Option<String> {
+ let binop_string = match binop {
+ BinOpKind::Ge => ">",
+ BinOpKind::Le => "<",
+ _ => return None,
+ };
+ if let Some(snippet) = snippet_opt(cx, node.span) {
+ if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
+ let rec = match side {
+ Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
+ Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
+ };
+ return rec;
+ }
+ }
+ None
+ }
+
+ fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
+ span_lint_and_sugg(
+ cx,
+ INT_PLUS_ONE,
+ block.span,
+ "unnecessary `>= y + 1` or `x - 1 >=`",
+ "change it to",
+ recommendation,
+ Applicability::MachineApplicable, // snippet
+ );
+ }
+}
+
+impl EarlyLintPass for IntPlusOne {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
+ if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
+ if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
+ Self::emit_warning(cx, item, rec);
+ }
+ }
+ }
+}
--- /dev/null
- use std::cmp::Ordering;
-
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::{self, IntTy, UintTy};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
++use clippy_utils::comparisons;
+use clippy_utils::comparisons::Rel;
- use clippy_utils::consts::{constant, Constant};
++use clippy_utils::consts::{constant_full_int, FullInt};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::snippet;
- use clippy_utils::{comparisons, sext};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for comparisons where the relation is always either
+ /// true or false, but where one side has been upcast so that the comparison is
+ /// necessary. Only integer types are checked.
+ ///
+ /// ### Why is this bad?
+ /// An expression like `let x : u8 = ...; (x as u32) > 300`
+ /// will mistakenly imply that it is possible for `x` to be outside the range of
+ /// `u8`.
+ ///
+ /// ### Known problems
+ /// https://github.com/rust-lang/rust-clippy/issues/886
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x: u8 = 1;
+ /// (x as u32) > 300;
+ /// ```
+ pub INVALID_UPCAST_COMPARISONS,
+ pedantic,
+ "a comparison involving an upcast which is always true or false"
+}
+
+declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
+
- #[derive(Copy, Clone, Debug, Eq)]
- enum FullInt {
- S(i128),
- U(u128),
- }
-
- impl FullInt {
- #[allow(clippy::cast_sign_loss)]
- #[must_use]
- fn cmp_s_u(s: i128, u: u128) -> Ordering {
- if s < 0 {
- Ordering::Less
- } else if u > (i128::MAX as u128) {
- Ordering::Greater
- } else {
- (s as u128).cmp(&u)
- }
- }
- }
-
- impl PartialEq for FullInt {
- #[must_use]
- fn eq(&self, other: &Self) -> bool {
- self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal
- }
- }
-
- impl PartialOrd for FullInt {
- #[must_use]
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(match (self, other) {
- (&Self::S(s), &Self::S(o)) => s.cmp(&o),
- (&Self::U(s), &Self::U(o)) => s.cmp(&o),
- (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o),
- (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(),
- })
- }
- }
-
- impl Ord for FullInt {
- #[must_use]
- fn cmp(&self, other: &Self) -> Ordering {
- self.partial_cmp(other)
- .expect("`partial_cmp` for FullInt can never return `None`")
- }
- }
-
+fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
+ if let ExprKind::Cast(cast_exp, _) = expr.kind {
+ let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp);
+ let cast_ty = cx.typeck_results().expr_ty(expr);
+ // if it's a cast from i32 to u32 wrapping will invalidate all these checks
+ if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
+ return None;
+ }
+ match pre_cast_ty.kind() {
+ ty::Int(int_ty) => Some(match int_ty {
+ IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
+ IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
+ IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))),
+ IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))),
+ IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)),
+ IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)),
+ }),
+ ty::Uint(uint_ty) => Some(match uint_ty {
+ UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))),
+ UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))),
+ UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))),
+ UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))),
+ UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)),
+ UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)),
+ }),
+ _ => None,
+ }
+ } else {
+ None
+ }
+}
+
- fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<FullInt> {
- let val = constant(cx, cx.typeck_results(), expr)?.0;
- if let Constant::Int(const_int) = val {
- match *cx.typeck_results().expr_ty(expr).kind() {
- ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
- ty::Uint(_) => Some(FullInt::U(const_int)),
- _ => None,
- }
- } else {
- None
- }
- }
-
+fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
+ if let ExprKind::Cast(cast_val, _) = expr.kind {
+ span_lint(
+ cx,
+ INVALID_UPCAST_COMPARISONS,
+ span,
+ &format!(
+ "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
+ snippet(cx, cast_val.span, "the expression"),
+ if always { "true" } else { "false" },
+ ),
+ );
+ }
+}
+
+fn upcast_comparison_bounds_err<'tcx>(
+ cx: &LateContext<'tcx>,
+ span: Span,
+ rel: comparisons::Rel,
+ lhs_bounds: Option<(FullInt, FullInt)>,
+ lhs: &'tcx Expr<'_>,
+ rhs: &'tcx Expr<'_>,
+ invert: bool,
+) {
+ if let Some((lb, ub)) = lhs_bounds {
- if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) {
++ if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) {
+ if rel == Rel::Eq || rel == Rel::Ne {
+ if norm_rhs_val < lb || norm_rhs_val > ub {
+ err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
+ }
+ } else if match rel {
+ Rel::Lt => {
+ if invert {
+ norm_rhs_val < lb
+ } else {
+ ub < norm_rhs_val
+ }
+ },
+ Rel::Le => {
+ if invert {
+ norm_rhs_val <= lb
+ } else {
+ ub <= norm_rhs_val
+ }
+ },
+ Rel::Eq | Rel::Ne => unreachable!(),
+ } {
+ err_upcast_comparison(cx, span, lhs, true);
+ } else if match rel {
+ Rel::Lt => {
+ if invert {
+ norm_rhs_val >= ub
+ } else {
+ lb >= norm_rhs_val
+ }
+ },
+ Rel::Le => {
+ if invert {
+ norm_rhs_val > ub
+ } else {
+ lb > norm_rhs_val
+ }
+ },
+ Rel::Eq | Rel::Ne => unreachable!(),
+ } {
+ err_upcast_comparison(cx, span, lhs, false);
+ }
+ }
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
+ let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
+ let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
+ val
+ } else {
+ return;
+ };
+
+ let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
+ let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
+
+ upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
+ upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
+ }
+ }
+}
--- /dev/null
- LintId::of(if_then_panic::IF_THEN_PANIC),
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_group(true, "clippy::all", Some("clippy_all"), vec![
+ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
+ LintId::of(approx_const::APPROX_CONSTANT),
+ LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
+ LintId::of(assign_ops::ASSIGN_OP_PATTERN),
+ LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
+ LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
+ LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
+ LintId::of(attrs::DEPRECATED_CFG_ATTR),
+ LintId::of(attrs::DEPRECATED_SEMVER),
+ LintId::of(attrs::MISMATCHED_TARGET_OS),
+ LintId::of(attrs::USELESS_ATTRIBUTE),
+ LintId::of(bit_mask::BAD_BIT_MASK),
+ LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
+ LintId::of(blacklisted_name::BLACKLISTED_NAME),
+ LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
+ LintId::of(booleans::LOGIC_BUG),
+ LintId::of(booleans::NONMINIMAL_BOOL),
+ LintId::of(casts::CAST_REF_TO_MUT),
+ LintId::of(casts::CHAR_LIT_AS_U8),
+ LintId::of(casts::FN_TO_NUMERIC_CAST),
+ LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
+ LintId::of(casts::UNNECESSARY_CAST),
+ LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
+ LintId::of(collapsible_if::COLLAPSIBLE_IF),
+ LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
+ LintId::of(comparison_chain::COMPARISON_CHAIN),
+ LintId::of(copies::IFS_SAME_COND),
+ LintId::of(copies::IF_SAME_THEN_ELSE),
+ LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+ LintId::of(derivable_impls::DERIVABLE_IMPLS),
+ LintId::of(derive::DERIVE_HASH_XOR_EQ),
+ LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
+ LintId::of(doc::MISSING_SAFETY_DOC),
+ LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
+ LintId::of(double_comparison::DOUBLE_COMPARISONS),
+ LintId::of(double_parens::DOUBLE_PARENS),
+ LintId::of(drop_forget_ref::DROP_COPY),
+ LintId::of(drop_forget_ref::DROP_REF),
+ LintId::of(drop_forget_ref::FORGET_COPY),
+ LintId::of(drop_forget_ref::FORGET_REF),
+ LintId::of(duration_subsec::DURATION_SUBSEC),
+ LintId::of(entry::MAP_ENTRY),
+ LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
+ LintId::of(enum_variants::ENUM_VARIANT_NAMES),
+ LintId::of(enum_variants::MODULE_INCEPTION),
+ LintId::of(eq_op::EQ_OP),
+ LintId::of(eq_op::OP_REF),
+ LintId::of(erasing_op::ERASING_OP),
+ LintId::of(escape::BOXED_LOCAL),
+ LintId::of(eta_reduction::REDUNDANT_CLOSURE),
+ LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
+ LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
+ LintId::of(explicit_write::EXPLICIT_WRITE),
+ LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
+ LintId::of(float_literal::EXCESSIVE_PRECISION),
+ LintId::of(format::USELESS_FORMAT),
+ LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
+ LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
+ 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(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(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
+ 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_replace::MEM_REPLACE_OPTION_WITH_NONE),
+ LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
+ LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
+ LintId::of(methods::BIND_INSTEAD_OF_MAP),
+ LintId::of(methods::BYTES_NTH),
+ LintId::of(methods::CHARS_LAST_CMP),
+ LintId::of(methods::CHARS_NEXT_CMP),
+ LintId::of(methods::CLONE_DOUBLE_REF),
+ LintId::of(methods::CLONE_ON_COPY),
+ LintId::of(methods::EXPECT_FUN_CALL),
+ LintId::of(methods::EXTEND_WITH_DRAIN),
+ LintId::of(methods::FILTER_MAP_IDENTITY),
+ LintId::of(methods::FILTER_NEXT),
+ LintId::of(methods::FLAT_MAP_IDENTITY),
+ LintId::of(methods::INSPECT_FOR_EACH),
+ LintId::of(methods::INTO_ITER_ON_REF),
+ LintId::of(methods::ITERATOR_STEP_BY_ZERO),
+ LintId::of(methods::ITER_CLONED_COLLECT),
+ LintId::of(methods::ITER_COUNT),
+ LintId::of(methods::ITER_NEXT_SLICE),
+ LintId::of(methods::ITER_NTH),
+ LintId::of(methods::ITER_NTH_ZERO),
+ LintId::of(methods::ITER_SKIP_NEXT),
+ LintId::of(methods::MANUAL_FILTER_MAP),
+ LintId::of(methods::MANUAL_FIND_MAP),
+ LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
+ LintId::of(methods::MANUAL_SPLIT_ONCE),
+ LintId::of(methods::MANUAL_STR_REPEAT),
+ LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+ LintId::of(methods::MAP_IDENTITY),
+ LintId::of(methods::NEW_RET_NO_SELF),
+ LintId::of(methods::OK_EXPECT),
+ LintId::of(methods::OPTION_AS_REF_DEREF),
+ LintId::of(methods::OPTION_FILTER_MAP),
+ LintId::of(methods::OPTION_MAP_OR_NONE),
+ LintId::of(methods::OR_FUN_CALL),
+ LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
+ LintId::of(methods::SEARCH_IS_SOME),
+ LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
+ LintId::of(methods::SINGLE_CHAR_ADD_STR),
+ LintId::of(methods::SINGLE_CHAR_PATTERN),
+ LintId::of(methods::SKIP_WHILE_NEXT),
+ LintId::of(methods::STRING_EXTEND_CHARS),
+ LintId::of(methods::SUSPICIOUS_MAP),
+ LintId::of(methods::SUSPICIOUS_SPLITN),
+ LintId::of(methods::UNINIT_ASSUMED_INIT),
+ LintId::of(methods::UNNECESSARY_FILTER_MAP),
+ LintId::of(methods::UNNECESSARY_FOLD),
+ LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
+ LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
+ LintId::of(methods::USELESS_ASREF),
+ LintId::of(methods::WRONG_SELF_CONVENTION),
+ LintId::of(methods::ZST_OFFSET),
+ LintId::of(minmax::MIN_MAX),
+ LintId::of(misc::CMP_NAN),
+ LintId::of(misc::CMP_OWNED),
+ LintId::of(misc::MODULO_ONE),
+ LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
+ LintId::of(misc::TOPLEVEL_REF_ARG),
+ LintId::of(misc::ZERO_PTR),
+ LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
+ LintId::of(misc_early::DOUBLE_NEG),
+ LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
+ LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
+ LintId::of(misc_early::REDUNDANT_PATTERN),
+ LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
+ LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
+ LintId::of(mut_key::MUTABLE_KEY_TYPE),
+ LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
+ LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
+ LintId::of(mutex_atomic::MUTEX_ATOMIC),
+ LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
+ LintId::of(needless_bool::BOOL_COMPARISON),
+ LintId::of(needless_bool::NEEDLESS_BOOL),
+ LintId::of(needless_borrow::NEEDLESS_BORROW),
+ LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+ LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
+ LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
+ LintId::of(needless_update::NEEDLESS_UPDATE),
+ LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
+ LintId::of(neg_multiply::NEG_MULTIPLY),
+ LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
+ LintId::of(no_effect::NO_EFFECT),
+ LintId::of(no_effect::UNNECESSARY_OPERATION),
+ LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
+ LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
+ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
+ LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
++ LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+ 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_NUM_TO_BYTES),
+ LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
+ LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
+ LintId::of(transmute::WRONG_TRANSMUTE),
+ LintId::of(transmuting_null::TRANSMUTING_NULL),
+ LintId::of(try_err::TRY_ERR),
+ LintId::of(types::BORROWED_BOX),
+ LintId::of(types::BOX_COLLECTION),
+ LintId::of(types::REDUNDANT_ALLOCATION),
+ LintId::of(types::TYPE_COMPLEXITY),
+ LintId::of(types::VEC_BOX),
+ LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
+ LintId::of(unicode::INVISIBLE_CHARACTERS),
+ LintId::of(uninit_vec::UNINIT_VEC),
++ LintId::of(unit_hash::UNIT_HASH),
+ LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+ LintId::of(unit_types::UNIT_ARG),
+ LintId::of(unit_types::UNIT_CMP),
+ LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
+ LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
+ LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
+ LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
+ LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
+ LintId::of(unused_unit::UNUSED_UNIT),
+ LintId::of(unwrap::PANICKING_UNWRAP),
+ LintId::of(unwrap::UNNECESSARY_UNWRAP),
+ LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
+ LintId::of(useless_conversion::USELESS_CONVERSION),
+ LintId::of(vec::USELESS_VEC),
+ LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
+ LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
+ LintId::of(write::PRINTLN_EMPTY_STRING),
+ LintId::of(write::PRINT_LITERAL),
+ LintId::of(write::PRINT_WITH_NEWLINE),
+ LintId::of(write::WRITELN_EMPTY_STRING),
+ LintId::of(write::WRITE_LITERAL),
+ LintId::of(write::WRITE_WITH_NEWLINE),
+ LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
+])
--- /dev/null
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
+ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
+ LintId::of(approx_const::APPROX_CONSTANT),
+ LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
+ LintId::of(attrs::DEPRECATED_SEMVER),
+ LintId::of(attrs::MISMATCHED_TARGET_OS),
+ LintId::of(attrs::USELESS_ATTRIBUTE),
+ LintId::of(bit_mask::BAD_BIT_MASK),
+ LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
+ LintId::of(booleans::LOGIC_BUG),
+ LintId::of(casts::CAST_REF_TO_MUT),
+ LintId::of(copies::IFS_SAME_COND),
+ LintId::of(copies::IF_SAME_THEN_ELSE),
+ LintId::of(derive::DERIVE_HASH_XOR_EQ),
+ LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
+ LintId::of(drop_forget_ref::DROP_COPY),
+ LintId::of(drop_forget_ref::DROP_REF),
+ LintId::of(drop_forget_ref::FORGET_COPY),
+ LintId::of(drop_forget_ref::FORGET_REF),
+ LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
+ LintId::of(eq_op::EQ_OP),
+ LintId::of(erasing_op::ERASING_OP),
+ LintId::of(formatting::POSSIBLE_MISSING_COMMA),
+ LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
+ LintId::of(if_let_mutex::IF_LET_MUTEX),
+ LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
+ LintId::of(infinite_iter::INFINITE_ITER),
+ LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
+ LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
+ LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
+ LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
+ LintId::of(loops::ITER_NEXT_LOOP),
+ LintId::of(loops::NEVER_LOOP),
+ LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
+ LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
+ LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
+ LintId::of(methods::CLONE_DOUBLE_REF),
+ LintId::of(methods::ITERATOR_STEP_BY_ZERO),
+ LintId::of(methods::SUSPICIOUS_SPLITN),
+ LintId::of(methods::UNINIT_ASSUMED_INIT),
+ LintId::of(methods::ZST_OFFSET),
+ LintId::of(minmax::MIN_MAX),
+ LintId::of(misc::CMP_NAN),
+ LintId::of(misc::MODULO_ONE),
+ LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
+ LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+ LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
+ LintId::of(ptr::INVALID_NULL_PTR_USAGE),
+ LintId::of(ptr::MUT_FROM_REF),
+ LintId::of(ranges::REVERSED_EMPTY_RANGES),
+ LintId::of(regex::INVALID_REGEX),
+ LintId::of(self_assignment::SELF_ASSIGNMENT),
+ LintId::of(serde_api::SERDE_API_MISUSE),
+ LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
+ LintId::of(swap::ALMOST_SWAPPED),
+ LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
+ LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
+ LintId::of(transmute::WRONG_TRANSMUTE),
+ LintId::of(transmuting_null::TRANSMUTING_NULL),
+ LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
+ LintId::of(unicode::INVISIBLE_CHARACTERS),
+ LintId::of(uninit_vec::UNINIT_VEC),
++ LintId::of(unit_hash::UNIT_HASH),
+ LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+ LintId::of(unit_types::UNIT_CMP),
+ LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
+ LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
+ LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
+ LintId::of(unwrap::PANICKING_UNWRAP),
+ LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
+])
--- /dev/null
- if_then_panic::IF_THEN_PANIC,
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_lints(&[
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::CLIPPY_LINTS_INTERNAL,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::COMPILER_LINT_FUNCTIONS,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::DEFAULT_LINT,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::IF_CHAIN_STYLE,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::INTERNING_DEFINED_SYMBOL,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::INVALID_PATHS,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::LINT_WITHOUT_LINT_PASS,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::OUTER_EXPN_EXPN_DATA,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::PRODUCE_ICE,
+ #[cfg(feature = "internal-lints")]
+ utils::internal_lints::UNNECESSARY_SYMBOL_STR,
+ absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
+ approx_const::APPROX_CONSTANT,
+ arithmetic::FLOAT_ARITHMETIC,
+ arithmetic::INTEGER_ARITHMETIC,
+ as_conversions::AS_CONVERSIONS,
+ asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
+ asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
+ assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
+ assign_ops::ASSIGN_OP_PATTERN,
+ assign_ops::MISREFACTORED_ASSIGN_OP,
+ async_yields_async::ASYNC_YIELDS_ASYNC,
+ attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
+ attrs::DEPRECATED_CFG_ATTR,
+ attrs::DEPRECATED_SEMVER,
+ attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
+ attrs::INLINE_ALWAYS,
+ attrs::MISMATCHED_TARGET_OS,
+ attrs::USELESS_ATTRIBUTE,
+ await_holding_invalid::AWAIT_HOLDING_LOCK,
+ await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
+ bit_mask::BAD_BIT_MASK,
+ bit_mask::INEFFECTIVE_BIT_MASK,
+ bit_mask::VERBOSE_BIT_MASK,
+ blacklisted_name::BLACKLISTED_NAME,
+ blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
+ bool_assert_comparison::BOOL_ASSERT_COMPARISON,
+ booleans::LOGIC_BUG,
+ booleans::NONMINIMAL_BOOL,
+ bytecount::NAIVE_BYTECOUNT,
+ cargo_common_metadata::CARGO_COMMON_METADATA,
+ case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ casts::CAST_LOSSLESS,
+ casts::CAST_POSSIBLE_TRUNCATION,
+ casts::CAST_POSSIBLE_WRAP,
+ casts::CAST_PRECISION_LOSS,
+ casts::CAST_PTR_ALIGNMENT,
+ casts::CAST_REF_TO_MUT,
+ casts::CAST_SIGN_LOSS,
+ casts::CHAR_LIT_AS_U8,
+ casts::FN_TO_NUMERIC_CAST,
+ casts::FN_TO_NUMERIC_CAST_ANY,
+ casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
+ casts::PTR_AS_PTR,
+ casts::UNNECESSARY_CAST,
+ checked_conversions::CHECKED_CONVERSIONS,
+ cognitive_complexity::COGNITIVE_COMPLEXITY,
+ collapsible_if::COLLAPSIBLE_ELSE_IF,
+ collapsible_if::COLLAPSIBLE_IF,
+ collapsible_match::COLLAPSIBLE_MATCH,
+ comparison_chain::COMPARISON_CHAIN,
+ copies::BRANCHES_SHARING_CODE,
+ copies::IFS_SAME_COND,
+ copies::IF_SAME_THEN_ELSE,
+ copies::SAME_FUNCTIONS_IN_IF_CONDITION,
+ copy_iterator::COPY_ITERATOR,
+ create_dir::CREATE_DIR,
+ dbg_macro::DBG_MACRO,
+ default::DEFAULT_TRAIT_ACCESS,
+ default::FIELD_REASSIGN_WITH_DEFAULT,
+ default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
+ dereference::EXPLICIT_DEREF_METHODS,
+ derivable_impls::DERIVABLE_IMPLS,
+ derive::DERIVE_HASH_XOR_EQ,
+ derive::DERIVE_ORD_XOR_PARTIAL_ORD,
+ derive::EXPL_IMPL_CLONE_ON_COPY,
+ derive::UNSAFE_DERIVE_DESERIALIZE,
+ disallowed_method::DISALLOWED_METHOD,
+ disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
+ disallowed_type::DISALLOWED_TYPE,
+ doc::DOC_MARKDOWN,
+ doc::MISSING_ERRORS_DOC,
+ doc::MISSING_PANICS_DOC,
+ doc::MISSING_SAFETY_DOC,
+ doc::NEEDLESS_DOCTEST_MAIN,
+ double_comparison::DOUBLE_COMPARISONS,
+ double_parens::DOUBLE_PARENS,
+ drop_forget_ref::DROP_COPY,
+ drop_forget_ref::DROP_REF,
+ drop_forget_ref::FORGET_COPY,
+ drop_forget_ref::FORGET_REF,
+ duration_subsec::DURATION_SUBSEC,
+ else_if_without_else::ELSE_IF_WITHOUT_ELSE,
+ empty_enum::EMPTY_ENUM,
+ entry::MAP_ENTRY,
+ enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
+ enum_variants::ENUM_VARIANT_NAMES,
+ enum_variants::MODULE_INCEPTION,
+ enum_variants::MODULE_NAME_REPETITIONS,
+ eq_op::EQ_OP,
+ eq_op::OP_REF,
+ equatable_if_let::EQUATABLE_IF_LET,
+ erasing_op::ERASING_OP,
+ escape::BOXED_LOCAL,
+ eta_reduction::REDUNDANT_CLOSURE,
+ eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+ eval_order_dependence::DIVERGING_SUB_EXPRESSION,
+ eval_order_dependence::EVAL_ORDER_DEPENDENCE,
+ excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
+ excessive_bools::STRUCT_EXCESSIVE_BOOLS,
+ exhaustive_items::EXHAUSTIVE_ENUMS,
+ exhaustive_items::EXHAUSTIVE_STRUCTS,
+ exit::EXIT,
+ explicit_write::EXPLICIT_WRITE,
+ fallible_impl_from::FALLIBLE_IMPL_FROM,
+ feature_name::NEGATIVE_FEATURE_NAMES,
+ feature_name::REDUNDANT_FEATURE_NAMES,
+ float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
+ float_literal::EXCESSIVE_PRECISION,
+ float_literal::LOSSY_FLOAT_LITERAL,
+ floating_point_arithmetic::IMPRECISE_FLOPS,
+ floating_point_arithmetic::SUBOPTIMAL_FLOPS,
+ format::USELESS_FORMAT,
+ format_args::FORMAT_IN_FORMAT_ARGS,
+ format_args::TO_STRING_IN_FORMAT_ARGS,
+ formatting::POSSIBLE_MISSING_COMMA,
+ formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
+ formatting::SUSPICIOUS_ELSE_FORMATTING,
+ formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
+ from_over_into::FROM_OVER_INTO,
+ from_str_radix_10::FROM_STR_RADIX_10,
+ functions::DOUBLE_MUST_USE,
+ functions::MUST_USE_CANDIDATE,
+ functions::MUST_USE_UNIT,
+ functions::NOT_UNSAFE_PTR_ARG_DEREF,
+ functions::RESULT_UNIT_ERR,
+ functions::TOO_MANY_ARGUMENTS,
+ functions::TOO_MANY_LINES,
+ future_not_send::FUTURE_NOT_SEND,
+ get_last_with_len::GET_LAST_WITH_LEN,
+ identity_op::IDENTITY_OP,
+ if_let_mutex::IF_LET_MUTEX,
+ if_not_else::IF_NOT_ELSE,
+ if_then_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_assert::MANUAL_ASSERT,
+ 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,
+ match_str_case_mismatch::MATCH_STR_CASE_MISMATCH,
+ 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_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::SEPARATED_LITERAL_SUFFIX,
+ 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::NO_EFFECT_UNDERSCORE_BINDING,
+ no_effect::UNNECESSARY_OPERATION,
+ non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
+ non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
+ non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
+ non_expressive_names::MANY_SINGLE_CHAR_NAMES,
+ non_expressive_names::SIMILAR_NAMES,
+ non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
+ non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
+ nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
+ 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_SLICE,
+ 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,
+ trailing_empty_array::TRAILING_EMPTY_ARRAY,
+ trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
+ trait_bounds::TYPE_REPETITION_IN_BOUNDS,
+ transmute::CROSSPOINTER_TRANSMUTE,
+ transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
+ transmute::TRANSMUTE_BYTES_TO_STR,
+ transmute::TRANSMUTE_FLOAT_TO_INT,
+ transmute::TRANSMUTE_INT_TO_BOOL,
+ transmute::TRANSMUTE_INT_TO_CHAR,
+ transmute::TRANSMUTE_INT_TO_FLOAT,
+ transmute::TRANSMUTE_NUM_TO_BYTES,
+ transmute::TRANSMUTE_PTR_TO_PTR,
+ transmute::TRANSMUTE_PTR_TO_REF,
+ transmute::UNSOUND_COLLECTION_TRANSMUTE,
+ transmute::USELESS_TRANSMUTE,
+ transmute::WRONG_TRANSMUTE,
+ transmuting_null::TRANSMUTING_NULL,
+ try_err::TRY_ERR,
+ types::BORROWED_BOX,
+ types::BOX_COLLECTION,
+ types::LINKEDLIST,
+ types::OPTION_OPTION,
+ types::RC_BUFFER,
+ types::RC_MUTEX,
+ types::REDUNDANT_ALLOCATION,
+ types::TYPE_COMPLEXITY,
+ types::VEC_BOX,
+ undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
+ undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
+ unicode::INVISIBLE_CHARACTERS,
+ unicode::NON_ASCII_LITERAL,
+ unicode::UNICODE_NOT_NFC,
+ uninit_vec::UNINIT_VEC,
++ unit_hash::UNIT_HASH,
+ unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
+ unit_types::LET_UNIT_VALUE,
+ unit_types::UNIT_ARG,
+ unit_types::UNIT_CMP,
+ unnamed_address::FN_ADDRESS_COMPARISONS,
+ unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+ unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
+ unnecessary_sort_by::UNNECESSARY_SORT_BY,
+ unnecessary_wraps::UNNECESSARY_WRAPS,
+ unnested_or_patterns::UNNESTED_OR_PATTERNS,
+ unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
+ unused_async::UNUSED_ASYNC,
+ unused_io_amount::UNUSED_IO_AMOUNT,
+ unused_self::UNUSED_SELF,
+ unused_unit::UNUSED_UNIT,
+ unwrap::PANICKING_UNWRAP,
+ unwrap::UNNECESSARY_UNWRAP,
+ unwrap_in_result::UNWRAP_IN_RESULT,
+ upper_case_acronyms::UPPER_CASE_ACRONYMS,
+ use_self::USE_SELF,
+ useless_conversion::USELESS_CONVERSION,
+ vec::USELESS_VEC,
+ vec_init_then_push::VEC_INIT_THEN_PUSH,
+ vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
+ verbose_file_reads::VERBOSE_FILE_READS,
+ wildcard_dependencies::WILDCARD_DEPENDENCIES,
+ wildcard_imports::ENUM_GLOB_USE,
+ wildcard_imports::WILDCARD_IMPORTS,
+ write::PRINTLN_EMPTY_STRING,
+ write::PRINT_LITERAL,
+ write::PRINT_STDERR,
+ write::PRINT_STDOUT,
+ write::PRINT_WITH_NEWLINE,
+ write::USE_DEBUG,
+ write::WRITELN_EMPTY_STRING,
+ write::WRITE_LITERAL,
+ write::WRITE_WITH_NEWLINE,
+ zero_div_zero::ZERO_DIVIDED_BY_ZERO,
+ zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
+])
--- /dev/null
- LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
+ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
+ LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
+ LintId::of(copies::BRANCHES_SHARING_CODE),
+ LintId::of(disallowed_method::DISALLOWED_METHOD),
+ LintId::of(disallowed_type::DISALLOWED_TYPE),
+ LintId::of(equatable_if_let::EQUATABLE_IF_LET),
+ LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
+ LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
+ LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
+ LintId::of(future_not_send::FUTURE_NOT_SEND),
+ LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
+ LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
+ LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
+ LintId::of(mutex_atomic::MUTEX_INTEGER),
+ LintId::of(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(trailing_empty_array::TRAILING_EMPTY_ARRAY),
+ LintId::of(transmute::USELESS_TRANSMUTE),
+ LintId::of(use_self::USE_SELF),
+])
--- /dev/null
- LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
+ LintId::of(attrs::INLINE_ALWAYS),
+ LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
+ LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
+ LintId::of(bit_mask::VERBOSE_BIT_MASK),
+ LintId::of(bytecount::NAIVE_BYTECOUNT),
+ LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
+ LintId::of(casts::CAST_LOSSLESS),
+ LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
+ LintId::of(casts::CAST_POSSIBLE_WRAP),
+ LintId::of(casts::CAST_PRECISION_LOSS),
+ LintId::of(casts::CAST_PTR_ALIGNMENT),
+ LintId::of(casts::CAST_SIGN_LOSS),
+ LintId::of(casts::PTR_AS_PTR),
+ LintId::of(checked_conversions::CHECKED_CONVERSIONS),
+ LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
+ LintId::of(copy_iterator::COPY_ITERATOR),
+ LintId::of(default::DEFAULT_TRAIT_ACCESS),
+ LintId::of(dereference::EXPLICIT_DEREF_METHODS),
+ LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
+ LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
+ LintId::of(doc::DOC_MARKDOWN),
+ LintId::of(doc::MISSING_ERRORS_DOC),
+ LintId::of(doc::MISSING_PANICS_DOC),
+ LintId::of(empty_enum::EMPTY_ENUM),
+ LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
+ LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
+ LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
+ LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
+ LintId::of(functions::MUST_USE_CANDIDATE),
+ LintId::of(functions::TOO_MANY_LINES),
+ LintId::of(if_not_else::IF_NOT_ELSE),
+ LintId::of(implicit_hasher::IMPLICIT_HASHER),
+ LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
+ LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
+ LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
+ LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
+ LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
+ LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
+ LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
+ LintId::of(let_underscore::LET_UNDERSCORE_DROP),
+ LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
+ LintId::of(literal_representation::UNREADABLE_LITERAL),
+ LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
+ LintId::of(loops::EXPLICIT_ITER_LOOP),
+ LintId::of(macro_use::MACRO_USE_IMPORTS),
++ LintId::of(manual_assert::MANUAL_ASSERT),
+ 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(unicode::NON_ASCII_LITERAL),
+ 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(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
+ LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
+ LintId::of(non_expressive_names::SIMILAR_NAMES),
+ LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
+ LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
+ LintId::of(ranges::RANGE_MINUS_ONE),
+ LintId::of(ranges::RANGE_PLUS_ONE),
+ LintId::of(redundant_else::REDUNDANT_ELSE),
+ LintId::of(ref_option_ref::REF_OPTION_REF),
+ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
+ LintId::of(strings::STRING_ADD_ASSIGN),
+ LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
+ LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
+ LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
+ LintId::of(types::LINKEDLIST),
+ LintId::of(types::OPTION_OPTION),
+ LintId::of(unicode::UNICODE_NOT_NFC),
+ LintId::of(unit_types::LET_UNIT_VALUE),
+ LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
+ LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
+ LintId::of(unused_async::UNUSED_ASYNC),
+ LintId::of(unused_self::UNUSED_SELF),
+ LintId::of(wildcard_imports::ENUM_GLOB_USE),
+ LintId::of(wildcard_imports::WILDCARD_IMPORTS),
+ LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
+])
--- /dev/null
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
+ LintId::of(arithmetic::FLOAT_ARITHMETIC),
+ LintId::of(arithmetic::INTEGER_ARITHMETIC),
+ LintId::of(as_conversions::AS_CONVERSIONS),
+ LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
+ LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
+ LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
+ LintId::of(create_dir::CREATE_DIR),
+ LintId::of(dbg_macro::DBG_MACRO),
+ LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
+ LintId::of(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::SEPARATED_LITERAL_SUFFIX),
+ LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
++ LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
+ LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
+ LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
+ LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
+ LintId::of(module_style::MOD_MODULE_FILES),
+ LintId::of(module_style::SELF_NAMED_MODULE_FILES),
+ LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
+ LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
+ LintId::of(panic_unimplemented::PANIC),
+ LintId::of(panic_unimplemented::TODO),
+ LintId::of(panic_unimplemented::UNIMPLEMENTED),
+ LintId::of(panic_unimplemented::UNREACHABLE),
+ LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
+ LintId::of(same_name_method::SAME_NAME_METHOD),
+ LintId::of(shadow::SHADOW_REUSE),
+ LintId::of(shadow::SHADOW_SAME),
+ LintId::of(shadow::SHADOW_UNRELATED),
+ LintId::of(strings::STRING_ADD),
++ LintId::of(strings::STRING_SLICE),
+ LintId::of(strings::STRING_TO_STRING),
+ LintId::of(strings::STR_TO_STRING),
+ LintId::of(types::RC_BUFFER),
+ LintId::of(types::RC_MUTEX),
+ LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
++ LintId::of(unicode::NON_ASCII_LITERAL),
+ LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
+ LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
+ LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
+ LintId::of(write::PRINT_STDERR),
+ LintId::of(write::PRINT_STDOUT),
+ LintId::of(write::USE_DEBUG),
+])
--- /dev/null
- LintId::of(if_then_panic::IF_THEN_PANIC),
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_group(true, "clippy::style", Some("clippy_style"), vec![
+ LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
+ LintId::of(assign_ops::ASSIGN_OP_PATTERN),
+ LintId::of(blacklisted_name::BLACKLISTED_NAME),
+ LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
+ LintId::of(casts::FN_TO_NUMERIC_CAST),
+ LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
+ LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
+ LintId::of(collapsible_if::COLLAPSIBLE_IF),
+ LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
+ LintId::of(comparison_chain::COMPARISON_CHAIN),
+ LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+ LintId::of(doc::MISSING_SAFETY_DOC),
+ LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
+ LintId::of(enum_variants::ENUM_VARIANT_NAMES),
+ LintId::of(enum_variants::MODULE_INCEPTION),
+ LintId::of(eq_op::OP_REF),
+ LintId::of(eta_reduction::REDUNDANT_CLOSURE),
+ LintId::of(float_literal::EXCESSIVE_PRECISION),
+ LintId::of(from_over_into::FROM_OVER_INTO),
+ LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
+ LintId::of(functions::DOUBLE_MUST_USE),
+ LintId::of(functions::MUST_USE_UNIT),
+ LintId::of(functions::RESULT_UNIT_ERR),
+ LintId::of(inherent_to_string::INHERENT_TO_STRING),
+ LintId::of(len_zero::COMPARISON_TO_EMPTY),
+ LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
+ LintId::of(len_zero::LEN_ZERO),
+ LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
+ LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
+ LintId::of(loops::FOR_KV_MAP),
+ LintId::of(loops::NEEDLESS_RANGE_LOOP),
+ LintId::of(loops::SAME_ITEM_PUSH),
+ LintId::of(loops::WHILE_LET_ON_ITERATOR),
+ LintId::of(main_recursion::MAIN_RECURSION),
+ LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
+ LintId::of(manual_map::MANUAL_MAP),
+ LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
+ LintId::of(map_clone::MAP_CLONE),
+ LintId::of(match_result_ok::MATCH_RESULT_OK),
+ LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
+ LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
+ LintId::of(matches::MATCH_OVERLAPPING_ARM),
+ LintId::of(matches::MATCH_REF_PATS),
+ LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
+ LintId::of(matches::SINGLE_MATCH),
+ LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
+ LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
+ LintId::of(methods::BYTES_NTH),
+ LintId::of(methods::CHARS_LAST_CMP),
+ LintId::of(methods::CHARS_NEXT_CMP),
+ LintId::of(methods::INTO_ITER_ON_REF),
+ LintId::of(methods::ITER_CLONED_COLLECT),
+ LintId::of(methods::ITER_NEXT_SLICE),
+ LintId::of(methods::ITER_NTH_ZERO),
+ LintId::of(methods::ITER_SKIP_NEXT),
+ LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
+ LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+ LintId::of(methods::NEW_RET_NO_SELF),
+ LintId::of(methods::OK_EXPECT),
+ LintId::of(methods::OPTION_MAP_OR_NONE),
+ LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
+ LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
+ LintId::of(methods::SINGLE_CHAR_ADD_STR),
+ LintId::of(methods::STRING_EXTEND_CHARS),
+ LintId::of(methods::UNNECESSARY_FOLD),
+ LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
+ LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
+ LintId::of(methods::WRONG_SELF_CONVENTION),
+ LintId::of(misc::TOPLEVEL_REF_ARG),
+ LintId::of(misc::ZERO_PTR),
+ LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
+ LintId::of(misc_early::DOUBLE_NEG),
+ LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
+ LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
+ LintId::of(misc_early::REDUNDANT_PATTERN),
+ LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
+ LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
+ LintId::of(needless_borrow::NEEDLESS_BORROW),
+ LintId::of(neg_multiply::NEG_MULTIPLY),
+ LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
+ LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
+ LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
+ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
+ LintId::of(ptr::CMP_NULL),
+ LintId::of(ptr::PTR_ARG),
+ LintId::of(ptr_eq::PTR_EQ),
+ LintId::of(question_mark::QUESTION_MARK),
+ LintId::of(ranges::MANUAL_RANGE_CONTAINS),
+ LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
+ LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
+ LintId::of(returns::LET_AND_RETURN),
+ LintId::of(returns::NEEDLESS_RETURN),
+ LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
+ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
+ LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
+ LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
+ LintId::of(try_err::TRY_ERR),
+ LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
+ LintId::of(unused_unit::UNUSED_UNIT),
+ LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
+ LintId::of(write::PRINTLN_EMPTY_STRING),
+ LintId::of(write::PRINT_LITERAL),
+ LintId::of(write::PRINT_WITH_NEWLINE),
+ LintId::of(write::WRITELN_EMPTY_STRING),
+ LintId::of(write::WRITE_LITERAL),
+ LintId::of(write::WRITE_WITH_NEWLINE),
+])
--- /dev/null
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
+ LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
+ LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
+ LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
+ LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
+ LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
+ LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
+ LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
+ LintId::of(loops::EMPTY_LOOP),
+ LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
+ LintId::of(loops::MUT_RANGE_BOUND),
+ LintId::of(methods::SUSPICIOUS_MAP),
+ LintId::of(mut_key::MUTABLE_KEY_TYPE),
++ LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+ LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
+ LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
+])
--- /dev/null
- mod if_then_panic;
+// error-pattern:cargo-clippy
+
+#![feature(box_patterns)]
+#![feature(drain_filter)]
+#![feature(in_band_lifetimes)]
+#![feature(iter_zip)]
+#![feature(once_cell)]
+#![feature(rustc_private)]
+#![feature(stmt_expr_attributes)]
+#![feature(control_flow_enum)]
+#![recursion_limit = "512"]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
+#![warn(trivial_casts, trivial_numeric_casts)]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![warn(rustc::internal)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate rustc_ast;
+extern crate rustc_ast_pretty;
+extern crate rustc_data_structures;
+extern crate rustc_driver;
+extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_hir_pretty;
+extern crate rustc_index;
+extern crate rustc_infer;
+extern crate rustc_lexer;
+extern crate rustc_lint;
+extern crate rustc_middle;
+extern crate rustc_mir_dataflow;
+extern crate rustc_parse;
+extern crate rustc_parse_format;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_target;
+extern crate rustc_trait_selection;
+extern crate rustc_typeck;
+
+#[macro_use]
+extern crate clippy_utils;
+
+use clippy_utils::parse_msrv;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_lint::LintId;
+use rustc_session::Session;
+
+/// Macro used to declare a Clippy lint.
+///
+/// Every lint declaration consists of 4 parts:
+///
+/// 1. The documentation, which is used for the website
+/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
+/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
+/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
+/// 4. The `description` that contains a short explanation on what's wrong with code where the
+/// lint is triggered.
+///
+/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
+/// enabled by default. As said in the README.md of this repository, if the lint level mapping
+/// changes, please update README.md.
+///
+/// # Example
+///
+/// ```
+/// #![feature(rustc_private)]
+/// extern crate rustc_session;
+/// use rustc_session::declare_tool_lint;
+/// use clippy_lints::declare_clippy_lint;
+///
+/// declare_clippy_lint! {
+/// /// ### What it does
+/// /// Checks for ... (describe what the lint matches).
+/// ///
+/// /// ### Why is this bad?
+/// /// Supply the reason for linting the code.
+/// ///
+/// /// ### Example
+/// /// ```rust
+/// /// // Bad
+/// /// Insert a short example of code that triggers the lint
+/// ///
+/// /// // Good
+/// /// Insert a short example of improved code that doesn't trigger the lint
+/// /// ```
+/// pub LINT_NAME,
+/// pedantic,
+/// "description"
+/// }
+/// ```
+/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
+#[macro_export]
+macro_rules! declare_clippy_lint {
+ { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
+ }
+ };
+ { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
+ declare_tool_lint! {
+ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
+ }
+ };
+}
+
+#[cfg(feature = "metadata-collector-lint")]
+mod deprecated_lints;
+mod utils;
+
+// begin lints modules, do not remove this comment, it’s used in `update_lints`
+mod absurd_extreme_comparisons;
+mod approx_const;
+mod arithmetic;
+mod as_conversions;
+mod asm_syntax;
+mod assertions_on_constants;
+mod assign_ops;
+mod async_yields_async;
+mod attrs;
+mod await_holding_invalid;
+mod bit_mask;
+mod blacklisted_name;
+mod blocks_in_if_conditions;
+mod bool_assert_comparison;
+mod booleans;
+mod bytecount;
+mod cargo_common_metadata;
+mod case_sensitive_file_extension_comparisons;
+mod casts;
+mod checked_conversions;
+mod cognitive_complexity;
+mod collapsible_if;
+mod collapsible_match;
+mod comparison_chain;
+mod copies;
+mod copy_iterator;
+mod create_dir;
+mod dbg_macro;
+mod default;
+mod default_numeric_fallback;
+mod dereference;
+mod derivable_impls;
+mod derive;
+mod disallowed_method;
+mod disallowed_script_idents;
+mod disallowed_type;
+mod doc;
+mod double_comparison;
+mod double_parens;
+mod drop_forget_ref;
+mod duration_subsec;
+mod else_if_without_else;
+mod empty_enum;
+mod entry;
+mod enum_clike;
+mod enum_variants;
+mod eq_op;
+mod equatable_if_let;
+mod erasing_op;
+mod escape;
+mod eta_reduction;
+mod eval_order_dependence;
+mod excessive_bools;
+mod exhaustive_items;
+mod exit;
+mod explicit_write;
+mod fallible_impl_from;
+mod feature_name;
+mod float_equality_without_abs;
+mod float_literal;
+mod floating_point_arithmetic;
+mod format;
+mod format_args;
+mod formatting;
+mod from_over_into;
+mod from_str_radix_10;
+mod functions;
+mod future_not_send;
+mod get_last_with_len;
+mod identity_op;
+mod if_let_mutex;
+mod if_not_else;
- store.register_early_pass(|| Box::new(if_not_else::IfNotElse));
+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_assert;
+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 match_str_case_mismatch;
+mod matches;
+mod mem_forget;
+mod mem_replace;
+mod methods;
+mod minmax;
+mod misc;
+mod misc_early;
+mod missing_const_for_fn;
+mod missing_doc;
+mod missing_enforced_import_rename;
+mod missing_inline;
+mod module_style;
+mod modulo_arithmetic;
+mod multiple_crate_versions;
+mod mut_key;
+mod mut_mut;
+mod mut_mutex_lock;
+mod mut_reference;
+mod mutable_debug_assertion;
+mod mutex_atomic;
+mod needless_arbitrary_self_type;
+mod needless_bitwise_bool;
+mod needless_bool;
+mod needless_borrow;
+mod needless_borrowed_ref;
+mod needless_continue;
+mod needless_for_each;
+mod needless_option_as_deref;
+mod needless_pass_by_value;
+mod needless_question_mark;
+mod needless_update;
+mod neg_cmp_op_on_partial_ord;
+mod neg_multiply;
+mod new_without_default;
+mod no_effect;
+mod non_copy_const;
+mod non_expressive_names;
+mod non_octal_unix_permissions;
+mod non_send_fields_in_send_ty;
+mod nonstandard_macro_braces;
+mod open_options;
+mod option_env_unwrap;
+mod option_if_let_else;
+mod overflow_check_conditional;
+mod panic_in_result_fn;
+mod panic_unimplemented;
+mod partialeq_ne_impl;
+mod pass_by_ref_or_value;
+mod path_buf_push_overwrite;
+mod pattern_type_mismatch;
+mod precedence;
+mod ptr;
+mod ptr_eq;
+mod ptr_offset_with_cast;
+mod question_mark;
+mod ranges;
+mod redundant_clone;
+mod redundant_closure_call;
+mod redundant_else;
+mod redundant_field_names;
+mod redundant_pub_crate;
+mod redundant_slicing;
+mod redundant_static_lifetimes;
+mod ref_option_ref;
+mod reference;
+mod regex;
+mod repeat_once;
+mod returns;
+mod same_name_method;
+mod self_assignment;
+mod self_named_constructors;
+mod semicolon_if_nothing_returned;
+mod serde_api;
+mod shadow;
+mod single_component_path_imports;
+mod size_of_in_element_count;
+mod slow_vector_initialization;
+mod stable_sort_primitive;
+mod strings;
+mod strlen_on_c_strings;
+mod suspicious_operation_groupings;
+mod suspicious_trait_impl;
+mod swap;
+mod tabs_in_doc_comments;
+mod temporary_assignment;
+mod to_digit_is_some;
+mod to_string_in_display;
+mod trailing_empty_array;
+mod trait_bounds;
+mod transmute;
+mod transmuting_null;
+mod try_err;
+mod types;
+mod undocumented_unsafe_blocks;
+mod undropped_manually_drops;
+mod unicode;
+mod uninit_vec;
++mod unit_hash;
+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);
+
+ include!("lib.deprecated.rs");
+
+ include!("lib.register_lints.rs");
+ include!("lib.register_restriction.rs");
+ include!("lib.register_pedantic.rs");
+
+ #[cfg(feature = "internal-lints")]
+ include!("lib.register_internal.rs");
+
+ include!("lib.register_all.rs");
+ include!("lib.register_style.rs");
+ include!("lib.register_complexity.rs");
+ include!("lib.register_correctness.rs");
+ include!("lib.register_suspicious.rs");
+ include!("lib.register_perf.rs");
+ include!("lib.register_cargo.rs");
+ include!("lib.register_nursery.rs");
+
+ #[cfg(feature = "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(uninit_vec::UninitVec));
++ store.register_late_pass(|| Box::new(unit_hash::UnitHash));
+ store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
+ store.register_late_pass(|| Box::new(strings::StringAdd));
+ store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
+ store.register_late_pass(|| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
+ store.register_late_pass(|| Box::new(default_numeric_fallback::DefaultNumericFallback));
+ store.register_late_pass(|| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
+ store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
+ store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
+
+ let msrv = conf.msrv.as_ref().and_then(|s| {
+ parse_msrv(s, None, None).or_else(|| {
+ sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
+ None
+ })
+ });
+
+ let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
+ store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
+ store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
+ store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
+ store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
+ store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
+ store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
+ store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
+ store.register_late_pass(move || Box::new(checked_conversions::CheckedConversions::new(msrv)));
+ store.register_late_pass(move || Box::new(mem_replace::MemReplace::new(msrv)));
+ store.register_late_pass(move || Box::new(ranges::Ranges::new(msrv)));
+ store.register_late_pass(move || Box::new(from_over_into::FromOverInto::new(msrv)));
+ store.register_late_pass(move || Box::new(use_self::UseSelf::new(msrv)));
+ store.register_late_pass(move || Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
+ store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
+ store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
+ store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
+
+ store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
+ store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
+ store.register_late_pass(|| Box::new(map_clone::MapClone));
+ store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
+ store.register_late_pass(|| Box::new(shadow::Shadow::default()));
+ store.register_late_pass(|| Box::new(unit_types::UnitTypes));
+ store.register_late_pass(|| Box::new(loops::Loops));
+ store.register_late_pass(|| Box::new(main_recursion::MainRecursion::default()));
+ store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
+ store.register_late_pass(|| Box::new(entry::HashMapPass));
+ store.register_late_pass(|| Box::new(minmax::MinMaxPass));
+ store.register_late_pass(|| Box::new(open_options::OpenOptions));
+ store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
+ store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
+ store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
+ store.register_late_pass(|| Box::new(needless_borrow::NeedlessBorrow::default()));
+ store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
+ store.register_late_pass(|| Box::new(no_effect::NoEffect));
+ store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
+ store.register_late_pass(|| Box::new(transmute::Transmute));
+ let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
+ store.register_late_pass(move || Box::new(cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)));
+ let too_large_for_stack = conf.too_large_for_stack;
+ store.register_late_pass(move || Box::new(escape::BoxedLocal{too_large_for_stack}));
+ store.register_late_pass(move || Box::new(vec::UselessVec{too_large_for_stack}));
+ store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
+ store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
+ store.register_late_pass(|| Box::new(derive::Derive));
+ store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
+ store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen));
+ store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
+ store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
+ store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
+ store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
+ store.register_late_pass(|| Box::new(regex::Regex));
+ store.register_late_pass(|| Box::new(copies::CopyAndPaste));
+ store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
+ store.register_late_pass(|| Box::new(format::UselessFormat));
+ store.register_late_pass(|| Box::new(swap::Swap));
+ store.register_late_pass(|| Box::new(overflow_check_conditional::OverflowCheckConditional));
+ store.register_late_pass(|| Box::new(new_without_default::NewWithoutDefault::default()));
+ let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
+ let too_many_arguments_threshold = conf.too_many_arguments_threshold;
+ let too_many_lines_threshold = conf.too_many_lines_threshold;
+ store.register_late_pass(move || Box::new(functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)));
+ let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
+ store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
+ store.register_late_pass(|| Box::new(mem_forget::MemForget));
+ store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default()));
+ store.register_late_pass(|| Box::new(assign_ops::AssignOps));
+ store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
+ store.register_late_pass(|| Box::new(eval_order_dependence::EvalOrderDependence));
+ store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
+ store.register_late_pass(|| Box::new(missing_inline::MissingInline));
+ store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
+ store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
+ store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
+ store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
+ let enum_variant_size_threshold = conf.enum_variant_size_threshold;
+ store.register_late_pass(move || Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
+ store.register_late_pass(|| Box::new(explicit_write::ExplicitWrite));
+ store.register_late_pass(|| Box::new(needless_pass_by_value::NeedlessPassByValue));
+ let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
+ conf.trivial_copy_size_limit,
+ conf.pass_by_value_size_limit,
+ conf.avoid_breaking_exported_api,
+ &sess.target,
+ );
+ store.register_late_pass(move || Box::new(pass_by_ref_or_value));
+ store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
+ store.register_late_pass(|| Box::new(try_err::TryErr));
+ store.register_late_pass(|| Box::new(bytecount::ByteCount));
+ store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
+ store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
+ store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
+ store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
+ store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
+ store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons));
+ store.register_late_pass(|| Box::new(question_mark::QuestionMark));
+ store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
+ store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
+ store.register_late_pass(|| Box::new(map_unit_fn::MapUnit));
+ store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
+ store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
+ store.register_late_pass(|| Box::new(unwrap::Unwrap));
+ store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec));
+ store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
+ store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
+ store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
+ store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
+ store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
+ store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
+ store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
+ store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
+ store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
+ store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
+ store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
+ store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
+ let max_trait_bounds = conf.max_trait_bounds;
+ store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
+ store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
+ store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
+ store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
+ store.register_early_pass(|| Box::new(reference::DerefAddrOf));
+ store.register_early_pass(|| Box::new(reference::RefInDeref));
+ store.register_early_pass(|| Box::new(double_parens::DoubleParens));
+ store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new()));
+ store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
- store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic));
+ 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(if_not_else::IfNotElse));
+ store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
+ store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
+ store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems));
+ store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
+ store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
+ store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
+ let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
+ store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames {
+ single_char_binding_names_threshold,
+ }));
+ let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
+ store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
+ store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
+ store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
+ store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
+ store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
+ store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
+ store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr));
+ store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
+ store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
+ store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
+ store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
+ let disallowed_methods = conf.disallowed_methods.clone();
+ store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
+ store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
+ store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
+ store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
+ store.register_late_pass(|| Box::new(strings::StrToString));
+ store.register_late_pass(|| Box::new(strings::StringToString));
+ store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
+ store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
+ store.register_late_pass(|| Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons));
+ store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
+ store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
+ store.register_late_pass(|| Box::new(manual_map::ManualMap));
+ store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
+ store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
+ store.register_early_pass(move || Box::new(module_style::ModStyle));
+ store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
+ let disallowed_types = conf.disallowed_types.clone();
+ store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(disallowed_types.clone())));
+ 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(manual_assert::ManualAssert));
+ let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
+ store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send)));
+ store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default()));
+ store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
+ store.register_late_pass(move || Box::new(format_args::FormatArgs));
+ store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
++ // add lints here, do not remove this comment, it's used in `new_lint`
+}
+
+#[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) {
++ // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs
+ 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");
+ ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums");
+}
+
+// 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
- TyKind::OpaqueDef(item, _) => {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{in_macro, trait_ref_of_method};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::intravisit::{
+ walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
+ NestedVisitorMap, Visitor,
+};
+use rustc_hir::FnRetTy::Return;
+use rustc_hir::{
+ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
+ ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
+ TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::symbol::{kw, Symbol};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for lifetime annotations which can be removed by
+ /// relying on lifetime elision.
+ ///
+ /// ### Why is this bad?
+ /// The additional lifetimes make the code look more
+ /// complicated, while there is nothing out of the ordinary going on. Removing
+ /// them leads to more readable code.
+ ///
+ /// ### Known problems
+ /// - We bail out if the function has a `where` clause where lifetimes
+ /// are mentioned due to potenial false positives.
+ /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
+ /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad: unnecessary lifetime annotations
+ /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
+ /// x
+ /// }
+ ///
+ /// // Good
+ /// fn elided(x: &u8, y: u8) -> &u8 {
+ /// x
+ /// }
+ /// ```
+ pub NEEDLESS_LIFETIMES,
+ complexity,
+ "using explicit lifetimes for references in function arguments when elision rules \
+ would allow omitting them"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for lifetimes in generics that are never used
+ /// anywhere else.
+ ///
+ /// ### Why is this bad?
+ /// The additional lifetimes make the code look more
+ /// complicated, while there is nothing out of the ordinary going on. Removing
+ /// them leads to more readable code.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad: unnecessary lifetimes
+ /// fn unused_lifetime<'a>(x: u8) {
+ /// // ..
+ /// }
+ ///
+ /// // Good
+ /// fn no_lifetime(x: u8) {
+ /// // ...
+ /// }
+ /// ```
+ pub EXTRA_UNUSED_LIFETIMES,
+ complexity,
+ "unused lifetimes in function definitions"
+}
+
+declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
+
+impl<'tcx> LateLintPass<'tcx> for Lifetimes {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
+ check_fn_inner(cx, sig.decl, Some(id), generics, item.span, true);
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
+ if let ImplItemKind::Fn(ref sig, id) = item.kind {
+ let report_extra_lifetimes = trait_ref_of_method(cx, item.hir_id()).is_none();
+ check_fn_inner(
+ cx,
+ sig.decl,
+ Some(id),
+ &item.generics,
+ item.span,
+ report_extra_lifetimes,
+ );
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
+ let body = match *body {
+ TraitFn::Required(_) => None,
+ TraitFn::Provided(id) => Some(id),
+ };
+ check_fn_inner(cx, sig.decl, body, &item.generics, item.span, true);
+ }
+ }
+}
+
+/// The lifetime of a &-reference.
+#[derive(PartialEq, Eq, Hash, Debug, Clone)]
+enum RefLt {
+ Unnamed,
+ Static,
+ Named(Symbol),
+}
+
+fn check_fn_inner<'tcx>(
+ cx: &LateContext<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ body: Option<BodyId>,
+ generics: &'tcx Generics<'_>,
+ span: Span,
+ report_extra_lifetimes: bool,
+) {
+ if in_macro(span) || has_where_lifetimes(cx, &generics.where_clause) {
+ return;
+ }
+
+ let types = generics
+ .params
+ .iter()
+ .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
+ for typ in types {
+ for bound in typ.bounds {
+ let mut visitor = RefVisitor::new(cx);
+ walk_param_bound(&mut visitor, bound);
+ if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
+ return;
+ }
+ if let GenericBound::Trait(ref trait_ref, _) = *bound {
+ let params = &trait_ref
+ .trait_ref
+ .path
+ .segments
+ .last()
+ .expect("a path must have at least one segment")
+ .args;
+ if let Some(params) = *params {
+ let lifetimes = params.args.iter().filter_map(|arg| match arg {
+ GenericArg::Lifetime(lt) => Some(lt),
+ _ => None,
+ });
+ for bound in lifetimes {
+ if bound.name != LifetimeName::Static && !bound.is_elided() {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ if could_use_elision(cx, decl, body, generics.params) {
+ span_lint(
+ cx,
+ NEEDLESS_LIFETIMES,
+ span.with_hi(decl.output.span().hi()),
+ "explicit lifetimes given in parameter types where they could be elided \
+ (or replaced with `'_` if needed by type declaration)",
+ );
+ }
+ if report_extra_lifetimes {
+ self::report_extra_lifetimes(cx, decl, generics);
+ }
+}
+
+fn could_use_elision<'tcx>(
+ cx: &LateContext<'tcx>,
+ func: &'tcx FnDecl<'_>,
+ body: Option<BodyId>,
+ named_generics: &'tcx [GenericParam<'_>],
+) -> bool {
+ // There are two scenarios where elision works:
+ // * no output references, all input references have different LT
+ // * output references, exactly one input reference with same LT
+ // All lifetimes must be unnamed, 'static or defined without bounds on the
+ // level of the current item.
+
+ // check named LTs
+ let allowed_lts = allowed_lts_from(named_generics);
+
+ // these will collect all the lifetimes for references in arg/return types
+ let mut input_visitor = RefVisitor::new(cx);
+ let mut output_visitor = RefVisitor::new(cx);
+
+ // extract lifetimes in input argument types
+ for arg in func.inputs {
+ input_visitor.visit_ty(arg);
+ }
+ // extract lifetimes in output type
+ if let Return(ty) = func.output {
+ output_visitor.visit_ty(ty);
+ }
+ for lt in named_generics {
+ input_visitor.visit_generic_param(lt);
+ }
+
+ if input_visitor.abort() || output_visitor.abort() {
+ return false;
+ }
+
+ if allowed_lts
+ .intersection(
+ &input_visitor
+ .nested_elision_site_lts
+ .iter()
+ .chain(output_visitor.nested_elision_site_lts.iter())
+ .cloned()
+ .filter(|v| matches!(v, RefLt::Named(_)))
+ .collect(),
+ )
+ .next()
+ .is_some()
+ {
+ return false;
+ }
+
+ let input_lts = input_visitor.lts;
+ let output_lts = output_visitor.lts;
+
+ if let Some(body_id) = body {
+ let mut checker = BodyLifetimeChecker {
+ lifetimes_used_in_body: false,
+ };
+ checker.visit_expr(&cx.tcx.hir().body(body_id).value);
+ if checker.lifetimes_used_in_body {
+ return false;
+ }
+ }
+
+ // check for lifetimes from higher scopes
+ for lt in input_lts.iter().chain(output_lts.iter()) {
+ if !allowed_lts.contains(lt) {
+ return false;
+ }
+ }
+
+ // no input lifetimes? easy case!
+ if input_lts.is_empty() {
+ false
+ } else if output_lts.is_empty() {
+ // no output lifetimes, check distinctness of input lifetimes
+
+ // only unnamed and static, ok
+ let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
+ if unnamed_and_static {
+ return false;
+ }
+ // we have no output reference, so we only need all distinct lifetimes
+ input_lts.len() == unique_lifetimes(&input_lts)
+ } else {
+ // we have output references, so we need one input reference,
+ // and all output lifetimes must be the same
+ if unique_lifetimes(&output_lts) > 1 {
+ return false;
+ }
+ if input_lts.len() == 1 {
+ match (&input_lts[0], &output_lts[0]) {
+ (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
+ (&RefLt::Named(_), &RefLt::Unnamed) => true,
+ _ => false, /* already elided, different named lifetimes
+ * or something static going on */
+ }
+ } else {
+ false
+ }
+ }
+}
+
+fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
+ let mut allowed_lts = FxHashSet::default();
+ for par in named_generics.iter() {
+ if let GenericParamKind::Lifetime { .. } = par.kind {
+ if par.bounds.is_empty() {
+ allowed_lts.insert(RefLt::Named(par.name.ident().name));
+ }
+ }
+ }
+ allowed_lts.insert(RefLt::Unnamed);
+ allowed_lts.insert(RefLt::Static);
+ allowed_lts
+}
+
+/// Number of unique lifetimes in the given vector.
+#[must_use]
+fn unique_lifetimes(lts: &[RefLt]) -> usize {
+ lts.iter().collect::<FxHashSet<_>>().len()
+}
+
+const CLOSURE_TRAIT_BOUNDS: [LangItem; 3] = [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce];
+
+/// A visitor usable for `rustc_front::visit::walk_ty()`.
+struct RefVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ lts: Vec<RefLt>,
+ nested_elision_site_lts: Vec<RefLt>,
+ unelided_trait_object_lifetime: bool,
+}
+
+impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
+ fn new(cx: &'a LateContext<'tcx>) -> Self {
+ Self {
+ cx,
+ lts: Vec::new(),
+ nested_elision_site_lts: Vec::new(),
+ unelided_trait_object_lifetime: false,
+ }
+ }
+
+ fn record(&mut self, lifetime: &Option<Lifetime>) {
+ if let Some(ref lt) = *lifetime {
+ if lt.name == LifetimeName::Static {
+ self.lts.push(RefLt::Static);
+ } else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name {
+ // Fresh lifetimes generated should be ignored.
+ } else if lt.is_elided() {
+ self.lts.push(RefLt::Unnamed);
+ } else {
+ self.lts.push(RefLt::Named(lt.name.ident().name));
+ }
+ } else {
+ self.lts.push(RefLt::Unnamed);
+ }
+ }
+
+ fn all_lts(&self) -> Vec<RefLt> {
+ self.lts
+ .iter()
+ .chain(self.nested_elision_site_lts.iter())
+ .cloned()
+ .collect::<Vec<_>>()
+ }
+
+ fn abort(&self) -> bool {
+ self.unelided_trait_object_lifetime
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ // for lifetimes as parameters of generics
+ fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
+ self.record(&Some(*lifetime));
+ }
+
+ fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) {
+ let trait_ref = &poly_tref.trait_ref;
+ if CLOSURE_TRAIT_BOUNDS.iter().any(|&item| {
+ self.cx
+ .tcx
+ .lang_items()
+ .require(item)
+ .map_or(false, |id| Some(id) == trait_ref.trait_def_id())
+ }) {
+ let mut sub_visitor = RefVisitor::new(self.cx);
+ sub_visitor.visit_trait_ref(trait_ref);
+ self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
+ } else {
+ walk_poly_trait_ref(self, poly_tref, tbm);
+ }
+ }
+
+ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
+ match ty.kind {
++ TyKind::OpaqueDef(item, bounds) => {
+ let map = self.cx.tcx.hir();
+ let item = map.item(item);
+ walk_item(self, item);
+ walk_ty(self, ty);
++ self.lts.extend(bounds.iter().filter_map(|bound| match bound {
++ GenericArg::Lifetime(l) => Some(RefLt::Named(l.name.ident().name)),
++ _ => None,
++ }));
+ },
+ TyKind::BareFn(&BareFnTy { decl, .. }) => {
+ let mut sub_visitor = RefVisitor::new(self.cx);
+ sub_visitor.visit_fn_decl(decl);
+ self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
+ return;
+ },
+ TyKind::TraitObject(bounds, ref lt, _) => {
+ if !lt.is_elided() {
+ self.unelided_trait_object_lifetime = true;
+ }
+ for bound in bounds {
+ self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
+ }
+ return;
+ },
+ _ => (),
+ }
+ walk_ty(self, ty);
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
+/// reason about elision.
+fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool {
+ for predicate in where_clause.predicates {
+ match *predicate {
+ WherePredicate::RegionPredicate(..) => return true,
+ WherePredicate::BoundPredicate(ref pred) => {
+ // a predicate like F: Trait or F: for<'a> Trait<'a>
+ let mut visitor = RefVisitor::new(cx);
+ // walk the type F, it may not contain LT refs
+ walk_ty(&mut visitor, pred.bounded_ty);
+ if !visitor.all_lts().is_empty() {
+ return true;
+ }
+ // if the bounds define new lifetimes, they are fine to occur
+ let allowed_lts = allowed_lts_from(pred.bound_generic_params);
+ // now walk the bounds
+ for bound in pred.bounds.iter() {
+ walk_param_bound(&mut visitor, bound);
+ }
+ // and check that all lifetimes are allowed
+ if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
+ return true;
+ }
+ },
+ WherePredicate::EqPredicate(ref pred) => {
+ let mut visitor = RefVisitor::new(cx);
+ walk_ty(&mut visitor, pred.lhs_ty);
+ walk_ty(&mut visitor, pred.rhs_ty);
+ if !visitor.lts.is_empty() {
+ return true;
+ }
+ },
+ }
+ }
+ false
+}
+
+struct LifetimeChecker {
+ map: FxHashMap<Symbol, Span>,
+}
+
+impl<'tcx> Visitor<'tcx> for LifetimeChecker {
+ type Map = Map<'tcx>;
+
+ // for lifetimes as parameters of generics
+ fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
+ self.map.remove(&lifetime.name.ident().name);
+ }
+
+ fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
+ // don't actually visit `<'a>` or `<'a: 'b>`
+ // we've already visited the `'a` declarations and
+ // don't want to spuriously remove them
+ // `'b` in `'a: 'b` is useless unless used elsewhere in
+ // a non-lifetime bound
+ if let GenericParamKind::Type { .. } = param.kind {
+ walk_generic_param(self, param);
+ }
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
+ let hs = generics
+ .params
+ .iter()
+ .filter_map(|par| match par.kind {
+ GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
+ _ => None,
+ })
+ .collect();
+ let mut checker = LifetimeChecker { map: hs };
+
+ walk_generics(&mut checker, generics);
+ walk_fn_decl(&mut checker, func);
+
+ for &v in checker.map.values() {
+ span_lint(
+ cx,
+ EXTRA_UNUSED_LIFETIMES,
+ v,
+ "this lifetime isn't used in the function definition",
+ );
+ }
+}
+
+struct BodyLifetimeChecker {
+ lifetimes_used_in_body: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
+ type Map = Map<'tcx>;
+
+ // for lifetimes as parameters of generics
+ fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
+ if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime {
+ self.lifetimes_used_in_body = true;
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
--- /dev/null
- }
+use clippy_utils::ty::{has_iter_method, implements_trait};
+use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
+use rustc_hir::HirIdMap;
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Stmt, StmtKind};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_span::source_map::Span;
+use rustc_span::symbol::{sym, Symbol};
+use std::iter::Iterator;
+
+#[derive(Debug, PartialEq)]
+enum IncrementVisitorVarState {
+ Initial, // Not examined yet
+ IncrOnce, // Incremented exactly once, may be a loop counter
+ DontWarn,
+}
+
+/// Scan a for loop for variables that are incremented exactly once and not used after that.
+pub(super) struct IncrementVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>, // context reference
+ states: HirIdMap<IncrementVisitorVarState>, // incremented variables
+ depth: u32, // depth of conditional expressions
+ done: bool,
+}
+
+impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> {
+ pub(super) fn new(cx: &'a LateContext<'tcx>) -> Self {
+ Self {
+ cx,
+ states: HirIdMap::default(),
+ depth: 0,
+ done: false,
+ }
+ }
+
+ pub(super) fn into_results(self) -> impl Iterator<Item = HirId> {
+ self.states.into_iter().filter_map(|(id, state)| {
+ if state == IncrementVisitorVarState::IncrOnce {
+ Some(id)
+ } else {
+ None
+ }
+ })
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.done {
+ return;
+ }
+
+ // If node is a variable
+ if let Some(def_id) = path_to_local(expr) {
+ if let Some(parent) = get_parent_expr(self.cx, expr) {
+ let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial);
+ if *state == IncrementVisitorVarState::IncrOnce {
+ *state = IncrementVisitorVarState::DontWarn;
+ return;
+ }
+
+ match parent.kind {
+ ExprKind::AssignOp(op, lhs, rhs) => {
+ if lhs.hir_id == expr.hir_id {
+ *state = if op.node == BinOpKind::Add
+ && is_integer_const(self.cx, rhs, 1)
+ && *state == IncrementVisitorVarState::Initial
+ && self.depth == 0
+ {
+ IncrementVisitorVarState::IncrOnce
+ } else {
+ // Assigned some other value or assigned multiple times
+ IncrementVisitorVarState::DontWarn
+ };
+ }
+ },
+ ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => {
+ *state = IncrementVisitorVarState::DontWarn;
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
+ *state = IncrementVisitorVarState::DontWarn;
+ },
+ _ => (),
+ }
+ }
+
+ walk_expr(self, expr);
+ } else if is_loop(expr) || is_conditional(expr) {
+ self.depth += 1;
+ walk_expr(self, expr);
+ self.depth -= 1;
+ } else if let ExprKind::Continue(_) = expr.kind {
+ self.done = true;
+ } else {
+ walk_expr(self, expr);
+ }
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+enum InitializeVisitorState<'hir> {
+ Initial, // Not examined yet
+ Declared(Symbol), // Declared but not (yet) initialized
+ Initialized {
+ name: Symbol,
+ initializer: &'hir Expr<'hir>,
+ },
+ DontWarn,
+}
+
+/// Checks whether a variable is initialized at the start of a loop and not modified
+/// and used after the loop.
+pub(super) struct InitializeVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>, // context reference
+ end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here.
+ var_id: HirId,
+ state: InitializeVisitorState<'tcx>,
+ depth: u32, // depth of conditional expressions
+ past_loop: bool,
+}
+
+impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> {
+ pub(super) fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self {
+ Self {
+ cx,
+ end_expr,
+ var_id,
+ state: InitializeVisitorState::Initial,
+ depth: 0,
+ past_loop: false,
+ }
+ }
+
+ pub(super) fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> {
+ if let InitializeVisitorState::Initialized { name, initializer } = self.state {
+ Some((name, initializer))
+ } else {
+ None
+ }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+ // Look for declarations of the variable
+ if_chain! {
+ if let StmtKind::Local(local) = stmt.kind;
+ if local.pat.hir_id == self.var_id;
+ if let PatKind::Binding(.., ident, _) = local.pat.kind;
+ then {
+ self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| {
+ InitializeVisitorState::Initialized {
+ initializer: init,
+ name: ident.name,
+ }
+ })
+ }
+ }
+ walk_stmt(self, stmt);
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if matches!(self.state, InitializeVisitorState::DontWarn) {
+ return;
+ }
+ if expr.hir_id == self.end_expr.hir_id {
+ self.past_loop = true;
+ return;
+ }
+ // No need to visit expressions before the variable is
+ // declared
+ if matches!(self.state, InitializeVisitorState::Initial) {
+ return;
+ }
+
+ // If node is the desired variable, see how it's used
+ if path_to_local_id(expr, self.var_id) {
+ if self.past_loop {
+ self.state = InitializeVisitorState::DontWarn;
+ return;
+ }
+
+ if let Some(parent) = get_parent_expr(self.cx, expr) {
+ match parent.kind {
+ ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == expr.hir_id => {
+ self.state = InitializeVisitorState::DontWarn;
+ },
+ ExprKind::Assign(lhs, rhs, _) if lhs.hir_id == expr.hir_id => {
+ self.state = if_chain! {
+ if self.depth == 0;
+ if let InitializeVisitorState::Declared(name)
+ | InitializeVisitorState::Initialized { name, ..} = self.state;
+ then {
+ InitializeVisitorState::Initialized { initializer: rhs, name }
+ } else {
+ InitializeVisitorState::DontWarn
+ }
+ }
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
+ self.state = InitializeVisitorState::DontWarn;
+ },
+ _ => (),
+ }
+ }
+
+ walk_expr(self, expr);
+ } else if !self.past_loop && is_loop(expr) {
+ self.state = InitializeVisitorState::DontWarn;
+ } else if is_conditional(expr) {
+ self.depth += 1;
+ walk_expr(self, expr);
+ self.depth -= 1;
+ } else {
+ walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+}
+
+fn is_loop(expr: &Expr<'_>) -> bool {
+ matches!(expr.kind, ExprKind::Loop(..))
+}
+
+fn is_conditional(expr: &Expr<'_>) -> bool {
+ matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..))
+}
+
+#[derive(PartialEq, Eq)]
+pub(super) enum Nesting {
+ Unknown, // no nesting detected yet
+ RuledOut, // the iterator is initialized or assigned within scope
+ LookFurther, // no nesting detected, no further walk required
+}
+
+use self::Nesting::{LookFurther, RuledOut, Unknown};
+
+pub(super) struct LoopNestVisitor {
+ pub(super) hir_id: HirId,
+ pub(super) iterator: HirId,
+ pub(super) nesting: Nesting,
+}
+
+impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
+ type Map = Map<'tcx>;
+
+ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+ if stmt.hir_id == self.hir_id {
+ self.nesting = LookFurther;
+ } else if self.nesting == Unknown {
+ walk_stmt(self, stmt);
+ }
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.nesting != Unknown {
+ return;
+ }
+ if expr.hir_id == self.hir_id {
+ self.nesting = LookFurther;
+ return;
+ }
+ match expr.kind {
+ ExprKind::Assign(path, _, _) | ExprKind::AssignOp(_, path, _) => {
+ if path_to_local_id(path, self.iterator) {
+ self.nesting = RuledOut;
+ }
+ },
+ _ => walk_expr(self, expr),
+ }
+ }
+
+ fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
+ if self.nesting != Unknown {
+ return;
+ }
+ if let PatKind::Binding(_, id, ..) = pat.kind {
+ if id == self.iterator {
+ self.nesting = RuledOut;
+ return;
+ }
+ }
+ walk_pat(self, pat);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+// this function assumes the given expression is a `for` loop.
+pub(super) fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span {
+ // for some reason this is the only way to get the `Span`
+ // of the entire `for` loop
+ if let ExprKind::Match(_, arms, _) = &expr.kind {
+ arms[0].body.span
+ } else {
+ unreachable!()
+ }
+}
+
+/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
+/// actual `Iterator` that the loop uses.
+pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
+ let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
+ implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
+ });
+ if impls_iterator {
+ format!(
+ "{}",
+ sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
+ )
+ } else {
+ // (&x).into_iter() ==> x.iter()
+ // (&mut x).into_iter() ==> x.iter_mut()
+ match &arg.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner)
+ if has_iter_method(cx, cx.typeck_results().expr_ty(arg_inner)).is_some() =>
+ {
+ let meth_name = match mutability {
+ Mutability::Mut => "iter_mut",
+ Mutability::Not => "iter",
+ };
+ format!(
+ "{}.{}()",
+ sugg::Sugg::hir_with_applicability(cx, arg_inner, "_", applic_ref).maybe_par(),
+ meth_name,
+ )
++ },
+ _ => format!(
+ "{}.into_iter()",
+ sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
+ ),
+ }
+ }
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::higher::PanicExpn;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{is_expn_of, sugg};
++use rustc_errors::Applicability;
++use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// ### What it does
++ /// Detects `if`-then-`panic!` that can be replaced with `assert!`.
++ ///
++ /// ### Why is this bad?
++ /// `assert!` is simpler than `if`-then-`panic!`.
++ ///
++ /// ### Example
++ /// ```rust
++ /// let sad_people: Vec<&str> = vec![];
++ /// if !sad_people.is_empty() {
++ /// panic!("there are sad people: {:?}", sad_people);
++ /// }
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// let sad_people: Vec<&str> = vec![];
++ /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
++ /// ```
++ pub MANUAL_ASSERT,
++ pedantic,
++ "`panic!` and only a `panic!` in `if`-then statement"
++}
++
++declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]);
++
++impl LateLintPass<'_> for ManualAssert {
++ 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 call = if_chain! {
++ if let ExprKind::Block(block, _) = semi.kind;
++ if let Some(init) = block.expr;
++ then {
++ init
++ } else {
++ semi
++ }
++ };
++ let span = if let Some(panic_expn) = PanicExpn::parse(call) {
++ 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 let ExprKind::Call(_, [format_args]) = call.kind {
++ 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(e, ..) = cond.kind {
++ if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e {
++ sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string()
++ } else {
++ format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par())
++ }
++ } else {
++ format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par())
++ };
++
++ span_lint_and_sugg(
++ cx,
++ MANUAL_ASSERT,
++ expr.span,
++ "only a `panic!` in `if`-then statement",
++ "try",
++ format!("assert!({}, {});", cond_sugg, sugg),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ }
++}
--- /dev/null
- CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(char::is_lowercase) },
- CaseMethod::AsciiLowerCase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'a'..='z')) },
- CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(char::is_uppercase) },
- CaseMethod::AsciiUppercase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'A'..='Z')) },
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::SymbolStr;
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `match` expressions modifying the case of a string with non-compliant arms
+ ///
+ /// ### Why is this bad?
+ /// The arm is unreachable, which is likely a mistake
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let text = "Foo";
+ ///
+ /// match &*text.to_ascii_lowercase() {
+ /// "foo" => {},
+ /// "Bar" => {},
+ /// _ => {},
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let text = "Foo";
+ ///
+ /// match &*text.to_ascii_lowercase() {
+ /// "foo" => {},
+ /// "bar" => {},
+ /// _ => {},
+ /// }
+ /// ```
+ pub MATCH_STR_CASE_MISMATCH,
+ correctness,
+ "creation of a case altering match expression with non-compliant arms"
+}
+
+declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]);
+
+#[derive(Debug)]
+enum CaseMethod {
+ LowerCase,
+ AsciiLowerCase,
+ UpperCase,
+ AsciiUppercase,
+}
+
+impl LateLintPass<'_> for MatchStrCaseMismatch {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if_chain! {
+ if !in_external_macro(cx.tcx.sess, expr.span);
+ if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind;
+ if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind();
+ if let ty::Str = ty.kind();
+ then {
+ let mut visitor = MatchExprVisitor {
+ cx,
+ case_method: None,
+ };
+
+ visitor.visit_expr(match_expr);
+
+ if let Some(case_method) = visitor.case_method {
+ if let Some((bad_case_span, bad_case_str)) = verify_case(&case_method, arms) {
+ lint(cx, &case_method, bad_case_span, &bad_case_str);
+ }
+ }
+ }
+ }
+ }
+}
+
+struct MatchExprVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ case_method: Option<CaseMethod>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
+ match ex.kind {
+ ExprKind::MethodCall(segment, _, [receiver], _)
+ if self.case_altered(&*segment.ident.as_str(), receiver) => {},
+ _ => walk_expr(self, ex),
+ }
+ }
+}
+
+impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> {
+ fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool {
+ if let Some(case_method) = get_case_method(segment_ident) {
+ let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
+
+ if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str {
+ self.case_method = Some(case_method);
+ return true;
+ }
+ }
+
+ false
+ }
+}
+
+fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
+ match segment_ident_str {
+ "to_lowercase" => Some(CaseMethod::LowerCase),
+ "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase),
+ "to_uppercase" => Some(CaseMethod::UpperCase),
+ "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase),
+ _ => None,
+ }
+}
+
+fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, SymbolStr)> {
+ let case_check = match case_method {
- CaseMethod::LowerCase => ("to_lower_case", bad_case_str.to_lowercase()),
++ CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(|c| c.to_lowercase().next() == Some(c)) },
++ CaseMethod::AsciiLowerCase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_uppercase()) },
++ CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(|c| c.to_uppercase().next() == Some(c)) },
++ CaseMethod::AsciiUppercase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_lowercase()) },
+ };
+
+ for arm in arms {
+ if_chain! {
+ if let PatKind::Lit(Expr {
+ kind: ExprKind::Lit(lit),
+ ..
+ }) = arm.pat.kind;
+ if let LitKind::Str(symbol, _) = lit.node;
+ let input = symbol.as_str();
+ if !case_check(&input);
+ then {
+ return Some((lit.span, input));
+ }
+ }
+ }
+
+ None
+}
+
+fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad_case_str: &str) {
+ let (method_str, suggestion) = match case_method {
++ CaseMethod::LowerCase => ("to_lowercase", bad_case_str.to_lowercase()),
+ CaseMethod::AsciiLowerCase => ("to_ascii_lowercase", bad_case_str.to_ascii_lowercase()),
+ CaseMethod::UpperCase => ("to_uppercase", bad_case_str.to_uppercase()),
+ CaseMethod::AsciiUppercase => ("to_ascii_uppercase", bad_case_str.to_ascii_uppercase()),
+ };
+
+ span_lint_and_sugg(
+ cx,
+ MATCH_STR_CASE_MISMATCH,
+ bad_case_span,
+ "this `match` arm has a differing case than its expression",
+ &*format!("consider changing the case of this arm to respect `{}`", method_str),
+ format!("\"{}\"", suggestion),
+ Applicability::MachineApplicable,
+ );
+}
--- /dev/null
- use clippy_utils::consts::{constant, miri_to_const, Constant};
++use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
+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) {
++ if !ranges.is_empty() {
++ if let Some((start, end)) = overlapping(&ranges) {
+ span_lint_and_note(
+ cx,
+ MATCH_OVERLAPPING_ARM,
+ start.span,
+ "some ranges overlap",
+ Some(end.span),
+ "overlaps with this",
+ );
+ }
+ }
+ }
+}
+
+fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
+ let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
+ if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
+ for arm in arms {
+ if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
+ let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
+ if path_str == "Err" {
+ let mut matching_wild = inner.iter().any(is_wild);
+ let mut ident_bind_name = String::from("_");
+ if !matching_wild {
+ // Looking for unused bindings (i.e.: `_e`)
+ for pat in inner.iter() {
+ if let PatKind::Binding(_, id, ident, None) = pat.kind {
+ if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
+ ident_bind_name = (&ident.name.as_str()).to_string();
+ matching_wild = true;
+ }
+ }
+ }
+ }
+ if_chain! {
+ if matching_wild;
- if let ExprKind::Block(block, _) = arm.body.kind;
- if is_panic_block(block);
++ if is_panic_call(arm.body);
+ then {
+ // `Err(_)` or `Err(_e)` arm with `panic!` found
+ span_lint_and_note(cx,
+ MATCH_WILD_ERR_ARM,
+ arm.pat.span,
+ &format!("`Err({})` matches all errors", &ident_bind_name),
+ None,
+ "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+enum CommonPrefixSearcher<'a> {
+ None,
+ Path(&'a [PathSegment<'a>]),
+ Mixed,
+}
+impl CommonPrefixSearcher<'a> {
+ fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
+ match path {
+ [path @ .., _] => self.with_prefix(path),
+ [] => (),
+ }
+ }
+
+ fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
+ match self {
+ Self::None => *self = Self::Path(path),
+ Self::Path(self_path)
+ if path
+ .iter()
+ .map(|p| p.ident.name)
+ .eq(self_path.iter().map(|p| p.ident.name)) => {},
+ Self::Path(_) => *self = Self::Mixed,
+ Self::Mixed => (),
+ }
+ }
+}
+
+fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
+ let attrs = cx.tcx.get_attrs(variant_def.def_id);
+ clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
+ let ty = cx.typeck_results().expr_ty(ex).peel_refs();
+ let adt_def = match ty.kind() {
+ ty::Adt(adt_def, _)
+ if adt_def.is_enum()
+ && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) =>
+ {
+ adt_def
+ },
+ _ => return,
+ };
+
+ // First pass - check for violation, but don't do much book-keeping because this is hopefully
+ // the uncommon case, and the book-keeping is slightly expensive.
+ let mut wildcard_span = None;
+ let mut wildcard_ident = None;
+ let mut has_non_wild = false;
+ for arm in arms {
+ match peel_hir_pat_refs(arm.pat).0.kind {
+ PatKind::Wild => wildcard_span = Some(arm.pat.span),
+ PatKind::Binding(_, _, ident, None) => {
+ wildcard_span = Some(arm.pat.span);
+ wildcard_ident = Some(ident);
+ },
+ _ => has_non_wild = true,
+ }
+ }
+ let wildcard_span = match wildcard_span {
+ Some(x) if has_non_wild => x,
+ _ => return,
+ };
+
+ // Accumulate the variants which should be put in place of the wildcard because they're not
+ // already covered.
+ let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
+ let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
+
+ let mut path_prefix = CommonPrefixSearcher::None;
+ for arm in arms {
+ // Guards mean that this case probably isn't exhaustively covered. Technically
+ // this is incorrect, as we should really check whether each variant is exhaustively
+ // covered by the set of guards that cover it, but that's really hard to do.
+ recurse_or_patterns(arm.pat, |pat| {
+ let path = match &peel_hir_pat_refs(pat).0.kind {
+ PatKind::Path(path) => {
+ #[allow(clippy::match_same_arms)]
+ let id = match cx.qpath_res(path, pat.hir_id) {
+ Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
+ Res::Def(_, id) => id,
+ _ => return,
+ };
+ if arm.guard.is_none() {
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ path
+ },
+ PatKind::TupleStruct(path, patterns, ..) => {
+ if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ }
+ path
+ },
+ PatKind::Struct(path, patterns, ..) => {
+ if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
+ missing_variants.retain(|e| e.def_id != id);
+ }
+ }
+ path
+ },
+ _ => return,
+ };
+ match path {
+ QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
+ QPath::TypeRelative(
+ hir::Ty {
+ kind: TyKind::Path(QPath::Resolved(_, path)),
+ ..
+ },
+ _,
+ ) => path_prefix.with_prefix(path.segments),
+ _ => (),
+ }
+ });
+ }
+
+ let format_suggestion = |variant: &VariantDef| {
+ format!(
+ "{}{}{}{}",
+ if let Some(ident) = wildcard_ident {
+ format!("{} @ ", ident.name)
+ } else {
+ String::new()
+ },
+ if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
+ let mut s = String::new();
+ for seg in path_prefix {
+ s.push_str(&seg.ident.as_str());
+ s.push_str("::");
+ }
+ s
+ } else {
+ let mut s = cx.tcx.def_path_str(adt_def.did);
+ s.push_str("::");
+ s
+ },
+ variant.ident.name,
+ match variant.ctor_kind {
+ CtorKind::Fn if variant.fields.len() == 1 => "(_)",
+ CtorKind::Fn => "(..)",
+ CtorKind::Const => "",
+ CtorKind::Fictive => "{ .. }",
+ }
+ )
+ };
+
+ match missing_variants.as_slice() {
+ [] => (),
+ [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
+ cx,
+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ wildcard_span,
+ "wildcard matches only a single variant and will also match any future added variants",
+ "try this",
+ format_suggestion(x),
+ Applicability::MaybeIncorrect,
+ ),
+ variants => {
+ let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
+ let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
+ suggestions.push("_".into());
+ "wildcard matches known variants and will also match future added variants"
+ } else {
+ "wildcard match will also match any future added variants"
+ };
+
+ span_lint_and_sugg(
+ cx,
+ WILDCARD_ENUM_MATCH_ARM,
+ wildcard_span,
+ message,
+ "try this",
+ suggestions.join(" | "),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ };
+}
+
+// If the block contains only a `panic!` macro (as expression or statement)
- fn is_panic_block(block: &Block<'_>) -> bool {
- match (&block.expr, block.stmts.len(), block.stmts.first()) {
- (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(),
- (&None, 1, Some(stmt)) => {
- is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
- },
- _ => false,
- }
++fn is_panic_call(expr: &Expr<'_>) -> bool {
++ // Unwrap any wrapping blocks
++ let span = if let ExprKind::Block(block, _) = expr.kind {
++ match (&block.expr, block.stmts.len(), block.stmts.first()) {
++ (&Some(exp), 0, _) => exp.span,
++ (&None, 1, Some(stmt)) => stmt.span,
++ _ => return false,
++ }
++ } else {
++ expr.span
++ };
++
++ is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none()
+}
+
+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_multiple_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>> {
++fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<FullInt>> {
+ 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),
++
++ let lhs_val = lhs.int_value(cx, ty)?;
++ let rhs_val = rhs.int_value(cx, ty)?;
++
++ let rhs_bound = match range_end {
++ RangeEnd::Included => Bound::Included(rhs_val),
++ RangeEnd::Excluded => Bound::Excluded(rhs_val),
+ };
+ return Some(SpannedRange {
+ span: pat.span,
- node: (lhs, rhs),
++ node: (lhs_val, rhs_bound),
+ });
+ }
+
+ if let PatKind::Lit(value) = pat.kind {
- let value = constant(cx, cx.typeck_results(), value)?.0;
++ let value = constant_full_int(cx, cx.typeck_results(), value)?;
+ return Some(SpannedRange {
+ span: pat.span,
- node: (value.clone(), Bound::Included(value)),
++ node: (value, 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_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
+where
+ 'b: 'a,
+ I: Iterator<Item = &'a Pat<'b>>,
+{
+ let mut ref_count = 0;
+ 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 {
+ ref_count += 1;
+ }
+ } else {
+ return false;
+ }
+ }
+ ref_count > 1
+}
+
+pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
+where
+ T: Copy + Ord,
+{
+ #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+ enum Kind<'a, T> {
+ Start(T, &'a SpannedRange<T>),
+ End(Bound<T>, &'a SpannedRange<T>),
+ }
+
+ impl<'a, T: Copy> Kind<'a, T> {
+ fn range(&self) -> &'a SpannedRange<T> {
+ match *self {
+ Kind::Start(_, r) | Kind::End(_, r) => r,
+ }
+ }
+
+ fn value(self) -> Bound<T> {
+ match self {
+ Kind::Start(t, _) => Bound::Included(t),
+ Kind::End(t, _) => t,
+ }
+ }
+ }
+
+ impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+ }
+
+ impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ match (self.value(), other.value()) {
+ (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
+ // Range patterns cannot be unbounded (yet)
+ (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
+ (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
+ Ordering::Equal => Ordering::Greater,
+ other => other,
+ },
+ (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
+ Ordering::Equal => Ordering::Less,
+ other => other,
+ },
+ }
+ }
+ }
+
+ let mut values = Vec::with_capacity(2 * ranges.len());
+
+ for r in ranges {
+ values.push(Kind::Start(r.node.0, r));
+ values.push(Kind::End(r.node.1, r));
+ }
+
+ values.sort();
+
+ for (a, b) in iter::zip(&values, values.iter().skip(1)) {
+ match (a, b) {
+ (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
+ if ra.node != rb.node {
+ return Some((ra, rb));
+ }
+ },
+ (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
+ _ => {
+ // skip if the range `a` is completely included into the range `b`
+ if let Ordering::Equal | Ordering::Less = a.cmp(b) {
+ let kind_a = Kind::End(a.range().node.1, a.range());
+ let kind_b = Kind::End(b.range().node.1, b.range());
+ if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
+ return None;
+ }
+ }
+ return Some((a.range(), b.range()));
+ },
+ }
+ }
+
+ None
+}
+
+mod redundant_pattern_match {
+ use super::REDUNDANT_PATTERN_MATCHING;
+ use clippy_utils::diagnostics::span_lint_and_then;
+ use clippy_utils::higher;
+ use clippy_utils::source::{snippet, snippet_with_applicability};
+ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+ use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
+ use if_chain::if_chain;
+ use rustc_ast::ast::LitKind;
+ use rustc_data_structures::fx::FxHashSet;
+ use rustc_errors::Applicability;
+ use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+ use rustc_hir::{
+ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+ Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
+ };
+ use rustc_lint::LateContext;
+ use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
+ use rustc_span::sym;
+
+ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if let Some(higher::IfLet {
+ if_else,
+ let_pat,
+ let_expr,
+ ..
+ }) = higher::IfLet::hir(cx, expr)
+ {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
+ }
+ if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
+ find_sugg_for_match(cx, expr, op, arms);
+ }
+ if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
+ }
+ }
+
+ /// Checks if the drop order for a type matters. Some std types implement drop solely to
+ /// deallocate memory. For these types, and composites containing them, changing the drop order
+ /// won't result in any observable side effects.
+ fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+ }
+
+ fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+ if !seen.insert(ty) {
+ return false;
+ }
+ if !ty.needs_drop(cx.tcx, cx.param_env) {
+ false
+ } else if !cx
+ .tcx
+ .lang_items()
+ .drop_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // This type doesn't implement drop, so no side effects here.
+ // Check if any component type has any.
+ match ty.kind() {
+ ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
+ ty::Adt(adt, subs) => adt
+ .all_fields()
+ .map(|f| f.ty(cx.tcx, subs))
+ .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ _ => true,
+ }
+ }
+ // Check for std types which implement drop, but only for memory allocation.
+ else if is_type_diagnostic_item(cx, ty, sym::Vec)
+ || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+ || is_type_diagnostic_item(cx, ty, sym::Rc)
+ || is_type_diagnostic_item(cx, ty, sym::Arc)
+ || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+ || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
+ || is_type_diagnostic_item(cx, ty, sym::LinkedList)
+ || match_type(cx, ty, &paths::WEAK_RC)
+ || match_type(cx, ty, &paths::WEAK_ARC)
+ {
+ // Check all of the generic arguments.
+ if let ty::Adt(_, subs) = ty.kind() {
+ subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
+ } else {
+ true
+ }
+ } else {
+ true
+ }
+ }
+
+ // Extract the generic arguments out of a type
+ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+ if_chain! {
+ if let ty::Adt(_, subs) = ty.kind();
+ if let Some(sub) = subs.get(index);
+ if let GenericArgKind::Type(sub_ty) = sub.unpack();
+ then {
+ Some(sub_ty)
+ } else {
+ None
+ }
+ }
+ }
+
+ // Checks if there are any temporaries created in the given expression for which drop order
+ // matters.
+ fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ struct V<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ res: bool,
+ }
+ impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ match expr.kind {
+ // Taking the reference of a value leaves a temporary
+ // e.g. In `&String::new()` the string is a temporary value.
+ // Remaining fields are temporary values
+ // e.g. In `(String::new(), 0).1` the string is a temporary value.
+ ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+ if !matches!(expr.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+ self.res = true;
+ } else {
+ self.visit_expr(expr);
+ }
+ }
+ },
+ // the base type is alway taken by reference.
+ // e.g. In `(vec![0])[0]` the vector is a temporary value.
+ ExprKind::Index(base, index) => {
+ if !matches!(base.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+ self.res = true;
+ } else {
+ self.visit_expr(base);
+ }
+ }
+ self.visit_expr(index);
+ },
+ // Method calls can take self by reference.
+ // e.g. In `String::new().len()` the string is a temporary value.
+ ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
+ if !matches!(self_arg.kind, ExprKind::Path(_)) {
+ let self_by_ref = self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+ if self_by_ref
+ && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
+ {
+ self.res = true;
+ } else {
+ self.visit_expr(self_arg);
+ }
+ }
+ args.iter().for_each(|arg| self.visit_expr(arg));
+ },
+ // Either explicitly drops values, or changes control flow.
+ ExprKind::DropTemps(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Break(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Block(Block { expr: None, .. }, _)
+ | ExprKind::Loop(..) => (),
+
+ // Only consider the final expression.
+ ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+ _ => walk_expr(self, expr),
+ }
+ }
+ }
+
+ let mut v = V { cx, res: false };
+ v.visit_expr(expr);
+ v.res
+ }
+
+ fn find_sugg_for_if_let<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ let_pat: &Pat<'_>,
+ let_expr: &'tcx Expr<'_>,
+ keyword: &'static str,
+ has_else: bool,
+ ) {
+ // also look inside refs
+ let mut kind = &let_pat.kind;
+ // if we have &None for example, peel it so we can detect "if let None = x"
+ if let PatKind::Ref(inner, _mutability) = kind {
+ kind = &inner.kind;
+ }
+ let op_ty = cx.typeck_results().expr_ty(let_expr);
+ // Determine which function should be used, and the type contained by the corresponding
+ // variant.
+ let (good_method, inner_ty) = match kind {
+ PatKind::TupleStruct(ref path, [sub_pat], _) => {
+ if let PatKind::Wild = sub_pat.kind {
+ if is_lang_ctor(cx, path, ResultOk) {
+ ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+ } else if is_lang_ctor(cx, path, ResultErr) {
+ ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+ } else if is_lang_ctor(cx, path, OptionSome) {
+ ("is_some()", op_ty)
+ } else if is_lang_ctor(cx, path, PollReady) {
+ ("is_ready()", op_ty)
+ } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
+ ("is_ipv4()", op_ty)
+ } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
+ ("is_ipv6()", op_ty)
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ },
+ PatKind::Path(ref path) => {
+ let method = if is_lang_ctor(cx, path, OptionNone) {
+ "is_none()"
+ } else if is_lang_ctor(cx, path, PollPending) {
+ "is_pending()"
+ } else {
+ return;
+ };
+ // `None` and `Pending` don't have an inner type.
+ (method, cx.tcx.types.unit)
+ },
+ _ => return,
+ };
+
+ // If this is the last expression in a block or there is an else clause then the whole
+ // type needs to be considered, not just the inner type of the branch being matched on.
+ // Note the last expression in a block is dropped after all local bindings.
+ let check_ty = if has_else
+ || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+ {
+ op_ty
+ } else {
+ inner_ty
+ };
+
+ // All temporaries created in the scrutinee expression are dropped at the same time as the
+ // scrutinee would be, so they have to be considered as well.
+ // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
+ // for the duration if body.
+ let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
+
+ // check that `while_let_on_iterator` lint does not trigger
+ if_chain! {
+ if keyword == "while";
+ if let ExprKind::MethodCall(method_path, _, _, _) = let_expr.kind;
+ if method_path.ident.name == sym::next;
+ if is_trait_method(cx, let_expr, sym::Iterator);
+ then {
+ return;
+ }
+ }
+
+ let result_expr = match &let_expr.kind {
+ ExprKind::AddrOf(_, _, borrowed) => borrowed,
+ _ => let_expr,
+ };
+ span_lint_and_then(
+ cx,
+ REDUNDANT_PATTERN_MATCHING,
+ let_pat.span,
+ &format!("redundant pattern matching, consider using `{}`", good_method),
+ |diag| {
+ // if/while let ... = ... { ... }
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ let expr_span = expr.span;
+
+ // if/while let ... = ... { ... }
+ // ^^^
+ let op_span = result_expr.span.source_callsite();
+
+ // if/while let ... = ... { ... }
+ // ^^^^^^^^^^^^^^^^^^^
+ let span = expr_span.until(op_span.shrink_to_hi());
+
+ let mut app = if needs_drop {
+ Applicability::MaybeIncorrect
+ } else {
+ Applicability::MachineApplicable
+ };
+ let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+ diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
+
+ if needs_drop {
+ diag.note("this will change drop order of the result, as well as all temporaries");
+ diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
+ }
+ },
+ );
+ }
+
+ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
+ if arms.len() == 2 {
+ let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
+
+ let found_good_method = match node_pair {
+ (
+ PatKind::TupleStruct(ref path_left, patterns_left, _),
+ PatKind::TupleStruct(ref path_right, patterns_right, _),
+ ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
+ if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::RESULT_OK,
+ &paths::RESULT_ERR,
+ "is_ok()",
+ "is_err()",
+ )
+ .or_else(|| {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::IPADDR_V4,
+ &paths::IPADDR_V6,
+ "is_ipv4()",
+ "is_ipv6()",
+ )
+ })
+ } else {
+ None
+ }
+ },
+ (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
+ | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
+ if patterns.len() == 1 =>
+ {
+ if let PatKind::Wild = patterns[0].kind {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::OPTION_SOME,
+ &paths::OPTION_NONE,
+ "is_some()",
+ "is_none()",
+ )
+ .or_else(|| {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::POLL_READY,
+ &paths::POLL_PENDING,
+ "is_ready()",
+ "is_pending()",
+ )
+ })
+ } else {
+ None
+ }
+ },
+ _ => None,
+ };
+
+ if let Some(good_method) = found_good_method {
+ let span = expr.span.to(op.span);
+ let result_expr = match &op.kind {
+ ExprKind::AddrOf(_, _, borrowed) => borrowed,
+ _ => op,
+ };
+ span_lint_and_then(
+ cx,
+ REDUNDANT_PATTERN_MATCHING,
+ expr.span,
+ &format!("redundant pattern matching, consider using `{}`", good_method),
+ |diag| {
+ diag.span_suggestion(
+ span,
+ "try this",
+ format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
+ Applicability::MaybeIncorrect, // snippet
+ );
+ },
+ );
+ }
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ fn find_good_method_for_match<'a>(
+ cx: &LateContext<'_>,
+ arms: &[Arm<'_>],
+ path_left: &QPath<'_>,
+ path_right: &QPath<'_>,
+ expected_left: &[&str],
+ expected_right: &[&str],
+ should_be_left: &'a str,
+ should_be_right: &'a str,
+ ) -> Option<&'a str> {
+ let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
+ && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
+ {
+ (&(*arms[0].body).kind, &(*arms[1].body).kind)
+ } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
+ && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
+ {
+ (&(*arms[1].body).kind, &(*arms[0].body).kind)
+ } else {
+ return None;
+ };
+
+ match body_node_pair {
+ (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
+ (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
+ (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+}
+
+#[test]
+fn test_overlapping() {
+ use rustc_span::source_map::DUMMY_SP;
+
+ let sp = |s, e| SpannedRange {
+ span: DUMMY_SP,
+ node: (s, e),
+ };
+
+ assert_eq!(None, overlapping::<u8>(&[]));
+ assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
+ assert_eq!(
+ None,
+ overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
+ );
+ assert_eq!(
+ None,
+ overlapping(&[
+ sp(1, Bound::Included(4)),
+ sp(5, Bound::Included(6)),
+ sp(10, Bound::Included(11))
+ ],)
+ );
+ assert_eq!(
+ Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
+ overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
+ );
+ assert_eq!(
+ Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
+ overlapping(&[
+ sp(1, Bound::Included(4)),
+ sp(5, Bound::Included(6)),
+ sp(6, Bound::Included(11))
+ ],)
+ );
+}
+
+/// Implementation of `MATCH_SAME_ARMS`.
+fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
+ let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
+ let mut h = SpanlessHash::new(cx);
+ h.hash_expr(arm.body);
+ h.finish()
+ };
+
+ let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
+ let min_index = usize::min(lindex, rindex);
+ let max_index = usize::max(lindex, rindex);
+
+ let mut local_map: HirIdMap<HirId> = HirIdMap::default();
+ let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
+ if_chain! {
+ if let Some(a_id) = path_to_local(a);
+ if let Some(b_id) = path_to_local(b);
+ let entry = match local_map.entry(a_id) {
+ Entry::Vacant(entry) => entry,
+ // check if using the same bindings as before
+ Entry::Occupied(entry) => return *entry.get() == b_id,
+ };
+ // the names technically don't have to match; this makes the lint more conservative
+ if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
+ if TyS::same_type(cx.typeck_results().expr_ty(a), cx.typeck_results().expr_ty(b));
+ if pat_contains_local(lhs.pat, a_id);
+ if pat_contains_local(rhs.pat, b_id);
+ then {
+ entry.insert(b_id);
+ true
+ } else {
+ false
+ }
+ }
+ };
+ // Arms with a guard are ignored, those can’t always be merged together
+ // This is also the case for arms in-between each there is an arm with a guard
+ (min_index..=max_index).all(|index| arms[index].guard.is_none())
+ && SpanlessEq::new(cx)
+ .expr_fallback(eq_fallback)
+ .eq_expr(lhs.body, rhs.body)
+ // these checks could be removed to allow unused bindings
+ && bindings_eq(lhs.pat, local_map.keys().copied().collect())
+ && bindings_eq(rhs.pat, local_map.values().copied().collect())
+ };
+
+ let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
+ for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
+ span_lint_and_then(
+ cx,
+ MATCH_SAME_ARMS,
+ j.body.span,
+ "this `match` has identical arm bodies",
+ |diag| {
+ diag.span_note(i.body.span, "same as this");
+
+ // Note: this does not use `span_suggestion` on purpose:
+ // there is no clean way
+ // to remove the other arm. Building a span and suggest to replace it to ""
+ // makes an even more confusing error message. Also in order not to make up a
+ // span for the whole pattern, the suggestion is only shown when there is only
+ // one pattern. The user should know about `|` if they are already using it…
+
+ let lhs = snippet(cx, i.pat.span, "<pat1>");
+ let rhs = snippet(cx, j.pat.span, "<pat2>");
+
+ if let PatKind::Wild = j.pat.kind {
+ // if the last arm is _, then i could be integrated into _
+ // note that i.pat cannot be _, because that would mean that we're
+ // hiding all the subsequent arms, and rust won't compile
+ diag.span_note(
+ i.body.span,
+ &format!(
+ "`{}` has the same arm body as the `_` wildcard, consider removing it",
+ lhs
+ ),
+ );
+ } else {
+ diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
+ .help("...or consider changing the match arm bodies");
+ }
+ },
+ );
+ }
+ }
+}
+
+fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
+ let mut result = false;
+ pat.walk_short(|p| {
+ result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
+ !result
+ });
+ result
+}
+
+/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
+fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
+ let mut result = true;
+ pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
+ result && ids.is_empty()
+}
--- /dev/null
- }
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::get_parent_node;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::sugg;
+use clippy_utils::ty::is_copy;
+use rustc_errors::Applicability;
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, adjustment::Adjust};
+use rustc_span::symbol::{sym, Symbol};
+
+use super::CLONE_DOUBLE_REF;
+use super::CLONE_ON_COPY;
+
+/// Checks for the `CLONE_ON_COPY` lint.
+#[allow(clippy::too_many_lines)]
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, args: &[Expr<'_>]) {
+ let arg = match args {
+ [arg] if method_name == sym::clone => arg,
+ _ => return,
+ };
+ if cx
+ .typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .and_then(|id| cx.tcx.trait_of_item(id))
+ .zip(cx.tcx.lang_items().clone_trait())
+ .map_or(true, |(x, y)| x != y)
+ {
+ return;
+ }
+ let arg_adjustments = cx.typeck_results().expr_adjustments(arg);
+ let arg_ty = arg_adjustments
+ .last()
+ .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target);
+
+ let ty = cx.typeck_results().expr_ty(expr);
+ if let ty::Ref(_, inner, _) = arg_ty.kind() {
+ if let ty::Ref(_, innermost, _) = inner.kind() {
+ span_lint_and_then(
+ cx,
+ CLONE_DOUBLE_REF,
+ expr.span,
+ &format!(
+ "using `clone` on a double-reference; \
+ this will copy the reference of type `{}` instead of cloning the inner type",
+ ty
+ ),
+ |diag| {
+ if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
+ let mut ty = innermost;
+ let mut n = 0;
+ while let ty::Ref(_, inner, _) = ty.kind() {
+ ty = inner;
+ n += 1;
+ }
+ let refs = "&".repeat(n + 1);
+ let derefs = "*".repeat(n);
+ let explicit = format!("<{}{}>::clone({})", refs, ty, snip);
+ diag.span_suggestion(
+ expr.span,
+ "try dereferencing it",
+ format!("{}({}{}).clone()", refs, derefs, snip.deref()),
+ Applicability::MaybeIncorrect,
+ );
+ diag.span_suggestion(
+ expr.span,
+ "or try being explicit if you are sure, that you want to clone a reference",
+ explicit,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ },
+ );
+ return; // don't report clone_on_copy
+ }
+ }
+
+ if is_copy(cx, ty) {
+ let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) {
+ Some(Node::Expr(parent)) => match parent.kind {
+ // &*x is a nop, &x.clone() is not
+ ExprKind::AddrOf(..) => return,
+ // (*x).func() is useless, x.clone().func() can work in case func borrows self
+ ExprKind::MethodCall(_, _, [self_arg, ..], _)
+ if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) =>
+ {
+ return;
- }
++ },
+ ExprKind::MethodCall(_, _, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true,
+ ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+ | ExprKind::Field(..)
+ | ExprKind::Index(..) => true,
+ _ => false,
+ },
+ // local binding capturing a reference
+ Some(Node::Local(l))
+ if matches!(
+ l.pat.kind,
+ PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..)
+ ) =>
+ {
+ return;
++ },
+ _ => false,
+ };
+
+ let mut app = Applicability::MachineApplicable;
+ let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0;
+
+ let deref_count = arg_adjustments
+ .iter()
+ .take_while(|adj| matches!(adj.kind, Adjust::Deref(_)))
+ .count();
+ let (help, sugg) = if deref_count == 0 {
+ ("try removing the `clone` call", snip.into())
+ } else if parent_is_suffix_expr {
+ ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip))
+ } else {
+ ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip))
+ };
+
+ span_lint_and_sugg(
+ cx,
+ CLONE_ON_COPY,
+ expr.span,
+ &format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
+ help,
+ sugg,
+ app,
+ );
+ }
+}
--- /dev/null
- }
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eager_or_lazy::is_lazyness_candidate;
+use clippy_utils::is_trait_item;
+use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
+use clippy_utils::ty::implements_trait;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{contains_return, last_path_segment, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::{BlockCheckMode, UnsafeSource};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::source_map::Span;
+use rustc_span::symbol::{kw, sym};
+use std::borrow::Cow;
+
+use super::OR_FUN_CALL;
+
+/// Checks for the `OR_FUN_CALL` lint.
+#[allow(clippy::too_many_lines)]
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &hir::Expr<'_>,
+ method_span: Span,
+ name: &str,
+ args: &'tcx [hir::Expr<'_>],
+) {
+ /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
+ fn check_unwrap_or_default(
+ cx: &LateContext<'_>,
+ name: &str,
+ fun: &hir::Expr<'_>,
+ self_expr: &hir::Expr<'_>,
+ arg: &hir::Expr<'_>,
+ or_has_args: bool,
+ span: Span,
+ ) -> bool {
+ let is_default_default = || is_trait_item(cx, fun, sym::Default);
+
+ let implements_default = |arg, default_trait_id| {
+ let arg_ty = cx.typeck_results().expr_ty(arg);
+ implements_trait(cx, arg_ty, default_trait_id, &[])
+ };
+
+ if_chain! {
+ if !or_has_args;
+ if name == "unwrap_or";
+ if let hir::ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
+ let path = last_path_segment(qpath).ident.name;
+ // needs to target Default::default in particular or be *::new and have a Default impl
+ // available
+ if (matches!(path, kw::Default) && is_default_default())
+ || (matches!(path, sym::new) && implements_default(arg, default_trait_id));
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ OR_FUN_CALL,
+ span,
+ &format!("use of `{}` followed by a call to `{}`", name, path),
+ "try this",
+ format!(
+ "{}.unwrap_or_default()",
+ snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
+ ),
+ applicability,
+ );
+
+ true
+ } else {
+ false
+ }
+ }
+ }
+
+ /// Checks for `*or(foo())`.
+ #[allow(clippy::too_many_arguments)]
+ fn check_general_case<'tcx>(
+ cx: &LateContext<'tcx>,
+ name: &str,
+ method_span: Span,
+ self_expr: &hir::Expr<'_>,
+ arg: &'tcx hir::Expr<'_>,
+ span: Span,
+ // None if lambda is required
+ fun_span: Option<Span>,
+ ) {
+ // (path, fn_has_argument, methods, suffix)
+ static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
+ (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
+ (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
+ (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
+ (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
+ ];
+
+ if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind {
+ if path.ident.name == sym::len {
+ let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+
+ match ty.kind() {
+ ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
+ _ => (),
+ }
+
+ if is_type_diagnostic_item(cx, ty, sym::Vec) {
+ return;
+ }
+ }
+ }
+
+ if_chain! {
+ if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
+
+ if is_lazyness_candidate(cx, arg);
+ if !contains_return(arg);
+
+ let self_ty = cx.typeck_results().expr_ty(self_expr);
+
+ if let Some(&(_, fn_has_arguments, poss, suffix)) =
+ KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
+
+ if poss.contains(&name);
+
+ then {
+ let macro_expanded_snipped;
+ let sugg: Cow<'_, str> = {
+ let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
+ (false, Some(fun_span)) => (fun_span, false),
+ _ => (arg.span, true),
+ };
+ let snippet = {
+ let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
+ if not_macro_argument_snippet == "vec![]" {
+ macro_expanded_snipped = snippet(cx, snippet_span, "..");
+ match macro_expanded_snipped.strip_prefix("$crate::vec::") {
+ Some(stripped) => Cow::from(stripped),
+ None => macro_expanded_snipped
+ }
+ }
+ else {
+ not_macro_argument_snippet
+ }
+ };
+
+ if use_lambda {
+ let l_arg = if fn_has_arguments { "_" } else { "" };
+ format!("|{}| {}", l_arg, snippet).into()
+ } else {
+ snippet
+ }
+ };
+ let span_replace_word = method_span.with_hi(span.hi());
+ span_lint_and_sugg(
+ cx,
+ OR_FUN_CALL,
+ span_replace_word,
+ &format!("use of `{}` followed by a function call", name),
+ "try this",
+ format!("{}_{}({})", name, suffix, sugg),
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ }
+
+ if args.len() == 2 {
+ match args[1].kind {
+ hir::ExprKind::Call(fun, or_args) => {
+ let or_has_args = !or_args.is_empty();
+ if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
+ let fun_span = if or_has_args { None } else { Some(fun.span) };
+ check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
+ }
+ },
+ hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
+ check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
+ },
+ hir::ExprKind::Block(block, _)
+ if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) =>
+ {
+ if let Some(block_expr) = block.expr {
+ if let hir::ExprKind::MethodCall(..) = block_expr.kind {
+ check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
+ }
+ }
++ },
+ _ => (),
+ }
+ }
+}
--- /dev/null
- use super::MiscEarlyLints;
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::ast::{Expr, ExprKind, UnOp};
+use rustc_lint::EarlyContext;
+
+use super::DOUBLE_NEG;
+
+pub(super) fn check(cx: &EarlyContext<'_>, expr: &Expr) {
- match expr.kind {
- ExprKind::Unary(UnOp::Neg, ref inner) => {
- if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
- span_lint(
- cx,
- DOUBLE_NEG,
- expr.span,
- "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
- );
- }
- },
- ExprKind::Lit(ref lit) => MiscEarlyLints::check_lit(cx, lit),
- _ => (),
++ if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind {
++ if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
++ span_lint(
++ cx,
++ DOUBLE_NEG,
++ expr.span,
++ "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
++ );
++ }
+ }
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use rustc_ast::ast::Lit;
++use rustc_errors::Applicability;
++use rustc_lint::EarlyContext;
++
++use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX};
++
++pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) {
++ let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
++ val
++ } else {
++ return; // It's useless so shouldn't lint.
++ };
++ // Do not lint when literal is unsuffixed.
++ if !suffix.is_empty() {
++ if lit_snip.as_bytes()[maybe_last_sep_idx] == b'_' {
++ span_lint_and_sugg(
++ cx,
++ SEPARATED_LITERAL_SUFFIX,
++ lit.span,
++ &format!("{} type suffix should not be separated by an underscore", sugg_type),
++ "remove the underscore",
++ format!("{}{}", &lit_snip[..maybe_last_sep_idx], suffix),
++ Applicability::MachineApplicable,
++ );
++ } else {
++ span_lint_and_sugg(
++ cx,
++ UNSEPARATED_LITERAL_SUFFIX,
++ lit.span,
++ &format!("{} type suffix should be separated by an underscore", sugg_type),
++ "add an underscore",
++ format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++}
--- /dev/null
- mod unseparated_literal_suffix;
+mod builtin_type_shadow;
+mod double_neg;
++mod literal_suffix;
+mod mixed_case_hex_literals;
+mod redundant_pattern;
+mod unneeded_field_pattern;
+mod unneeded_wildcard_pattern;
- use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
+mod zero_prefixed_literal;
+
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::snippet_opt;
- /// It is much less readable.
++use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
+use rustc_ast::visit::FnKind;
+use rustc_data_structures::fx::FxHashMap;
+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 structure field patterns bound to wildcards.
+ ///
+ /// ### Why is this bad?
+ /// Using `..` instead is shorter and leaves the focus on
+ /// the fields that are actually bound.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # struct Foo {
+ /// # a: i32,
+ /// # b: i32,
+ /// # c: i32,
+ /// # }
+ /// let f = Foo { a: 0, b: 0, c: 0 };
+ ///
+ /// // Bad
+ /// match f {
+ /// Foo { a: _, b: 0, .. } => {},
+ /// Foo { a: _, b: _, c: _ } => {},
+ /// }
+ ///
+ /// // Good
+ /// match f {
+ /// Foo { b: 0, .. } => {},
+ /// Foo { .. } => {},
+ /// }
+ /// ```
+ pub UNNEEDED_FIELD_PATTERN,
+ restriction,
+ "struct fields bound to a wildcard instead of using `..`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for function arguments having the similar names
+ /// differing by an underscore.
+ ///
+ /// ### Why is this bad?
+ /// It affects code readability.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// fn foo(a: i32, _a: i32) {}
+ ///
+ /// // Good
+ /// fn bar(a: i32, _b: i32) {}
+ /// ```
+ pub DUPLICATE_UNDERSCORE_ARGUMENT,
+ style,
+ "function arguments having names which only differ by an underscore"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects expressions of the form `--x`.
+ ///
+ /// ### Why is this bad?
+ /// It can mislead C/C++ programmers to think `x` was
+ /// decremented.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut x = 3;
+ /// --x;
+ /// ```
+ pub DOUBLE_NEG,
+ style,
+ "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns on hexadecimal literals with mixed-case letter
+ /// digits.
+ ///
+ /// ### Why is this bad?
+ /// It looks confusing.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let y = 0x1a9BAcD;
+ ///
+ /// // Good
+ /// let y = 0x1A9BACD;
+ /// ```
+ pub MIXED_CASE_HEX_LITERALS,
+ style,
+ "hex literals whose letter digits are not consistently upper- or lowercased"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns if literal suffixes are not separated by an
+ /// underscore.
++ /// To enforce unseparated literal suffix style,
++ /// see the `separated_literal_suffix` lint.
+ ///
+ /// ### Why is this bad?
- pedantic,
++ /// Suffix style should be consistent.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let y = 123832i32;
+ ///
+ /// // Good
+ /// let y = 123832_i32;
+ /// ```
+ pub UNSEPARATED_LITERAL_SUFFIX,
- unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "integer");
++ restriction,
+ "literals whose suffix is not separated by an underscore"
+}
+
++declare_clippy_lint! {
++ /// ### What it does
++ /// Warns if literal suffixes are separated by an underscore.
++ /// To enforce separated literal suffix style,
++ /// see the `unseparated_literal_suffix` lint.
++ ///
++ /// ### Why is this bad?
++ /// Suffix style should be consistent.
++ ///
++ /// ### Example
++ /// ```rust
++ /// // Bad
++ /// let y = 123832_i32;
++ ///
++ /// // Good
++ /// let y = 123832i32;
++ /// ```
++ pub SEPARATED_LITERAL_SUFFIX,
++ restriction,
++ "literals whose suffix is separated by an underscore"
++}
++
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns if an integral constant literal starts with `0`.
+ ///
+ /// ### Why is this bad?
+ /// In some languages (including the infamous C language
+ /// and most of its
+ /// family), this marks an octal constant. In Rust however, this is a decimal
+ /// constant. This could
+ /// be confusing for both the writer and a reader of the constant.
+ ///
+ /// ### Example
+ ///
+ /// In Rust:
+ /// ```rust
+ /// fn main() {
+ /// let a = 0123;
+ /// println!("{}", a);
+ /// }
+ /// ```
+ ///
+ /// prints `123`, while in C:
+ ///
+ /// ```c
+ /// #include <stdio.h>
+ ///
+ /// int main() {
+ /// int a = 0123;
+ /// printf("%d\n", a);
+ /// }
+ /// ```
+ ///
+ /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
+ pub ZERO_PREFIXED_LITERAL,
+ complexity,
+ "integer literals starting with `0`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns if a generic shadows a built-in type.
+ ///
+ /// ### Why is this bad?
+ /// This gives surprising type errors.
+ ///
+ /// ### Example
+ ///
+ /// ```ignore
+ /// impl<u32> Foo<u32> {
+ /// fn impl_func(&self) -> u32 {
+ /// 42
+ /// }
+ /// }
+ /// ```
+ pub BUILTIN_TYPE_SHADOW,
+ style,
+ "shadowing a builtin type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for patterns in the form `name @ _`.
+ ///
+ /// ### Why is this bad?
+ /// It's almost always more readable to just use direct
+ /// bindings.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let v = Some("abc");
+ ///
+ /// // Bad
+ /// match v {
+ /// Some(x) => (),
+ /// y @ _ => (),
+ /// }
+ ///
+ /// // Good
+ /// match v {
+ /// Some(x) => (),
+ /// y => (),
+ /// }
+ /// ```
+ pub REDUNDANT_PATTERN,
+ style,
+ "using `name @ _` in a pattern"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for tuple patterns with a wildcard
+ /// pattern (`_`) is next to a rest pattern (`..`).
+ ///
+ /// _NOTE_: While `_, ..` means there is at least one element left, `..`
+ /// means there are 0 or more elements left. This can make a difference
+ /// when refactoring, but shouldn't result in errors in the refactored code,
+ /// since the wildcard pattern isn't used anyway.
+ /// ### Why is this bad?
+ /// The wildcard pattern is unneeded as the rest pattern
+ /// can match that element as well.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # struct TupleStruct(u32, u32, u32);
+ /// # let t = TupleStruct(1, 2, 3);
+ /// // Bad
+ /// match t {
+ /// TupleStruct(0, .., _) => (),
+ /// _ => (),
+ /// }
+ ///
+ /// // Good
+ /// match t {
+ /// TupleStruct(0, ..) => (),
+ /// _ => (),
+ /// }
+ /// ```
+ pub UNNEEDED_WILDCARD_PATTERN,
+ complexity,
+ "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
+}
+
+declare_lint_pass!(MiscEarlyLints => [
+ UNNEEDED_FIELD_PATTERN,
+ DUPLICATE_UNDERSCORE_ARGUMENT,
+ DOUBLE_NEG,
+ MIXED_CASE_HEX_LITERALS,
+ UNSEPARATED_LITERAL_SUFFIX,
++ SEPARATED_LITERAL_SUFFIX,
+ ZERO_PREFIXED_LITERAL,
+ BUILTIN_TYPE_SHADOW,
+ REDUNDANT_PATTERN,
+ UNNEEDED_WILDCARD_PATTERN,
+]);
+
+impl EarlyLintPass for MiscEarlyLints {
+ fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
+ for param in &gen.params {
+ builtin_type_shadow::check(cx, param);
+ }
+ }
+
+ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
+ unneeded_field_pattern::check(cx, pat);
+ redundant_pattern::check(cx, pat);
+ unneeded_wildcard_pattern::check(cx, pat);
+ }
+
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
+ let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
+
+ for arg in &fn_kind.decl().inputs {
+ if let PatKind::Ident(_, ident, None) = arg.pat.kind {
+ let arg_name = ident.to_string();
+
+ if let Some(arg_name) = arg_name.strip_prefix('_') {
+ if let Some(correspondence) = registered_names.get(arg_name) {
+ span_lint(
+ cx,
+ DUPLICATE_UNDERSCORE_ARGUMENT,
+ *correspondence,
+ &format!(
+ "`{}` already exists, having another argument having almost the same \
+ name makes code comprehension and documentation more difficult",
+ arg_name
+ ),
+ );
+ }
+ } else {
+ registered_names.insert(arg_name, arg.pat.span);
+ }
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ if in_external_macro(cx.sess, expr.span) {
+ return;
+ }
++
++ if let ExprKind::Lit(ref lit) = expr.kind {
++ MiscEarlyLints::check_lit(cx, lit);
++ }
+ double_neg::check(cx, expr);
+ }
+}
+
+impl MiscEarlyLints {
+ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
+ // We test if first character in snippet is a number, because the snippet could be an expansion
+ // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
+ // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
+ // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
+ // FIXME: Find a better way to detect those cases.
+ let lit_snip = match snippet_opt(cx, lit.span) {
+ Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
+ _ => return,
+ };
+
+ if let LitKind::Int(value, lit_int_type) = lit.kind {
+ let suffix = match lit_int_type {
+ LitIntType::Signed(ty) => ty.name_str(),
+ LitIntType::Unsigned(ty) => ty.name_str(),
+ LitIntType::Unsuffixed => "",
+ };
- unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "float");
++ literal_suffix::check(cx, lit, &lit_snip, suffix, "integer");
+ if lit_snip.starts_with("0x") {
+ mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip);
+ } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
+ // nothing to do
+ } else if value != 0 && lit_snip.starts_with('0') {
+ zero_prefixed_literal::check(cx, lit, &lit_snip);
+ }
+ } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
+ let suffix = float_ty.name_str();
++ literal_suffix::check(cx, lit, &lit_snip, suffix, "float");
+ }
+ }
+}
--- /dev/null
- }
+use std::{
+ ffi::OsString,
+ path::{Component, Path},
+};
+
+use rustc_ast::ast;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks that module layout uses only self named module files, bans mod.rs files.
+ ///
+ /// ### Why is this bad?
+ /// Having multiple module layout styles in a project can be confusing.
+ ///
+ /// ### Example
+ /// ```text
+ /// src/
+ /// stuff/
+ /// stuff_files.rs
+ /// mod.rs
+ /// lib.rs
+ /// ```
+ /// Use instead:
+ /// ```text
+ /// src/
+ /// stuff/
+ /// stuff_files.rs
+ /// stuff.rs
+ /// lib.rs
+ /// ```
+ pub MOD_MODULE_FILES,
+ restriction,
+ "checks that module layout is consistent"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks that module layout uses only mod.rs files.
+ ///
+ /// ### Why is this bad?
+ /// Having multiple module layout styles in a project can be confusing.
+ ///
+ /// ### Example
+ /// ```text
+ /// src/
+ /// stuff/
+ /// stuff_files.rs
+ /// stuff.rs
+ /// lib.rs
+ /// ```
+ /// Use instead:
+ /// ```text
+ /// src/
+ /// stuff/
+ /// stuff_files.rs
+ /// mod.rs
+ /// lib.rs
+ /// ```
+
+ pub SELF_NAMED_MODULE_FILES,
+ restriction,
+ "checks that module layout is consistent"
+}
+
+pub struct ModStyle;
+
+impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]);
+
+impl EarlyLintPass for ModStyle {
+ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
+ if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow
+ && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow
+ {
+ return;
+ }
+
+ let files = cx.sess.source_map().files();
+
+ let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess.opts.working_dir {
+ p.to_string_lossy()
+ } else {
+ return;
+ };
+
+ // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
+ // `[path, to]` but not foo
+ let mut folder_segments = FxHashSet::default();
+ // `mod_folders` is all the unique folder names that contain a mod.rs file
+ let mut mod_folders = FxHashSet::default();
+ // `file_map` maps file names to the full path including the file name
+ // `{ foo => path/to/foo.rs, .. }
+ let mut file_map = FxHashMap::default();
+ for file in files.iter() {
+ match &file.name {
+ FileName::Real(RealFileName::LocalPath(lp))
+ if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) =>
+ {
+ let p = lp.to_string_lossy();
+ let path = Path::new(p.trim_start_matches(trim_to_src.as_ref()));
+ if let Some(stem) = path.file_stem() {
+ file_map.insert(stem.to_os_string(), (file, path.to_owned()));
+ }
+ process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
+ check_self_named_mod_exists(cx, path, file);
++ },
+ _ => {},
+ }
+ }
+
+ for folder in &folder_segments {
+ if !mod_folders.contains(folder) {
+ if let Some((file, path)) = file_map.get(folder) {
+ let mut correct = path.clone();
+ correct.pop();
+ correct.push(folder);
+ correct.push("mod.rs");
+ cx.struct_span_lint(
+ SELF_NAMED_MODULE_FILES,
+ Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None),
+ |build| {
+ let mut lint =
+ build.build(&format!("`mod.rs` files are required, found `{}`", path.display()));
+ lint.help(&format!("move `{}` to `{}`", path.display(), correct.display(),));
+ lint.emit();
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
+/// For each `path` we add each folder component to `folder_segments` and if the file name
+/// is `mod.rs` we add it's parent folder to `mod_folders`.
+fn process_paths_for_mod_files(
+ path: &Path,
+ folder_segments: &mut FxHashSet<OsString>,
+ mod_folders: &mut FxHashSet<OsString>,
+) {
+ let mut comp = path.components().rev().peekable();
+ let _ = comp.next();
+ if path.ends_with("mod.rs") {
+ mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default());
+ }
+ let folders = comp
+ .filter_map(|c| {
+ if let Component::Normal(s) = c {
+ Some(s.to_os_string())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ folder_segments.extend(folders);
+}
+
+/// Checks every path for the presence of `mod.rs` files and emits the lint if found.
+fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) {
+ if path.ends_with("mod.rs") {
+ let mut mod_file = path.to_path_buf();
+ mod_file.pop();
+ mod_file.set_extension("rs");
+
+ cx.struct_span_lint(
+ MOD_MODULE_FILES,
+ Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None),
+ |build| {
+ let mut lint = build.build(&format!("`mod.rs` files are not allowed, found `{}`", path.display()));
+ lint.help(&format!("move `{}` to `{}`", path.display(), mod_file.display(),));
+ lint.emit();
+ },
+ );
+ }
+}
--- /dev/null
- if let [Adjustment {
- kind: Adjust::Deref(_), ..
- }, Adjustment {
- kind: Adjust::Deref(_), ..
- }, Adjustment {
- kind: Adjust::Borrow(_),
- ..
- }] = *adj3
+//! 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;
+ }
+ 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!(
+ "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
- nursery,
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_lint_allowed;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, is_copy};
+use rustc_ast::ImplPolarity;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{FieldDef, Item, ItemKind, Node};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns about fields in struct implementing `Send` that are neither `Send` nor `Copy`.
+ ///
+ /// ### Why is this bad?
+ /// Sending the struct to another thread will transfer the ownership to
+ /// the new thread by dropping in the current thread during the transfer.
+ /// This causes soundness issues for non-`Send` fields, as they are also
+ /// dropped and might not be set up to handle this.
+ ///
+ /// See:
+ /// * [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html)
+ /// * [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html)
+ ///
+ /// ### Known Problems
+ /// Data structures that contain raw pointers may cause false positives.
+ /// They are sometimes safe to be sent across threads but do not implement
+ /// the `Send` trait. This lint has a heuristic to filter out basic cases
+ /// such as `Vec<*const T>`, but it's not perfect. Feel free to create an
+ /// issue if you have a suggestion on how this heuristic can be improved.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// struct ExampleStruct<T> {
+ /// rc_is_not_send: Rc<String>,
+ /// unbounded_generic_field: T,
+ /// }
+ ///
+ /// // This impl is unsound because it allows sending `!Send` types through `ExampleStruct`
+ /// unsafe impl<T> Send for ExampleStruct<T> {}
+ /// ```
+ /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
+ /// or specify correct bounds on generic type parameters (`T: Send`).
+ pub NON_SEND_FIELDS_IN_SEND_TY,
++ suspicious,
+ "there is field that does not implement `Send` in a `Send` struct"
+}
+
+#[derive(Copy, Clone)]
+pub struct NonSendFieldInSendTy {
+ enable_raw_pointer_heuristic: bool,
+}
+
+impl NonSendFieldInSendTy {
+ pub fn new(enable_raw_pointer_heuristic: bool) -> Self {
+ Self {
+ enable_raw_pointer_heuristic,
+ }
+ }
+}
+
+impl_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELDS_IN_SEND_TY]);
+
+impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ let ty_allowed_in_send = if self.enable_raw_pointer_heuristic {
+ ty_allowed_with_raw_pointer_heuristic
+ } else {
+ ty_allowed_without_raw_pointer_heuristic
+ };
+
+ // Checks if we are in `Send` impl item.
+ // We start from `Send` impl instead of `check_field_def()` because
+ // single `AdtDef` may have multiple `Send` impls due to generic
+ // parameters, and the lint is much easier to implement in this way.
+ if_chain! {
+ if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send);
+ if let ItemKind::Impl(hir_impl) = &item.kind;
+ if let Some(trait_ref) = &hir_impl.of_trait;
+ if let Some(trait_id) = trait_ref.trait_def_id();
+ if send_trait == trait_id;
+ if hir_impl.polarity == ImplPolarity::Positive;
+ if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.def_id);
+ if let self_ty = ty_trait_ref.self_ty();
+ if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
+ then {
+ let mut non_send_fields = Vec::new();
+
+ let hir_map = cx.tcx.hir();
+ for variant in &adt_def.variants {
+ for field in &variant.fields {
+ if_chain! {
+ if let Some(field_hir_id) = field
+ .did
+ .as_local()
+ .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id));
+ if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id);
+ if let field_ty = field.ty(cx.tcx, impl_trait_substs);
+ if !ty_allowed_in_send(cx, field_ty, send_trait);
+ if let Node::Field(field_def) = hir_map.get(field_hir_id);
+ then {
+ non_send_fields.push(NonSendField {
+ def: field_def,
+ ty: field_ty,
+ generic_params: collect_generic_params(cx, field_ty),
+ })
+ }
+ }
+ }
+ }
+
+ if !non_send_fields.is_empty() {
+ span_lint_and_then(
+ cx,
+ NON_SEND_FIELDS_IN_SEND_TY,
+ item.span,
+ &format!(
+ "this implementation is unsound, as some fields in `{}` are `!Send`",
+ snippet(cx, hir_impl.self_ty.span, "Unknown")
+ ),
+ |diag| {
+ for field in non_send_fields {
+ diag.span_note(
+ field.def.span,
+ &format!("the type of field `{}` is `!Send`", field.def.ident.name),
+ );
+
+ match field.generic_params.len() {
+ 0 => diag.help("use a thread-safe type that implements `Send`"),
+ 1 if is_ty_param(field.ty) => diag.help(&format!("add `{}: Send` bound in `Send` impl", field.ty)),
+ _ => diag.help(&format!(
+ "add bounds on type parameter{} `{}` that satisfy `{}: Send`",
+ if field.generic_params.len() > 1 { "s" } else { "" },
+ field.generic_params_string(),
+ snippet(cx, field.def.ty.span, "Unknown"),
+ )),
+ };
+ }
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
+struct NonSendField<'tcx> {
+ def: &'tcx FieldDef<'tcx>,
+ ty: Ty<'tcx>,
+ generic_params: Vec<Ty<'tcx>>,
+}
+
+impl<'tcx> NonSendField<'tcx> {
+ fn generic_params_string(&self) -> String {
+ self.generic_params
+ .iter()
+ .map(ToString::to_string)
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+}
+
+/// Given a type, collect all of its generic parameters.
+/// Example: `MyStruct<P, Box<Q, R>>` => `vec![P, Q, R]`
+fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec<Ty<'tcx>> {
+ ty.walk(cx.tcx)
+ .filter_map(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => Some(inner_ty),
+ _ => None,
+ })
+ .filter(|&inner_ty| is_ty_param(inner_ty))
+ .collect()
+}
+
+/// Be more strict when the heuristic is disabled
+fn ty_allowed_without_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool {
+ if implements_trait(cx, ty, send_trait, &[]) {
+ return true;
+ }
+
+ if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) {
+ return true;
+ }
+
+ false
+}
+
+/// Heuristic to allow cases like `Vec<*const u8>`
+fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool {
+ if implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) {
+ return true;
+ }
+
+ // The type is known to be `!Send` and `!Copy`
+ match ty.kind() {
+ ty::Tuple(_) => ty
+ .tuple_fields()
+ .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
+ ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
+ ty::Adt(_, substs) => {
+ if contains_raw_pointer(cx, ty) {
+ // descends only if ADT contains any raw pointers
+ substs.iter().all(|generic_arg| match generic_arg.unpack() {
+ GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
+ // Lifetimes and const generics are not solid part of ADT and ignored
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true,
+ })
+ } else {
+ false
+ }
+ },
+ // Raw pointers are `!Send` but allowed by the heuristic
+ ty::RawPtr(_) => true,
+ _ => false,
+ }
+}
+
+/// Checks if the type contains any raw pointers in substs (including nested ones).
+fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
+ for ty_node in target_ty.walk(cx.tcx) {
+ if_chain! {
+ if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
+ if let ty::RawPtr(_) = inner_ty.kind();
+ then {
+ return true;
+ }
+ }
+ }
+
+ false
+}
+
+/// Returns `true` if the type is a type parameter such as `T`.
+fn is_ty_param(target_ty: Ty<'_>) -> bool {
+ matches!(target_ty.kind(), ty::Param(_))
+}
--- /dev/null
- /// Using the dedicated functions of the Option type is clearer and
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{
+ can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while,
+ CaptureKind,
+};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::OptionSome;
+use rustc_hir::{def::Res, BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
+ /// idiomatically done with `Option::map_or` (if the else bit is a pure
+ /// expression) or `Option::map_or_else` (if the else bit is an impure
+ /// expression).
+ ///
+ /// ### Why is this bad?
++ /// Using the dedicated functions of the `Option` type is clearer and
+ /// more concise than an `if let` expression.
+ ///
+ /// ### Known problems
+ /// This lint uses a deliberately conservative metric for checking
+ /// if the inside of either body contains breaks or continues which will
+ /// cause it to not suggest a fix if either block contains a loop with
+ /// continues or breaks contained within the loop.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let optional: Option<u32> = Some(0);
+ /// # fn do_complicated_function() -> u32 { 5 };
+ /// let _ = if let Some(foo) = optional {
+ /// foo
+ /// } else {
+ /// 5
+ /// };
+ /// let _ = if let Some(foo) = optional {
+ /// foo
+ /// } else {
+ /// let y = do_complicated_function();
+ /// y*y
+ /// };
+ /// ```
+ ///
+ /// should be
+ ///
+ /// ```rust
+ /// # let optional: Option<u32> = Some(0);
+ /// # fn do_complicated_function() -> u32 { 5 };
+ /// let _ = optional.map_or(5, |foo| foo);
+ /// let _ = optional.map_or_else(||{
+ /// let y = do_complicated_function();
+ /// y*y
+ /// }, |foo| foo);
+ /// ```
+ pub OPTION_IF_LET_ELSE,
+ nursery,
+ "reimplementation of Option::map_or"
+}
+
+declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
+
+/// Returns true iff the given expression is the result of calling `Result::ok`
+fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
+ if let ExprKind::MethodCall(path, _, &[ref receiver], _) = &expr.kind {
+ path.ident.name.as_str() == "ok"
+ && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result)
+ } else {
+ false
+ }
+}
+
+/// A struct containing information about occurrences of the
+/// `if let Some(..) = .. else` construct that this lint detects.
+struct OptionIfLetElseOccurence {
+ option: String,
+ method_sugg: String,
+ some_expr: String,
+ none_expr: String,
+}
+
+/// Extracts the body of a given arm. If the arm contains only an expression,
+/// then it returns the expression. Otherwise, it returns the entire block
+fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
+ if let ExprKind::Block(
+ Block {
+ stmts: block_stmts,
+ expr: Some(block_expr),
+ ..
+ },
+ _,
+ ) = expr.kind
+ {
+ if let [] = block_stmts {
+ Some(block_expr)
+ } else {
+ Some(expr)
+ }
+ } else {
+ None
+ }
+}
+
+fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
+ format!(
+ "{}{}",
+ Sugg::hir(cx, cond_expr, "..").maybe_par(),
+ if as_mut {
+ ".as_mut()"
+ } else if as_ref {
+ ".as_ref()"
+ } else {
+ ""
+ }
+ )
+}
+
+/// If this expression is the option if let/else construct we're detecting, then
+/// this function returns an `OptionIfLetElseOccurence` struct with details if
+/// this construct is found, or None if this construct is not found.
+fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
+ if_chain! {
+ if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
+ if !in_constant(cx, expr.hir_id);
+ if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
+ = higher::IfLet::hir(cx, expr);
+ if !is_else_clause(cx.tcx, expr);
+ if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
+ if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
+ if is_lang_ctor(cx, struct_qpath, OptionSome);
+ if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
+ if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
+ if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
+ if some_captures
+ .iter()
+ .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
+ .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
+
+ then {
+ let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
+ let some_body = extract_body_from_expr(if_then)?;
+ let none_body = extract_body_from_expr(if_else)?;
+ let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) {
+ "map_or"
+ } else {
+ "map_or_else"
+ };
+ let capture_name = id.name.to_ident_string();
+ let (as_ref, as_mut) = match &let_expr.kind {
+ ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
+ ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
+ _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
+ };
+ let cond_expr = match let_expr.kind {
+ // Pointer dereferencing happens automatically, so we can omit it in the suggestion
+ ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
+ _ => let_expr,
+ };
+ // Check if captures the closure will need conflict with borrows made in the scrutinee.
+ // TODO: check all the references made in the scrutinee expression. This will require interacting
+ // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
+ if as_ref || as_mut {
+ let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
+ ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
+ _ => None,
+ });
+ if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
+ match some_captures.get(local_id)
+ .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id)))
+ {
+ Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
+ Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
+ Some(CaptureKind::Ref(Mutability::Not)) | None => (),
+ }
+ }
+ }
+ Some(OptionIfLetElseOccurence {
+ option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
+ method_sugg: method_sugg.to_string(),
+ some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")),
+ none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
+ })
+ } else {
+ None
+ }
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+ if let Some(detection) = detect_option_if_let_else(cx, expr) {
+ span_lint_and_sugg(
+ cx,
+ OPTION_IF_LET_ELSE,
+ expr.span,
+ format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
+ "try",
+ format!(
+ "{}.{}({}, {})",
+ detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+}
--- /dev/null
- use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_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;
- 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 clippy_utils::ty::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::def::Res;
+use rustc_hir::{
- use rustc_middle::ty;
++ BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, Impl, ImplItem, ImplItemKind, Item, ItemKind,
++ Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
- check_fn(cx, sig.decl, item.hir_id(), Some(body_id));
+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); }
+ /// ```
+ ///
+ /// ```ignore
+ /// // 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));
++ check_fn(cx, sig.decl, 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(), body_id);
++ check_fn(cx, sig.decl, 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
+ };
- 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();
++ check_fn(cx, sig.decl, 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)]
- for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
++fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, opt_body_id: Option<BodyId>) {
+ let body = opt_body_id.map(|id| cx.tcx.hir().body(id));
+
- if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
- if is_type_diagnostic_item(cx, ty, sym::Vec) {
++ for (idx, arg) in decl.inputs.iter().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;
+ }
+ }
+
- } else if is_type_diagnostic_item(cx, ty, sym::String) {
++ let (item_name, path) = if_chain! {
++ if let TyKind::Rptr(_, MutTy { ty, mutbl: Mutability::Not }) = arg.kind;
++ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
++ if let Res::Def(_, did) = path.res;
++ if let Some(item_name) = cx.tcx.get_diagnostic_name(did);
++ then {
++ (item_name, path)
++ } else {
++ continue
++ }
++ };
++
++ match item_name {
++ sym::Vec => {
+ 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::PathBuf) {
++ },
++ sym::String => {
+ 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 match_type(cx, ty, &paths::COW) {
++ },
++ 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,
+ );
+ }
+ },
+ );
+ }
- if let TyKind::Rptr(_, MutTy { ty, ..} ) = arg.kind;
- if let TyKind::Path(QPath::Resolved(None, pp)) = ty.kind;
- if let [ref bx] = *pp.segments;
++ },
++ sym::Cow => {
+ if_chain! {
- }
++ if let [ref bx] = *path.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
- fn expression_returns_unmodified_err(
- cx: &LateContext<'_>,
- expression: &Expr<'_>,
- origin_hir_id: &Expr<'_>,
- ) -> bool {
- match expression.kind {
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::is_lang_ctor;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
+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 expressions that could be replaced by the question mark operator.
+ ///
+ /// ### Why is this bad?
+ /// Question mark usage is more idiomatic.
+ ///
+ /// ### Example
+ /// ```ignore
+ /// if option.is_none() {
+ /// return None;
+ /// }
+ /// ```
+ ///
+ /// Could be written:
+ ///
+ /// ```ignore
+ /// option?;
+ /// ```
+ pub QUESTION_MARK,
+ style,
+ "checks for expressions that could be replaced by the question mark operator"
+}
+
+declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
+
+impl QuestionMark {
+ /// Checks if the given expression on the given context matches the following structure:
+ ///
+ /// ```ignore
+ /// if option.is_none() {
+ /// return None;
+ /// }
+ /// ```
+ ///
+ /// ```ignore
+ /// if result.is_err() {
+ /// return result;
+ /// }
+ /// ```
+ ///
+ /// If it matches, it will suggest to use the question mark operator instead
+ fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
+ if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind;
+ if let Some(subject) = args.get(0);
+ if (Self::option_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_none)) ||
+ (Self::result_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_err));
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
+ let mut replacement: Option<String> = None;
+ if let Some(else_inner) = r#else {
+ if_chain! {
+ if let ExprKind::Block(block, None) = &else_inner.kind;
+ if block.stmts.is_empty();
+ if let Some(block_expr) = &block.expr;
+ if eq_expr_value(cx, subject, block_expr);
+ then {
+ replacement = Some(format!("Some({}?)", receiver_str));
+ }
+ }
+ } else if Self::moves_by_default(cx, subject)
+ && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
+ {
+ replacement = Some(format!("{}.as_ref()?;", receiver_str));
+ } else {
+ replacement = Some(format!("{}?;", receiver_str));
+ }
+
+ if let Some(replacement_str) = replacement {
+ span_lint_and_sugg(
+ cx,
+ QUESTION_MARK,
+ expr.span,
+ "this block may be rewritten with the `?` operator",
+ "replace it with",
+ replacement_str,
+ applicability,
+ );
+ }
+ }
+ }
+ }
+
+ fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
+ = higher::IfLet::hir(cx, expr);
+ if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
+ if (Self::option_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, OptionSome)) ||
+ (Self::result_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, ResultOk));
+
+ if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
+ let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
+ if let ExprKind::Block(block, None) = if_then.kind;
+ if block.stmts.is_empty();
+ if let Some(trailing_expr) = &block.expr;
+ if path_to_local_id(trailing_expr, bind_id);
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
+ let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },);
+
+ span_lint_and_sugg(
+ cx,
+ QUESTION_MARK,
+ expr.span,
+ "this if-let-else may be rewritten with the `?` operator",
+ "replace it with",
+ replacement,
+ applicability,
+ );
+ }
+ }
+ }
+
+ fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
+ Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(cx, nested_expr, expr)
+ }
+
+ fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
+ Self::is_option(cx, expr) && Self::expression_returns_none(cx, nested_expr)
+ }
+
+ fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
+ let expr_ty = cx.typeck_results().expr_ty(expression);
+
+ !expr_ty.is_copy_modulo_regions(cx.tcx.at(expression.span), cx.param_env)
+ }
+
+ fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
+ let expr_ty = cx.typeck_results().expr_ty(expression);
+
+ is_type_diagnostic_item(cx, expr_ty, sym::Option)
+ }
+
+ fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
+ let expr_ty = cx.typeck_results().expr_ty(expression);
+
+ is_type_diagnostic_item(cx, expr_ty, sym::Result)
+ }
+
+ fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
+ match expression.kind {
+ ExprKind::Block(block, _) => {
+ if let Some(return_expression) = Self::return_expression(block) {
+ return Self::expression_returns_none(cx, return_expression);
+ }
+
+ false
+ },
+ ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
+ ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
+ _ => false,
+ }
+ }
+
- return Self::expression_returns_unmodified_err(cx, return_expression, origin_hir_id);
++ fn expression_returns_unmodified_err(cx: &LateContext<'_>, expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool {
++ match expr.kind {
+ ExprKind::Block(block, _) => {
+ if let Some(return_expression) = Self::return_expression(block) {
- ExprKind::Ret(Some(expr)) | ExprKind::Call(expr, _) => {
- Self::expression_returns_unmodified_err(cx, expr, origin_hir_id)
- },
- ExprKind::Path(_) => path_to_local(expression) == path_to_local(origin_hir_id),
++ return Self::expression_returns_unmodified_err(cx, return_expression, cond_expr);
+ }
+
+ false
+ },
++ ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(cx, ret_expr, cond_expr),
++ ExprKind::Path(_) => path_to_local(expr) == path_to_local(cond_expr),
+ _ => false,
+ }
+ }
+
+ fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ // Check if last expression is a return statement. Then, return the expression
+ if_chain! {
+ if block.stmts.len() == 1;
+ if let Some(expr) = block.stmts.iter().last();
+ if let StmtKind::Semi(expr) = expr.kind;
+ if let ExprKind::Ret(Some(ret_expr)) = expr.kind;
+
+ then {
+ return Some(ret_expr);
+ }
+ }
+
+ // Check for `return` without a semicolon.
+ if_chain! {
+ if block.stmts.is_empty();
+ if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
+ then {
+ return Some(ret_expr);
+ }
+ }
+
+ None
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for QuestionMark {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ Self::check_is_none_or_err_and_early_return(cx, expr);
+ Self::check_if_let_some_or_err_and_early_return(cx, expr);
+ }
+}
--- /dev/null
- declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN]);
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::SpanlessEq;
+use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for string appends of the form `x = x + y` (without
+ /// `let`!).
+ ///
+ /// ### Why is this bad?
+ /// It's not really bad, but some people think that the
+ /// `.push_str(_)` method is more readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut x = "Hello".to_owned();
+ /// x = x + ", World";
+ ///
+ /// // More readable
+ /// x += ", World";
+ /// x.push_str(", World");
+ /// ```
+ pub STRING_ADD_ASSIGN,
+ pedantic,
+ "using `x = x + ..` where x is a `String` instead of `push_str()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for all instances of `x + _` where `x` is of type
+ /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not*
+ /// match.
+ ///
+ /// ### Why is this bad?
+ /// It's not bad in and of itself. However, this particular
+ /// `Add` implementation is asymmetric (the other operand need not be `String`,
+ /// but `x` does), while addition as mathematically defined is symmetric, also
+ /// the `String::push_str(_)` function is a perfectly good replacement.
+ /// Therefore, some dislike it and wish not to have it in their code.
+ ///
+ /// That said, other people think that string addition, having a long tradition
+ /// in other languages is actually fine, which is why we decided to make this
+ /// particular lint `allow` by default.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = "Hello".to_owned();
+ /// x + ", World";
+ /// ```
+ pub STRING_ADD,
+ restriction,
+ "using `x + ..` where x is a `String` instead of `push_str()`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the `as_bytes` method called on string literals
+ /// that contain only ASCII characters.
+ ///
+ /// ### Why is this bad?
+ /// Byte string literals (e.g., `b"foo"`) can be used
+ /// instead. They are shorter but less discoverable than `as_bytes()`.
+ ///
+ /// ### Known problems
+ /// `"str".as_bytes()` and the suggested replacement of `b"str"` are not
+ /// equivalent because they have different types. The former is `&[u8]`
+ /// while the latter is `&[u8; 3]`. That means in general they will have a
+ /// different set of methods and different trait implementations.
+ ///
+ /// ```compile_fail
+ /// fn f(v: Vec<u8>) {}
+ ///
+ /// f("...".as_bytes().to_owned()); // works
+ /// f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec<u8>
+ ///
+ /// fn g(r: impl std::io::Read) {}
+ ///
+ /// g("...".as_bytes()); // works
+ /// g(b"..."); // does not work
+ /// ```
+ ///
+ /// The actual equivalent of `"str".as_bytes()` with the same type is not
+ /// `b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not
+ /// more readable than a function call.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let bs = "a byte string".as_bytes();
+ ///
+ /// // Good
+ /// let bs = b"a byte string";
+ /// ```
+ pub STRING_LIT_AS_BYTES,
+ nursery,
+ "calling `as_bytes` on a string literal instead of using a byte string literal"
+}
+
-
- if let ExprKind::Binary(
- Spanned {
- node: BinOpKind::Add, ..
- },
- left,
- _,
- ) = e.kind
- {
- if is_string(cx, left) {
- if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
- let parent = get_parent_expr(cx, e);
- if let Some(p) = parent {
- if let ExprKind::Assign(target, _, _) = p.kind {
- // avoid duplicate matches
- if SpanlessEq::new(cx).eq_expr(target, left) {
- return;
++declare_clippy_lint! {
++ /// ### What it does
++ /// Checks for slice operations on strings
++ ///
++ /// ### Why is this bad?
++ /// UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character
++ /// counts and string indices. This may lead to panics, and should warrant some test cases
++ /// containing wide UTF-8 characters. This lint is most useful in code that should avoid
++ /// panics at all costs.
++ ///
++ /// ### Known problems
++ /// Probably lots of false positives. If an index comes from a known valid position (e.g.
++ /// obtained via `char_indices` over the same string), it is totally OK.
++ ///
++ /// # Example
++ /// ```rust,should_panic
++ /// &"Ölkanne"[1..];
++ /// ```
++ pub STRING_SLICE,
++ restriction,
++ "slicing a string"
++}
++
++declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN, STRING_SLICE]);
+
+impl<'tcx> LateLintPass<'tcx> for StringAdd {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if in_external_macro(cx.sess(), e.span) {
+ return;
+ }
- span_lint(
- cx,
- STRING_ADD,
- e.span,
- "you added something to a string. Consider using `String::push_str()` instead",
- );
- }
- } else if let ExprKind::Assign(target, src, _) = e.kind {
- if is_string(cx, target) && is_add(cx, src, target) {
- span_lint(
- cx,
- STRING_ADD_ASSIGN,
- e.span,
- "you assigned the result of adding something to this string. Consider using \
- `String::push_str()` instead",
- );
- }
++ match e.kind {
++ ExprKind::Binary(
++ Spanned {
++ node: BinOpKind::Add, ..
++ },
++ left,
++ _,
++ ) => {
++ if is_string(cx, left) {
++ if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
++ let parent = get_parent_expr(cx, e);
++ if let Some(p) = parent {
++ if let ExprKind::Assign(target, _, _) = p.kind {
++ // avoid duplicate matches
++ if SpanlessEq::new(cx).eq_expr(target, left) {
++ return;
++ }
+ }
+ }
+ }
++ span_lint(
++ cx,
++ STRING_ADD,
++ e.span,
++ "you added something to a string. Consider using `String::push_str()` instead",
++ );
+ }
++ },
++ ExprKind::Assign(target, src, _) => {
++ if is_string(cx, target) && is_add(cx, src, target) {
++ span_lint(
++ cx,
++ STRING_ADD_ASSIGN,
++ e.span,
++ "you assigned the result of adding something to this string. Consider using \
++ `String::push_str()` instead",
++ );
++ }
++ },
++ ExprKind::Index(target, _idx) => {
++ let e_ty = cx.typeck_results().expr_ty(target).peel_refs();
++ if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) {
++ span_lint(
++ cx,
++ STRING_SLICE,
++ e.span,
++ "indexing into a string may panic if the index is within a UTF-8 character",
++ );
++ }
++ },
++ _ => {},
+ }
+ }
+}
+
+fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
+}
+
+fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
+ match src.kind {
+ ExprKind::Binary(
+ Spanned {
+ node: BinOpKind::Add, ..
+ },
+ left,
+ _,
+ ) => SpanlessEq::new(cx).eq_expr(target, left),
+ ExprKind::Block(block, _) => {
+ block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
+ },
+ _ => false,
+ }
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Check if the string is transformed to byte array and casted back to string.
+ ///
+ /// ### Why is this bad?
+ /// It's unnecessary, the string can be used directly.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
+ /// ```
+ /// could be written as
+ /// ```rust
+ /// let _ = &"Hello World!"[6..11];
+ /// ```
+ pub STRING_FROM_UTF8_AS_BYTES,
+ complexity,
+ "casting string slices to byte slices and back"
+}
+
+// Max length a b"foo" string can take
+const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
+
+declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]);
+
+impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ use rustc_ast::LitKind;
+
+ if_chain! {
+ // Find std::str::converts::from_utf8
+ if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8);
+
+ // Find string::as_bytes
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind;
+ if let ExprKind::Index(left, right) = args.kind;
+ let (method_names, expressions, _) = method_calls(left, 1);
+ if method_names.len() == 1;
+ if expressions.len() == 1;
+ if expressions[0].len() == 1;
+ if method_names[0] == sym!(as_bytes);
+
+ // Check for slicer
+ if let ExprKind::Struct(QPath::LangItem(LangItem::Range, _), _, _) = right.kind;
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let string_expression = &expressions[0][0];
+
+ let snippet_app = snippet_with_applicability(
+ cx,
+ string_expression.span, "..",
+ &mut applicability,
+ );
+
+ span_lint_and_sugg(
+ cx,
+ STRING_FROM_UTF8_AS_BYTES,
+ e.span,
+ "calling a slice of `as_bytes()` with `from_utf8` should be not necessary",
+ "try",
+ format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")),
+ applicability
+ )
+ }
+ }
+
+ if_chain! {
+ if let ExprKind::MethodCall(path, _, args, _) = &e.kind;
+ if path.ident.name == sym!(as_bytes);
+ if let ExprKind::Lit(lit) = &args[0].kind;
+ if let LitKind::Str(lit_content, _) = &lit.node;
+ then {
+ let callsite = snippet(cx, args[0].span.source_callsite(), r#""foo""#);
+ let mut applicability = Applicability::MachineApplicable;
+ if callsite.starts_with("include_str!") {
+ span_lint_and_sugg(
+ cx,
+ STRING_LIT_AS_BYTES,
+ e.span,
+ "calling `as_bytes()` on `include_str!(..)`",
+ "consider using `include_bytes!(..)` instead",
+ snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability).replacen(
+ "include_str",
+ "include_bytes",
+ 1,
+ ),
+ applicability,
+ );
+ } else if lit_content.as_str().is_ascii()
+ && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
+ && !args[0].span.from_expansion()
+ {
+ span_lint_and_sugg(
+ cx,
+ STRING_LIT_AS_BYTES,
+ e.span,
+ "calling `as_bytes()` on a string literal",
+ "consider using a byte string literal instead",
+ format!(
+ "b{}",
+ snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability)
+ ),
+ applicability,
+ );
+ }
+ }
+ }
+
+ if_chain! {
+ if let ExprKind::MethodCall(path, _, [recv], _) = &e.kind;
+ if path.ident.name == sym!(into_bytes);
+ if let ExprKind::MethodCall(path, _, [recv], _) = &recv.kind;
+ if matches!(&*path.ident.name.as_str(), "to_owned" | "to_string");
+ if let ExprKind::Lit(lit) = &recv.kind;
+ if let LitKind::Str(lit_content, _) = &lit.node;
+
+ if lit_content.as_str().is_ascii();
+ if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT;
+ if !recv.span.from_expansion();
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+
+ span_lint_and_sugg(
+ cx,
+ STRING_LIT_AS_BYTES,
+ e.span,
+ "calling `into_bytes()` on a string literal",
+ "consider using a byte string literal instead",
+ format!(
+ "b{}.to_vec()",
+ snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability)
+ ),
+ applicability,
+ );
+ }
+ }
+ }
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// This lint checks for `.to_string()` method calls on values of type `&str`.
+ ///
+ /// ### Why is this bad?
+ /// The `to_string` method is also used on other types to convert them to a string.
+ /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better
+ /// expressed with `.to_owned()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // example code where clippy issues a warning
+ /// let _ = "str".to_string();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// // example code which does not raise clippy warning
+ /// let _ = "str".to_owned();
+ /// ```
+ pub STR_TO_STRING,
+ restriction,
+ "using `to_string()` on a `&str`, which should be `to_owned()`"
+}
+
+declare_lint_pass!(StrToString => [STR_TO_STRING]);
+
+impl LateLintPass<'_> for StrToString {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ if_chain! {
+ if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
+ if path.ident.name == sym!(to_string);
+ let ty = cx.typeck_results().expr_ty(self_arg);
+ if let ty::Ref(_, ty, ..) = ty.kind();
+ if *ty.kind() == ty::Str;
+ then {
+ span_lint_and_help(
+ cx,
+ STR_TO_STRING,
+ expr.span,
+ "`to_string()` called on a `&str`",
+ None,
+ "consider using `.to_owned()`",
+ );
+ }
+ }
+ }
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// This lint checks for `.to_string()` method calls on values of type `String`.
+ ///
+ /// ### Why is this bad?
+ /// The `to_string` method is also used on other types to convert them to a string.
+ /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // example code where clippy issues a warning
+ /// let msg = String::from("Hello World");
+ /// let _ = msg.to_string();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// // example code which does not raise clippy warning
+ /// let msg = String::from("Hello World");
+ /// let _ = msg.clone();
+ /// ```
+ pub STRING_TO_STRING,
+ restriction,
+ "using `to_string()` on a `String`, which should be `clone()`"
+}
+
+declare_lint_pass!(StringToString => [STRING_TO_STRING]);
+
+impl LateLintPass<'_> for StringToString {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ if_chain! {
+ if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
+ if path.ident.name == sym!(to_string);
+ let ty = cx.typeck_results().expr_ty(self_arg);
+ if is_type_diagnostic_item(cx, ty, sym::String);
+ then {
+ span_lint_and_help(
+ cx,
+ STRING_TO_STRING,
+ expr.span,
+ "`to_string()` called on a `String`",
+ None,
+ "consider using `.clone()`",
+ );
+ }
+ }
+ }
+}
--- /dev/null
- let lex_start = (between_span.lo().0 + 1) as usize;
- let src_str = source_file.src.as_ref()?[lex_start..between_span.hi().0 as usize].to_string();
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{indent_of, reindent_multiline, snippet};
+use clippy_utils::{in_macro, is_lint_allowed};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource};
+use rustc_lexer::TokenKind;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{BytePos, Span};
+use std::borrow::Cow;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `unsafe` blocks without a `// Safety: ` comment
+ /// explaining why the unsafe operations performed inside
+ /// the block are safe.
+ ///
+ /// ### Why is this bad?
+ /// Undocumented unsafe blocks can make it difficult to
+ /// read and maintain code, as well as uncover unsoundness
+ /// and bugs.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::ptr::NonNull;
+ /// let a = &mut 42;
+ ///
+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::ptr::NonNull;
+ /// let a = &mut 42;
+ ///
+ /// // Safety: references are guaranteed to be non-null.
+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
+ /// ```
+ pub UNDOCUMENTED_UNSAFE_BLOCKS,
+ restriction,
+ "creating an unsafe block without explaining why it is safe"
+}
+
+impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
+
+#[derive(Default)]
+pub struct UndocumentedUnsafeBlocks {
+ pub local_level: u32,
+ pub local_span: Option<Span>,
+ // The local was already checked for an overall safety comment
+ // There is no need to continue checking the blocks in the local
+ pub local_checked: bool,
+ // Since we can only check the blocks from expanded macros
+ // We have to omit the suggestion due to the actual definition
+ // Not being available to us
+ pub macro_expansion: bool,
+}
+
+impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
+ fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
+ if_chain! {
+ if !self.local_checked;
+ if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id);
+ if !in_external_macro(cx.tcx.sess, block.span);
+ if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules;
+ if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id);
+ if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false);
+ then {
+ let mut span = block.span;
+
+ if let Some(local_span) = self.local_span {
+ span = local_span;
+
+ let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span);
+
+ if result.unwrap_or(true) {
+ self.local_checked = true;
+ return;
+ }
+ }
+
+ self.lint(cx, span);
+ }
+ }
+ }
+
+ fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) {
+ if_chain! {
+ if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id);
+ if !in_external_macro(cx.tcx.sess, local.span);
+ if let Some(init) = local.init;
+ then {
+ self.visit_expr(init);
+
+ if self.local_level > 0 {
+ self.local_span = Some(local.span);
+ }
+ }
+ }
+ }
+
+ fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) {
+ self.local_level = self.local_level.saturating_sub(1);
+
+ if self.local_level == 0 {
+ self.local_checked = false;
+ self.local_span = None;
+ }
+ }
+}
+
+impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks {
+ type Map = Map<'hir>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, ex: &'v Expr<'v>) {
+ match ex.kind {
+ ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1),
+ _ => walk_expr(self, ex),
+ }
+ }
+}
+
+impl UndocumentedUnsafeBlocks {
+ fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option<bool> {
+ let map = tcx.hir();
+ let source_map = tcx.sess.source_map();
+
+ let enclosing_scope_span = map.opt_span(enclosing_hir_id)?;
+
+ let between_span = if in_macro(block_span) {
+ self.macro_expansion = true;
+ enclosing_scope_span.with_hi(block_span.hi())
+ } else {
+ self.macro_expansion = false;
+ enclosing_scope_span.to(block_span)
+ };
+
+ let file_name = source_map.span_to_filename(between_span);
+ let source_file = source_map.get_source_file(&file_name)?;
+
++ let lex_start = (between_span.lo().0 - source_file.start_pos.0 + 1) as usize;
++ let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize;
++ let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string();
+
+ let mut pos = 0;
+ let mut comment = false;
+
+ for token in rustc_lexer::tokenize(&src_str) {
+ match token.kind {
+ TokenKind::LineComment { doc_style: None }
+ | TokenKind::BlockComment {
+ doc_style: None,
+ terminated: true,
+ } => {
+ let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase();
+
+ if comment_str.contains("SAFETY:") {
+ comment = true;
+ }
+ },
+ // We need to add all whitespace to `pos` before checking the comment's line number
+ TokenKind::Whitespace => {},
+ _ => {
+ if comment {
+ // Get the line number of the "comment" (really wherever the trailing whitespace ended)
+ let comment_line_num = source_file
+ .lookup_file_pos_with_col_display(BytePos((lex_start + pos).try_into().unwrap()))
+ .0;
+ // Find the block/local's line number
+ let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
+
+ // Check the comment is immediately followed by the block/local
+ if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num {
+ return Some(true);
+ }
+
+ comment = false;
+ }
+ },
+ }
+
+ pos += token.len;
+ }
+
+ Some(false)
+ }
+
+ fn lint(&self, cx: &LateContext<'_>, mut span: Span) {
+ let source_map = cx.tcx.sess.source_map();
+
+ if source_map.is_multiline(span) {
+ span = source_map.span_until_char(span, '\n');
+ }
+
+ if self.macro_expansion {
+ span_lint_and_help(
+ cx,
+ UNDOCUMENTED_UNSAFE_BLOCKS,
+ span,
+ "unsafe block in macro expansion missing a safety comment",
+ None,
+ "consider adding a safety comment in the macro definition",
+ );
+ } else {
+ let block_indent = indent_of(cx, span);
+ let suggestion = format!("// Safety: ...\n{}", snippet(cx, span, ".."));
+
+ span_lint_and_sugg(
+ cx,
+ UNDOCUMENTED_UNSAFE_BLOCKS,
+ span,
+ "unsafe block missing a safety comment",
+ "consider adding a safety comment",
+ reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(),
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+}
--- /dev/null
- pedantic,
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lint_allowed;
+use clippy_utils::source::snippet;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, HirId};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use unicode_normalization::UnicodeNormalization;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for invisible Unicode characters in the code.
+ ///
+ /// ### Why is this bad?
+ /// Having an invisible character in the code makes for all
+ /// sorts of April fools, but otherwise is very much frowned upon.
+ ///
+ /// ### Example
+ /// You don't see it, but there may be a zero-width space or soft hyphen
+ /// somewhere in this text.
+ pub INVISIBLE_CHARACTERS,
+ correctness,
+ "using an invisible character in a string literal, which is confusing"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for non-ASCII characters in string literals.
+ ///
+ /// ### Why is this bad?
+ /// Yeah, we know, the 90's called and wanted their charset
+ /// back. Even so, there still are editors and other programs out there that
+ /// don't work well with Unicode. So if the code is meant to be used
+ /// internationally, on multiple operating systems, or has other portability
+ /// requirements, activating this lint could be useful.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = String::from("€");
+ /// ```
+ /// Could be written as:
+ /// ```rust
+ /// let x = String::from("\u{20ac}");
+ /// ```
+ pub NON_ASCII_LITERAL,
++ restriction,
+ "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for string literals that contain Unicode in a form
+ /// that is not equal to its
+ /// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
+ ///
+ /// ### Why is this bad?
+ /// If such a string is compared to another, the results
+ /// may be surprising.
+ ///
+ /// ### Example
+ /// You may not see it, but "à"" and "à"" aren't the same string. The
+ /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
+ pub UNICODE_NOT_NFC,
+ pedantic,
+ "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
+}
+
+declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
+
+impl LateLintPass<'_> for Unicode {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
+ if let ExprKind::Lit(ref lit) = expr.kind {
+ if let LitKind::Str(_, _) = lit.node {
+ check_str(cx, lit.span, expr.hir_id);
+ }
+ }
+ }
+}
+
+fn escape<T: Iterator<Item = char>>(s: T) -> String {
+ let mut result = String::new();
+ for c in s {
+ if c as u32 > 0x7F {
+ for d in c.escape_unicode() {
+ result.push(d);
+ }
+ } else {
+ result.push(c);
+ }
+ }
+ result
+}
+
+fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
+ let string = snippet(cx, span, "");
+ if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
+ span_lint_and_sugg(
+ cx,
+ INVISIBLE_CHARACTERS,
+ span,
+ "invisible character detected",
+ "consider replacing the string with",
+ string
+ .replace("\u{200B}", "\\u{200B}")
+ .replace("\u{ad}", "\\u{AD}")
+ .replace("\u{2060}", "\\u{2060}"),
+ Applicability::MachineApplicable,
+ );
+ }
+ if string.chars().any(|c| c as u32 > 0x7F) {
+ span_lint_and_sugg(
+ cx,
+ NON_ASCII_LITERAL,
+ span,
+ "literal non-ASCII character detected",
+ "consider replacing the string with",
+ if is_lint_allowed(cx, UNICODE_NOT_NFC, id) {
+ escape(string.chars())
+ } else {
+ escape(string.nfc())
+ },
+ Applicability::MachineApplicable,
+ );
+ }
+ if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
+ span_lint_and_sugg(
+ cx,
+ UNICODE_NOT_NFC,
+ span,
+ "non-NFC Unicode sequence detected",
+ "consider replacing the string with",
+ string.nfc().collect::<String>(),
+ Applicability::MachineApplicable,
+ );
+ }
+}
--- /dev/null
--- /dev/null
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::source::snippet;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
++
++declare_clippy_lint! {
++ /// ### What it does
++ /// Detects `().hash(_)`.
++ ///
++ /// ### Why is this bad?
++ /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
++ ///
++ /// ### Example
++ /// ```rust
++ /// # use std::hash::Hash;
++ /// # use std::collections::hash_map::DefaultHasher;
++ /// # enum Foo { Empty, WithValue(u8) }
++ /// # use Foo::*;
++ /// # let mut state = DefaultHasher::new();
++ /// # let my_enum = Foo::Empty;
++ /// match my_enum {
++ /// Empty => ().hash(&mut state),
++ /// WithValue(x) => x.hash(&mut state),
++ /// }
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// # use std::hash::Hash;
++ /// # use std::collections::hash_map::DefaultHasher;
++ /// # enum Foo { Empty, WithValue(u8) }
++ /// # use Foo::*;
++ /// # let mut state = DefaultHasher::new();
++ /// # let my_enum = Foo::Empty;
++ /// match my_enum {
++ /// Empty => 0_u8.hash(&mut state),
++ /// WithValue(x) => x.hash(&mut state),
++ /// }
++ /// ```
++ pub UNIT_HASH,
++ correctness,
++ "hashing a unit value, which does nothing"
++}
++declare_lint_pass!(UnitHash => [UNIT_HASH]);
++
++impl LateLintPass<'tcx> for UnitHash {
++ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
++ if_chain! {
++ if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
++ if name_ident.ident.name == sym::hash;
++ if let [recv, state_param] = args;
++ if cx.typeck_results().expr_ty(recv).is_unit();
++ then {
++ span_lint_and_then(
++ cx,
++ UNIT_HASH,
++ expr.span,
++ "this call to `hash` on the unit type will do nothing",
++ |diag| {
++ diag.span_suggestion(
++ expr.span,
++ "remove the call to `hash` or consider using",
++ format!(
++ "0_u8.hash({})",
++ snippet(cx, state_param.span, ".."),
++ ),
++ Applicability::MaybeIncorrect,
++ );
++ diag.note("the implementation of `Hash` for `()` is a no-op");
++ }
++ );
++ }
++ }
++ }
++}
--- /dev/null
- /// Checks for functions of type Result that contain `expect()` or `unwrap()`
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{method_chain_args, return_ty};
+use if_chain::if_chain;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{Expr, ImplItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
++ /// Checks for functions of type `Result` that contain `expect()` or `unwrap()`
+ ///
+ /// ### Why is this bad?
+ /// These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
+ ///
+ /// ### Known problems
+ /// This can cause false positives in functions that handle both recoverable and non recoverable errors.
+ ///
+ /// ### Example
+ /// Before:
+ /// ```rust
+ /// fn divisible_by_3(i_str: String) -> Result<(), String> {
+ /// let i = i_str
+ /// .parse::<i32>()
+ /// .expect("cannot divide the input by three");
+ ///
+ /// if i % 3 != 0 {
+ /// Err("Number is not divisible by 3")?
+ /// }
+ ///
+ /// Ok(())
+ /// }
+ /// ```
+ ///
+ /// After:
+ /// ```rust
+ /// fn divisible_by_3(i_str: String) -> Result<(), String> {
+ /// let i = i_str
+ /// .parse::<i32>()
+ /// .map_err(|e| format!("cannot divide the input by three: {}", e))?;
+ ///
+ /// if i % 3 != 0 {
+ /// Err("Number is not divisible by 3")?
+ /// }
+ ///
+ /// Ok(())
+ /// }
+ /// ```
+ pub UNWRAP_IN_RESULT,
+ restriction,
+ "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
+}
+
+declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
+
+impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
+ if_chain! {
+ // first check if it's a method or function
+ if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
+ // checking if its return type is `result` or `option`
+ if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Result)
+ || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Option);
+ then {
+ lint_impl_body(cx, impl_item.span, impl_item);
+ }
+ }
+ }
+}
+
+struct FindExpectUnwrap<'a, 'tcx> {
+ lcx: &'a LateContext<'tcx>,
+ typeck_results: &'tcx ty::TypeckResults<'tcx>,
+ result: Vec<Span>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ // check for `expect`
+ if let Some(arglists) = method_chain_args(expr, &["expect"]) {
+ let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
+ if is_type_diagnostic_item(self.lcx, reciever_ty, sym::Option)
+ || is_type_diagnostic_item(self.lcx, reciever_ty, sym::Result)
+ {
+ self.result.push(expr.span);
+ }
+ }
+
+ // check for `unwrap`
+ if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
+ let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
+ if is_type_diagnostic_item(self.lcx, reciever_ty, sym::Option)
+ || is_type_diagnostic_item(self.lcx, reciever_ty, sym::Result)
+ {
+ self.result.push(expr.span);
+ }
+ }
+
+ // and check sub-expressions
+ intravisit::walk_expr(self, expr);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
+ if let ImplItemKind::Fn(_, body_id) = impl_item.kind {
+ let body = cx.tcx.hir().body(body_id);
+ let mut fpu = FindExpectUnwrap {
+ lcx: cx,
+ typeck_results: cx.tcx.typeck(impl_item.def_id),
+ result: Vec::new(),
+ };
+ fpu.visit_expr(&body.value);
+
+ // if we've found one, lint
+ if !fpu.result.is_empty() {
+ span_lint_and_then(
+ cx,
+ UNWRAP_IN_RESULT,
+ impl_span,
+ "used unwrap or expect in a function that returns result or option",
+ move |diag| {
+ diag.help("unwrap and expect should not be used in a function that returns result or option");
+ diag.span_note(fpu.result, "potential non-recoverable error(s)");
+ },
+ );
+ }
+ }
+}
--- /dev/null
- /// Lint: RESTRICTED_SCRIPTS.
+//! 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> },
+}
+
+/// A single disallowed type, used by the `DISALLOWED_TYPE` lint.
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum DisallowedType {
+ Simple(String),
+ WithReason { path: String, reason: Option<String> },
+}
+
+/// Conf with parse errors
+#[derive(Default)]
+pub struct TryConf {
+ pub conf: Conf,
+ pub errors: Vec<String>,
+}
+
+impl TryConf {
+ fn from_error(error: impl Error) -> Self {
+ Self {
+ conf: Conf::default(),
+ errors: vec![error.to_string()],
+ }
+ }
+}
+
+macro_rules! define_Conf {
+ ($(
+ $(#[doc = $doc:literal])+
+ $(#[conf_deprecated($dep:literal)])?
+ ($name:ident: $ty:ty = $default:expr),
+ )*) => {
+ /// Clippy lint configuration
+ pub struct Conf {
+ $($(#[doc = $doc])+ pub $name: $ty,)*
+ }
+
+ mod defaults {
+ $(pub fn $name() -> $ty { $default })*
+ }
+
+ impl Default for Conf {
+ fn default() -> Self {
+ Self { $($name: defaults::$name(),)* }
+ }
+ }
+
+ impl<'de> Deserialize<'de> for TryConf {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+ deserializer.deserialize_map(ConfVisitor)
+ }
+ }
+
+ #[derive(Deserialize)]
+ #[serde(field_identifier, rename_all = "kebab-case")]
+ #[allow(non_camel_case_types)]
+ enum Field { $($name,)* third_party, }
+
+ struct ConfVisitor;
+
+ impl<'de> Visitor<'de> for ConfVisitor {
+ type Value = TryConf;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("Conf")
+ }
+
+ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
+ let mut errors = Vec::new();
+ $(let mut $name = None;)*
+ // could get `Field` here directly, but get `str` first for diagnostics
+ while let Some(name) = map.next_key::<&str>()? {
+ match Field::deserialize(name.into_deserializer())? {
+ $(Field::$name => {
+ $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
+ match map.next_value() {
+ Err(e) => errors.push(e.to_string()),
+ Ok(value) => match $name {
+ Some(_) => errors.push(format!("duplicate field `{}`", name)),
+ None => $name = Some(value),
+ }
+ }
+ })*
+ // white-listed; ignore
+ Field::third_party => drop(map.next_value::<IgnoredAny>())
+ }
+ }
+ let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
+ Ok(TryConf { conf, errors })
+ }
+ }
+
+ #[cfg(feature = "metadata-collector-lint")]
+ pub mod metadata {
+ use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
+
+ macro_rules! wrap_option {
+ () => (None);
+ ($x:literal) => (Some($x));
+ }
+
+ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
+ vec![
+ $(
+ {
+ let deprecation_reason = wrap_option!($($dep)?);
+
+ ClippyConfiguration::new(
+ stringify!($name),
+ stringify!($ty),
+ format!("{:?}", super::defaults::$name()),
+ concat!($($doc, '\n',)*),
+ deprecation_reason,
+ )
+ },
+ )+
+ ]
+ }
+ }
+ };
+}
+
+define_Conf! {
+ /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+ ///
+ /// Suppress lints whenever the suggested change would cause breakage for other crates.
+ (avoid_breaking_exported_api: bool = true),
+ /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT.
+ ///
+ /// The minimum rust version that the project supports
+ (msrv: Option<String> = None),
+ /// Lint: BLACKLISTED_NAME.
+ ///
+ /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
+ (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
+ /// Lint: COGNITIVE_COMPLEXITY.
+ ///
+ /// The maximum cognitive complexity a function can have
+ (cognitive_complexity_threshold: u64 = 25),
+ /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
+ ///
+ /// Use the Cognitive Complexity lint instead.
+ #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
+ (cyclomatic_complexity_threshold: Option<u64> = None),
+ /// Lint: DOC_MARKDOWN.
+ ///
+ /// The list of words this lint should not consider as identifiers needing ticks
+ (doc_valid_idents: Vec<String> = [
+ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
+ "DirectX",
+ "ECMAScript",
+ "GPLv2", "GPLv3",
+ "GitHub", "GitLab",
+ "IPv4", "IPv6",
+ "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
+ "NaN", "NaNs",
+ "OAuth", "GraphQL",
+ "OCaml",
+ "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
+ "WebGL",
+ "TensorFlow",
+ "TrueType",
+ "iOS", "macOS", "FreeBSD",
+ "TeX", "LaTeX", "BibTeX", "BibLaTeX",
+ "MinGW",
+ "CamelCase",
+ ].iter().map(ToString::to_string).collect()),
+ /// Lint: TOO_MANY_ARGUMENTS.
+ ///
+ /// The maximum number of argument a function or method can have
+ (too_many_arguments_threshold: u64 = 7),
+ /// Lint: TYPE_COMPLEXITY.
+ ///
+ /// The maximum complexity a type can have
+ (type_complexity_threshold: u64 = 250),
+ /// Lint: MANY_SINGLE_CHAR_NAMES.
+ ///
+ /// The maximum number of single char bindings a scope may have
+ (single_char_binding_names_threshold: u64 = 4),
+ /// Lint: BOXED_LOCAL, USELESS_VEC.
+ ///
+ /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
+ (too_large_for_stack: u64 = 200),
+ /// Lint: ENUM_VARIANT_NAMES.
+ ///
+ /// The minimum number of enum variants for the lints about variant names to trigger
+ (enum_variant_name_threshold: u64 = 3),
+ /// Lint: LARGE_ENUM_VARIANT.
+ ///
+ /// The maximum size of an enum's variant to avoid box suggestion
+ (enum_variant_size_threshold: u64 = 200),
+ /// Lint: VERBOSE_BIT_MASK.
+ ///
+ /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
+ (verbose_bit_mask_threshold: u64 = 1),
+ /// Lint: DECIMAL_LITERAL_REPRESENTATION.
+ ///
+ /// The lower bound for linting decimal literals
+ (literal_representation_threshold: u64 = 16384),
+ /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
+ ///
+ /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
+ (trivial_copy_size_limit: Option<u64> = None),
+ /// Lint: LARGE_TYPE_PASS_BY_MOVE.
+ ///
+ /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
+ (pass_by_value_size_limit: u64 = 256),
+ /// Lint: TOO_MANY_LINES.
+ ///
+ /// The maximum number of lines a function or method can have
+ (too_many_lines_threshold: u64 = 100),
+ /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
+ ///
+ /// The maximum allowed size for arrays on the stack
+ (array_size_threshold: u64 = 512_000),
+ /// Lint: VEC_BOX.
+ ///
+ /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
+ (vec_box_size_threshold: u64 = 4096),
+ /// Lint: TYPE_REPETITION_IN_BOUNDS.
+ ///
+ /// The maximum number of bounds a trait can have to be linted
+ (max_trait_bounds: u64 = 3),
+ /// Lint: STRUCT_EXCESSIVE_BOOLS.
+ ///
+ /// The maximum number of bool fields a struct can have
+ (max_struct_bools: u64 = 3),
+ /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
+ ///
+ /// The maximum number of bool parameters a function can have
+ (max_fn_params_bools: u64 = 3),
+ /// Lint: WILDCARD_IMPORTS.
+ ///
+ /// Whether to allow certain wildcard imports (prelude, super in tests).
+ (warn_on_all_wildcard_imports: bool = false),
+ /// Lint: DISALLOWED_METHOD.
+ ///
+ /// The list of disallowed methods, written as fully qualified paths.
+ (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
+ /// Lint: DISALLOWED_TYPE.
+ ///
+ /// The list of disallowed types, written as fully qualified paths.
+ (disallowed_types: Vec<crate::utils::conf::DisallowedType> = 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()),
- (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
++ /// Lint: DISALLOWED_SCRIPT_IDENTS.
+ ///
+ /// The list of unicode scripts allowed to be used in the scope.
++ (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()),
+ /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
+ ///
+ /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
+ (enable_raw_pointer_heuristic_for_send: bool = true),
+}
+
+/// Search for the configuration file.
+pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
+ /// Possible filename to search for.
+ const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
+
+ // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
+ // If neither of those exist, use ".".
+ let mut current = env::var_os("CLIPPY_CONF_DIR")
+ .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
+ .map_or_else(|| PathBuf::from("."), PathBuf::from);
+ loop {
+ for config_file_name in &CONFIG_FILE_NAMES {
+ if let Ok(config_file) = current.join(config_file_name).canonicalize() {
+ match fs::metadata(&config_file) {
+ Err(e) if e.kind() == io::ErrorKind::NotFound => {},
+ Err(e) => return Err(e),
+ Ok(md) if md.is_dir() => {},
+ Ok(_) => return Ok(Some(config_file)),
+ }
+ }
+ }
+
+ // If the current directory has no parent, we're done searching.
+ if !current.pop() {
+ return Ok(None);
+ }
+ }
+}
+
+/// Read the `toml` configuration file.
+///
+/// In case of error, the function tries to continue as much as possible.
+pub fn read(path: &Path) -> TryConf {
+ let content = match fs::read_to_string(path) {
+ Err(e) => return TryConf::from_error(e),
+ Ok(content) => content,
+ };
+ toml::from_str(&content).unwrap_or_else(TryConf::from_error)
+}
--- /dev/null
- docs.push('\n');
+//! 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;
++ let mut is_code_block_rust = false;
+ for line in lines {
+ let line = line.as_str();
+ let line = &*line;
++
++ // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
++ if is_code_block_rust && line.trim_start().starts_with("# ") {
++ continue;
++ }
++
++ // The line should be represented in the lint list, even if it's just an empty line
++ docs.push('\n');
+ if let Some(info) = line.trim_start().strip_prefix("```") {
+ in_code_block = !in_code_block;
++ is_code_block_rust = false;
+ 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);
++
++ is_code_block_rust = lang == "rust";
+ 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
+#![allow(clippy::float_cmp)]
+
+use crate::{clip, is_direct_expn_of, sext, unsext};
+use if_chain::if_chain;
+use rustc_ast::ast::{self, LitFloatType, LitKind};
+use rustc_data_structures::sync::Lrc;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::ty::subst::{Subst, SubstsRef};
+use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
+use rustc_middle::{bug, span_bug};
+use rustc_span::symbol::Symbol;
+use std::cmp::Ordering::{self, Equal};
+use std::convert::TryInto;
+use std::hash::{Hash, Hasher};
+use std::iter;
+
+/// A `LitKind`-like enum to fold constant `Expr`s into.
+#[derive(Debug, Clone)]
+pub enum Constant {
+ /// A `String` (e.g., "abc").
+ Str(String),
+ /// A binary string (e.g., `b"abc"`).
+ Binary(Lrc<[u8]>),
+ /// A single `char` (e.g., `'a'`).
+ Char(char),
+ /// An integer's bit representation.
+ Int(u128),
+ /// An `f32`.
+ F32(f32),
+ /// An `f64`.
+ F64(f64),
+ /// `true` or `false`.
+ Bool(bool),
+ /// An array of constants.
+ Vec(Vec<Constant>),
+ /// Also an array, but with only one constant, repeated N times.
+ Repeat(Box<Constant>, u64),
+ /// A tuple of constants.
+ Tuple(Vec<Constant>),
+ /// A raw pointer.
+ RawPtr(u128),
+ /// A reference
+ Ref(Box<Constant>),
+ /// A literal with syntax error.
+ Err(Symbol),
+}
+
+impl PartialEq for Constant {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
+ (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
+ (&Self::Char(l), &Self::Char(r)) => l == r,
+ (&Self::Int(l), &Self::Int(r)) => l == r,
+ (&Self::F64(l), &Self::F64(r)) => {
+ // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
+ // `Fw32 == Fw64`, so don’t compare them.
+ // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
+ l.to_bits() == r.to_bits()
+ },
+ (&Self::F32(l), &Self::F32(r)) => {
+ // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
+ // `Fw32 == Fw64`, so don’t compare them.
+ // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
+ f64::from(l).to_bits() == f64::from(r).to_bits()
+ },
+ (&Self::Bool(l), &Self::Bool(r)) => l == r,
+ (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
+ (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
+ (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
+ // TODO: are there inter-type equalities?
+ _ => false,
+ }
+ }
+}
+
+impl Hash for Constant {
+ fn hash<H>(&self, state: &mut H)
+ where
+ H: Hasher,
+ {
+ std::mem::discriminant(self).hash(state);
+ match *self {
+ Self::Str(ref s) => {
+ s.hash(state);
+ },
+ Self::Binary(ref b) => {
+ b.hash(state);
+ },
+ Self::Char(c) => {
+ c.hash(state);
+ },
+ Self::Int(i) => {
+ i.hash(state);
+ },
+ Self::F32(f) => {
+ f64::from(f).to_bits().hash(state);
+ },
+ Self::F64(f) => {
+ f.to_bits().hash(state);
+ },
+ Self::Bool(b) => {
+ b.hash(state);
+ },
+ Self::Vec(ref v) | Self::Tuple(ref v) => {
+ v.hash(state);
+ },
+ Self::Repeat(ref c, l) => {
+ c.hash(state);
+ l.hash(state);
+ },
+ Self::RawPtr(u) => {
+ u.hash(state);
+ },
+ Self::Ref(ref r) => {
+ r.hash(state);
+ },
+ Self::Err(ref s) => {
+ s.hash(state);
+ },
+ }
+ }
+}
+
+impl Constant {
+ pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
+ match (left, right) {
+ (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
+ (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
+ (&Self::Int(l), &Self::Int(r)) => {
+ if let ty::Int(int_ty) = *cmp_type.kind() {
+ Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
+ } else {
+ Some(l.cmp(&r))
+ }
+ },
+ (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
+ (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
+ (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
+ (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r)
+ .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
+ .find(|r| r.map_or(true, |o| o != Ordering::Equal))
+ .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
+ (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
+ match Self::partial_cmp(tcx, cmp_type, lv, rv) {
+ Some(Equal) => Some(ls.cmp(rs)),
+ x => x,
+ }
+ },
+ (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
+ // TODO: are there any useful inter-type orderings?
+ _ => None,
+ }
+ }
++
++ /// Returns the integer value or `None` if `self` or `val_type` is not integer type.
++ pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> {
++ if let Constant::Int(const_int) = *self {
++ match *val_type.kind() {
++ ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
++ ty::Uint(_) => Some(FullInt::U(const_int)),
++ _ => None,
++ }
++ } else {
++ None
++ }
++ }
+}
+
+/// Parses a `LitKind` to a `Constant`.
+pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
+ match *lit {
+ LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
+ LitKind::Byte(b) => Constant::Int(u128::from(b)),
+ LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
+ LitKind::Char(c) => Constant::Char(c),
+ LitKind::Int(n, _) => Constant::Int(n),
+ LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
+ ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
+ ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
+ },
+ LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
+ ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
+ ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
+ _ => bug!(),
+ },
+ LitKind::Bool(b) => Constant::Bool(b),
+ LitKind::Err(s) => Constant::Err(s),
+ }
+}
+
+pub fn constant<'tcx>(
+ lcx: &LateContext<'tcx>,
+ typeck_results: &ty::TypeckResults<'tcx>,
+ e: &Expr<'_>,
+) -> Option<(Constant, bool)> {
+ let mut cx = ConstEvalLateContext {
+ lcx,
+ typeck_results,
+ param_env: lcx.param_env,
+ needed_resolution: false,
+ substs: lcx.tcx.intern_substs(&[]),
+ };
+ cx.expr(e).map(|cst| (cst, cx.needed_resolution))
+}
+
+pub fn constant_simple<'tcx>(
+ lcx: &LateContext<'tcx>,
+ typeck_results: &ty::TypeckResults<'tcx>,
+ e: &Expr<'_>,
+) -> Option<Constant> {
+ constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
+}
+
++pub fn constant_full_int(
++ lcx: &LateContext<'tcx>,
++ typeck_results: &ty::TypeckResults<'tcx>,
++ e: &Expr<'_>,
++) -> Option<FullInt> {
++ constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
++}
++
++#[derive(Copy, Clone, Debug, Eq)]
++pub enum FullInt {
++ S(i128),
++ U(u128),
++}
++
++impl PartialEq for FullInt {
++ #[must_use]
++ fn eq(&self, other: &Self) -> bool {
++ self.cmp(other) == Ordering::Equal
++ }
++}
++
++impl PartialOrd for FullInt {
++ #[must_use]
++ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
++ Some(self.cmp(other))
++ }
++}
++
++impl Ord for FullInt {
++ #[must_use]
++ fn cmp(&self, other: &Self) -> Ordering {
++ use FullInt::{S, U};
++
++ fn cmp_s_u(s: i128, u: u128) -> Ordering {
++ u128::try_from(s).map_or(Ordering::Less, |x| x.cmp(&u))
++ }
++
++ match (*self, *other) {
++ (S(s), S(o)) => s.cmp(&o),
++ (U(s), U(o)) => s.cmp(&o),
++ (S(s), U(o)) => cmp_s_u(s, o),
++ (U(s), S(o)) => cmp_s_u(o, s).reverse(),
++ }
++ }
++}
++
+/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
+pub fn constant_context<'a, 'tcx>(
+ lcx: &'a LateContext<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+) -> ConstEvalLateContext<'a, 'tcx> {
+ ConstEvalLateContext {
+ lcx,
+ typeck_results,
+ param_env: lcx.param_env,
+ needed_resolution: false,
+ substs: lcx.tcx.intern_substs(&[]),
+ }
+}
+
+pub struct ConstEvalLateContext<'a, 'tcx> {
+ lcx: &'a LateContext<'tcx>,
+ typeck_results: &'a ty::TypeckResults<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ needed_resolution: bool,
+ substs: SubstsRef<'tcx>,
+}
+
+impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
+ /// Simple constant folding: Insert an expression, get a constant or none.
+ pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
+ match e.kind {
+ ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
+ ExprKind::Block(block, _) => self.block(block),
+ ExprKind::Lit(ref lit) => {
+ if is_direct_expn_of(e.span, "cfg").is_some() {
+ None
+ } else {
+ Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
+ }
+ },
+ ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
+ ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
+ ExprKind::Repeat(value, _) => {
+ let n = match self.typeck_results.expr_ty(e).kind() {
+ ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
+ _ => span_bug!(e.span, "typeck error"),
+ };
+ self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
+ },
+ ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
+ UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
+ UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
+ UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
+ }),
+ ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
+ ExprKind::Binary(op, left, right) => self.binop(op, left, right),
+ ExprKind::Call(callee, args) => {
+ // We only handle a few const functions for now.
+ if_chain! {
+ if args.is_empty();
+ if let ExprKind::Path(qpath) = &callee.kind;
+ let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
+ if let Some(def_id) = res.opt_def_id();
+ let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
+ let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
+ if let ["core", "num", int_impl, "max_value"] = *def_path;
+ then {
+ let value = match int_impl {
+ "<impl i8>" => i8::MAX as u128,
+ "<impl i16>" => i16::MAX as u128,
+ "<impl i32>" => i32::MAX as u128,
+ "<impl i64>" => i64::MAX as u128,
+ "<impl i128>" => i128::MAX as u128,
+ _ => return None,
+ };
+ Some(Constant::Int(value))
+ }
+ else {
+ None
+ }
+ }
+ },
+ ExprKind::Index(arr, index) => self.index(arr, index),
+ ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
+ // TODO: add other expressions.
+ _ => None,
+ }
+ }
+
+ #[allow(clippy::cast_possible_wrap)]
+ fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+ use self::Constant::{Bool, Int};
+ match *o {
+ Bool(b) => Some(Bool(!b)),
+ Int(value) => {
+ let value = !value;
+ match *ty.kind() {
+ ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
+ ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
+ _ => None,
+ }
+ },
+ _ => None,
+ }
+ }
+
+ fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+ use self::Constant::{Int, F32, F64};
+ match *o {
+ Int(value) => {
+ let ity = match *ty.kind() {
+ ty::Int(ity) => ity,
+ _ => return None,
+ };
+ // sign extend
+ let value = sext(self.lcx.tcx, value, ity);
+ let value = value.checked_neg()?;
+ // clear unused bits
+ Some(Int(unsext(self.lcx.tcx, value, ity)))
+ },
+ F32(f) => Some(F32(-f)),
+ F64(f) => Some(F64(-f)),
+ _ => None,
+ }
+ }
+
+ /// Create `Some(Vec![..])` of all constants, unless there is any
+ /// non-constant part.
+ fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
+ vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
+ }
+
+ /// Lookup a possibly constant expression from an `ExprKind::Path`.
+ fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
+ let res = self.typeck_results.qpath_res(qpath, id);
+ match res {
+ Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
+ let substs = self.typeck_results.node_substs(id);
+ let substs = if self.substs.is_empty() {
+ substs
+ } else {
+ substs.subst(self.lcx.tcx, self.substs)
+ };
+
+ let result = self
+ .lcx
+ .tcx
+ .const_eval_resolve(
+ self.param_env,
+ ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
+ None,
+ )
+ .ok()
+ .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
+ let result = miri_to_const(result);
+ if result.is_some() {
+ self.needed_resolution = true;
+ }
+ result
+ },
+ // FIXME: cover all usable cases.
+ _ => None,
+ }
+ }
+
+ fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
+ let lhs = self.expr(lhs);
+ let index = self.expr(index);
+
+ match (lhs, index) {
+ (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
+ Some(Constant::F32(x)) => Some(Constant::F32(*x)),
+ Some(Constant::F64(x)) => Some(Constant::F64(*x)),
+ _ => None,
+ },
+ (Some(Constant::Vec(vec)), _) => {
+ if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
+ match vec.get(0) {
+ Some(Constant::F32(x)) => Some(Constant::F32(*x)),
+ Some(Constant::F64(x)) => Some(Constant::F64(*x)),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+ }
+
+ /// A block can only yield a constant if it only has one constant expression.
+ fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
+ if block.stmts.is_empty() {
+ block.expr.as_ref().and_then(|b| self.expr(b))
+ } else {
+ None
+ }
+ }
+
+ fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
+ if let Some(Constant::Bool(b)) = self.expr(cond) {
+ if b {
+ self.expr(&*then)
+ } else {
+ otherwise.as_ref().and_then(|expr| self.expr(expr))
+ }
+ } else {
+ None
+ }
+ }
+
+ fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
+ let l = self.expr(left)?;
+ let r = self.expr(right);
+ match (l, r) {
+ (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
+ ty::Int(ity) => {
+ let l = sext(self.lcx.tcx, l, ity);
+ let r = sext(self.lcx.tcx, r, ity);
+ let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
+ match op.node {
+ BinOpKind::Add => l.checked_add(r).map(zext),
+ BinOpKind::Sub => l.checked_sub(r).map(zext),
+ BinOpKind::Mul => l.checked_mul(r).map(zext),
+ BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
+ BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
+ BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
+ BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
+ BinOpKind::BitXor => Some(zext(l ^ r)),
+ BinOpKind::BitOr => Some(zext(l | r)),
+ BinOpKind::BitAnd => Some(zext(l & r)),
+ BinOpKind::Eq => Some(Constant::Bool(l == r)),
+ BinOpKind::Ne => Some(Constant::Bool(l != r)),
+ BinOpKind::Lt => Some(Constant::Bool(l < r)),
+ BinOpKind::Le => Some(Constant::Bool(l <= r)),
+ BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+ BinOpKind::Gt => Some(Constant::Bool(l > r)),
+ _ => None,
+ }
+ },
+ ty::Uint(_) => match op.node {
+ BinOpKind::Add => l.checked_add(r).map(Constant::Int),
+ BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
+ BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
+ BinOpKind::Div => l.checked_div(r).map(Constant::Int),
+ BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
+ BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
+ BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
+ BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
+ BinOpKind::BitOr => Some(Constant::Int(l | r)),
+ BinOpKind::BitAnd => Some(Constant::Int(l & r)),
+ BinOpKind::Eq => Some(Constant::Bool(l == r)),
+ BinOpKind::Ne => Some(Constant::Bool(l != r)),
+ BinOpKind::Lt => Some(Constant::Bool(l < r)),
+ BinOpKind::Le => Some(Constant::Bool(l <= r)),
+ BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+ BinOpKind::Gt => Some(Constant::Bool(l > r)),
+ _ => None,
+ },
+ _ => None,
+ },
+ (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
+ BinOpKind::Add => Some(Constant::F32(l + r)),
+ BinOpKind::Sub => Some(Constant::F32(l - r)),
+ BinOpKind::Mul => Some(Constant::F32(l * r)),
+ BinOpKind::Div => Some(Constant::F32(l / r)),
+ BinOpKind::Rem => Some(Constant::F32(l % r)),
+ BinOpKind::Eq => Some(Constant::Bool(l == r)),
+ BinOpKind::Ne => Some(Constant::Bool(l != r)),
+ BinOpKind::Lt => Some(Constant::Bool(l < r)),
+ BinOpKind::Le => Some(Constant::Bool(l <= r)),
+ BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+ BinOpKind::Gt => Some(Constant::Bool(l > r)),
+ _ => None,
+ },
+ (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
+ BinOpKind::Add => Some(Constant::F64(l + r)),
+ BinOpKind::Sub => Some(Constant::F64(l - r)),
+ BinOpKind::Mul => Some(Constant::F64(l * r)),
+ BinOpKind::Div => Some(Constant::F64(l / r)),
+ BinOpKind::Rem => Some(Constant::F64(l % r)),
+ BinOpKind::Eq => Some(Constant::Bool(l == r)),
+ BinOpKind::Ne => Some(Constant::Bool(l != r)),
+ BinOpKind::Lt => Some(Constant::Bool(l < r)),
+ BinOpKind::Le => Some(Constant::Bool(l <= r)),
+ BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+ BinOpKind::Gt => Some(Constant::Bool(l > r)),
+ _ => None,
+ },
+ (l, r) => match (op.node, l, r) {
+ (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
+ (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
+ (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
+ Some(r)
+ },
+ (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
+ (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
+ (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
+ _ => None,
+ },
+ }
+ }
+}
+
+pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
+ use rustc_middle::mir::interpret::ConstValue;
+ match result.val {
+ ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
+ match result.ty.kind() {
+ ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
+ ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
+ ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
+ int.try_into().expect("invalid f32 bit representation"),
+ ))),
+ ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
+ int.try_into().expect("invalid f64 bit representation"),
+ ))),
+ ty::RawPtr(type_and_mut) => {
+ if let ty::Uint(_) = type_and_mut.ty.kind() {
+ return Some(Constant::RawPtr(int.assert_bits(int.size())));
+ }
+ None
+ },
+ // FIXME: implement other conversions.
+ _ => None,
+ }
+ },
+ ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
+ ty::Ref(_, tam, _) => match tam.kind() {
+ ty::Str => String::from_utf8(
+ data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
+ .to_owned(),
+ )
+ .ok()
+ .map(Constant::Str),
+ _ => None,
+ },
+ _ => None,
+ },
+ ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
+ ty::Array(sub_type, len) => match sub_type.kind() {
+ ty::Float(FloatTy::F32) => match miri_to_const(len) {
+ Some(Constant::Int(len)) => alloc
+ .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
+ .to_owned()
+ .chunks(4)
+ .map(|chunk| {
+ Some(Constant::F32(f32::from_le_bytes(
+ chunk.try_into().expect("this shouldn't happen"),
+ )))
+ })
+ .collect::<Option<Vec<Constant>>>()
+ .map(Constant::Vec),
+ _ => None,
+ },
+ ty::Float(FloatTy::F64) => match miri_to_const(len) {
+ Some(Constant::Int(len)) => alloc
+ .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
+ .to_owned()
+ .chunks(8)
+ .map(|chunk| {
+ Some(Constant::F64(f64::from_le_bytes(
+ chunk.try_into().expect("this shouldn't happen"),
+ )))
+ })
+ .collect::<Option<Vec<Constant>>>()
+ .map(Constant::Vec),
+ _ => None,
+ },
+ // FIXME: implement other array type conversions.
+ _ => None,
+ },
+ _ => None,
+ },
+ // FIXME: implement other conversions.
+ _ => None,
+ }
+}
--- /dev/null
- /// = help: Consider using `f64::NAN` if you would like a constant representing NaN
+//! Clippy wrappers around rustc's diagnostic functions.
+//!
+//! These functions are used by the `INTERNAL_METADATA_COLLECTOR` lint to collect the corresponding
+//! lint applicability. Please make sure that you update the `LINT_EMISSION_FUNCTIONS` variable in
+//! `clippy_lints::utils::internal_lints::metadata_collector` when a new function is added
+//! or renamed.
+//!
+//! Thank you!
+//! ~The `INTERNAL_METADATA_COLLECTOR` lint
+
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_hir::HirId;
+use rustc_lint::{LateContext, Lint, LintContext};
+use rustc_span::source_map::{MultiSpan, Span};
+use std::env;
+
+fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
+ if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
+ if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
+ diag.help(&format!(
+ "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
+ &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
+ // extract just major + minor version and ignore patch versions
+ format!("rust-{}", n.rsplit_once('.').unwrap().1)
+ }),
+ lint
+ ));
+ }
+ }
+}
+
+/// Emit a basic lint message with a `msg` and a `span`.
+///
+/// This is the most primitive of our lint emission methods and can
+/// be a good way to get a new lint started.
+///
+/// Usually it's nicer to provide more context for lint messages.
+/// Be sure the output is understandable when you use this method.
+///
+/// # Example
+///
+/// ```ignore
+/// error: usage of mem::forget on Drop type
+/// --> $DIR/mem_forget.rs:17:5
+/// |
+/// 17 | std::mem::forget(seven);
+/// | ^^^^^^^^^^^^^^^^^^^^^^^
+/// ```
+pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
+ cx.struct_span_lint(lint, sp, |diag| {
+ let mut diag = diag.build(msg);
+ docs_link(&mut diag, lint);
+ diag.emit();
+ });
+}
+
+/// Same as `span_lint` but with an extra `help` message.
+///
+/// Use this if you want to provide some general help but
+/// can't provide a specific machine applicable suggestion.
+///
+/// The `help` message can be optionally attached to a `Span`.
+///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
+/// ```text
+/// error: constant division of 0.0 with 0.0 will always result in NaN
+/// --> $DIR/zero_div_zero.rs:6:25
+/// |
+/// 6 | let other_f64_nan = 0.0f64 / 0.0;
+/// | ^^^^^^^^^^^^
+/// |
++/// = help: consider using `f64::NAN` if you would like a constant representing NaN
+/// ```
+pub fn span_lint_and_help<'a, T: LintContext>(
+ cx: &'a T,
+ lint: &'static Lint,
+ span: Span,
+ msg: &str,
+ help_span: Option<Span>,
+ help: &str,
+) {
+ cx.struct_span_lint(lint, span, |diag| {
+ let mut diag = diag.build(msg);
+ if let Some(help_span) = help_span {
+ diag.span_help(help_span, help);
+ } else {
+ diag.help(help);
+ }
+ docs_link(&mut diag, lint);
+ diag.emit();
+ });
+}
+
+/// Like `span_lint` but with a `note` section instead of a `help` message.
+///
+/// The `note` message is presented separately from the main lint message
+/// and is attached to a specific span:
+///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
+/// ```text
+/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
+/// --> $DIR/drop_forget_ref.rs:10:5
+/// |
+/// 10 | forget(&SomeStruct);
+/// | ^^^^^^^^^^^^^^^^^^^
+/// |
+/// = note: `-D clippy::forget-ref` implied by `-D warnings`
+/// note: argument has type &SomeStruct
+/// --> $DIR/drop_forget_ref.rs:10:12
+/// |
+/// 10 | forget(&SomeStruct);
+/// | ^^^^^^^^^^^
+/// ```
+pub fn span_lint_and_note<'a, T: LintContext>(
+ cx: &'a T,
+ lint: &'static Lint,
+ span: impl Into<MultiSpan>,
+ msg: &str,
+ note_span: Option<Span>,
+ note: &str,
+) {
+ cx.struct_span_lint(lint, span, |diag| {
+ let mut diag = diag.build(msg);
+ if let Some(note_span) = note_span {
+ diag.span_note(note_span, note);
+ } else {
+ diag.note(note);
+ }
+ docs_link(&mut diag, lint);
+ diag.emit();
+ });
+}
+
+/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
+///
+/// If you need to customize your lint output a lot, use this function.
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F)
+where
+ C: LintContext,
+ S: Into<MultiSpan>,
+ F: FnOnce(&mut DiagnosticBuilder<'_>),
+{
+ cx.struct_span_lint(lint, sp, |diag| {
+ let mut diag = diag.build(msg);
+ f(&mut diag);
+ docs_link(&mut diag, lint);
+ diag.emit();
+ });
+}
+
+pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
+ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
+ let mut diag = diag.build(msg);
+ docs_link(&mut diag, lint);
+ diag.emit();
+ });
+}
+
+pub fn span_lint_hir_and_then(
+ cx: &LateContext<'_>,
+ lint: &'static Lint,
+ hir_id: HirId,
+ sp: impl Into<MultiSpan>,
+ msg: &str,
+ f: impl FnOnce(&mut DiagnosticBuilder<'_>),
+) {
+ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
+ let mut diag = diag.build(msg);
+ f(&mut diag);
+ docs_link(&mut diag, lint);
+ diag.emit();
+ });
+}
+
+/// Add a span lint with a suggestion on how to fix it.
+///
+/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
+/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
+/// 2)"`.
+///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
+/// ```text
+/// error: This `.fold` can be more succinctly expressed as `.any`
+/// --> $DIR/methods.rs:390:13
+/// |
+/// 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
+/// |
+/// = note: `-D fold-any` implied by `-D warnings`
+/// ```
+#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
+pub fn span_lint_and_sugg<'a, T: LintContext>(
+ cx: &'a T,
+ lint: &'static Lint,
+ sp: Span,
+ msg: &str,
+ help: &str,
+ sugg: String,
+ applicability: Applicability,
+) {
+ span_lint_and_then(cx, lint, sp, msg, |diag| {
+ diag.span_suggestion(sp, help, sugg, applicability);
+ });
+}
+
+/// Create a suggestion made from several `span → replacement`.
+///
+/// Note: in the JSON format (used by `compiletest_rs`), the help message will
+/// appear once per
+/// replacement. In human-readable format though, it only appears once before
+/// the whole suggestion.
+pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I)
+where
+ I: IntoIterator<Item = (Span, String)>,
+{
+ multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg);
+}
+
+/// Create a suggestion made from several `span → replacement`.
+///
+/// rustfix currently doesn't support the automatic application of suggestions with
+/// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141).
+/// Suggestions with multiple spans will be silently ignored.
+pub fn multispan_sugg_with_applicability<I>(
+ diag: &mut DiagnosticBuilder<'_>,
+ help_msg: &str,
+ applicability: Applicability,
+ sugg: I,
+) where
+ I: IntoIterator<Item = (Span, String)>,
+{
+ diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability);
+}
--- /dev/null
- //! This module contains functions that retrieves specifiec elements.
++//! This module contains functions that retrieve specific elements.
+
+#![deny(clippy::missing_docs_in_private_items)]
+
+use crate::ty::is_type_diagnostic_item;
- use crate::{is_expn_of, last_path_segment, match_def_path, paths};
++use crate::{is_expn_of, last_path_segment, match_def_path, path_to_local_id, 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, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp,
++ Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, PatKind, QPath, StmtKind, UnOp,
+};
+use rustc_lint::LateContext;
+use rustc_span::{sym, symbol, 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>,
++ /// Match arm patterns, the `arg0`, etc. from the next field `args`
++ pub arg_names: &'tcx [Pat<'tcx>],
+ /// 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 PatKind::Tuple(arg_names, None) = arm.pat.kind;
+ 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,
++ arg_names,
+ args,
+ fmt_expr,
+ })
+ } else {
+ None
+ }
+ }
+ }
+
+ /// Returns a vector of `FormatArgsArg`.
+ pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
+ if let Some(expr) = self.fmt_expr {
+ if_chain! {
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
+ if let ExprKind::Array(exprs) = expr.kind;
+ then {
+ exprs.iter().map(|fmt| {
+ if_chain! {
+ // struct `core::fmt::rt::v1::Argument`
+ if let ExprKind::Struct(_, fields, _) = fmt.kind;
+ if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
+ if let ExprKind::Lit(lit) = &position_field.expr.kind;
+ if let LitKind::Int(position, _) = lit.node;
++ if let Ok(i) = usize::try_from(position);
++ let arg = &self.args[i];
++ if let ExprKind::Call(_, [arg_name, _]) = arg.kind;
++ if let Some(j) = self
++ .arg_names
++ .iter()
++ .position(|pat| path_to_local_id(arg_name, pat.hir_id));
+ then {
- let i = usize::try_from(position).unwrap();
- Some(FormatArgsArg { value: self.value_args[i], arg: &self.args[i], fmt: Some(fmt) })
++ Some(FormatArgsArg { value: self.value_args[j], arg, fmt: Some(fmt) })
+ } else {
+ None
+ }
+ }
+ }).collect()
+ } else {
+ None
+ }
+ }
+ } else {
+ Some(
+ self.value_args
+ .iter()
+ .zip(self.args.iter())
+ .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None })
+ .collect(),
+ )
+ }
+ }
+}
+
+/// Type representing a `FormatArgsExpn`'s format arguments
+pub struct FormatArgsArg<'tcx> {
+ /// An element of `value_args` according to `position`
+ pub value: &'tcx Expr<'tcx>,
+ /// An element of `args` according to `position`
+ pub arg: &'tcx Expr<'tcx>,
+ /// An element of `fmt_expn`
+ pub fmt: Option<&'tcx Expr<'tcx>>,
+}
+
+impl<'tcx> FormatArgsArg<'tcx> {
+ /// Returns true if any formatting parameters are used that would have an effect on strings,
+ /// like `{:+2}` instead of just `{}`.
+ pub fn has_string_formatting(&self) -> bool {
+ self.fmt.map_or(false, |fmt| {
+ // `!` because these conditions check that `self` is unformatted.
+ !if_chain! {
+ // struct `core::fmt::rt::v1::Argument`
+ if let ExprKind::Struct(_, fields, _) = fmt.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(_, subfields, _) = format_field.expr.kind;
+ let mut precision_found = false;
+ let mut width_found = false;
+ if subfields.iter().all(|field| {
+ match field.ident.name {
+ sym::precision => {
+ precision_found = true;
+ if let ExprKind::Path(ref precision_path) = field.expr.kind {
+ last_path_segment(precision_path).ident.name == sym::Implied
+ } else {
+ false
+ }
+ }
+ sym::width => {
+ width_found = true;
+ if let ExprKind::Path(ref width_qpath) = field.expr.kind {
+ last_path_segment(width_qpath).ident.name == sym::Implied
+ } else {
+ false
+ }
+ }
+ _ => true,
+ }
+ });
+ if precision_found && width_found;
+ then { true } else { false }
+ }
+ })
+ }
+
+ /// Returns true if the argument is formatted using `Display::fmt`.
+ pub fn is_display(&self) -> bool {
+ if_chain! {
+ if let ExprKind::Call(_, [_, format_field]) = self.arg.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind;
+ if let [.., t, _] = path.segments;
+ if t.ident.name == sym::Display;
+ then { true } else { false }
+ }
+ }
+}
+
+/// 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;
++ if let ExprKind::Call(_, [format_args]) = expr.kind;
+ let expn_data = expr.span.ctxt().outer_expn_data();
+ if let Some(format_args) = FormatArgsExpn::parse(format_args);
+ then {
+ Some(PanicExpn {
+ call_site: expn_data.call_site,
+ format_args,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
+
+/// A parsed `Vec` initialization expression
+#[derive(Clone, Copy)]
+pub enum VecInitKind {
+ /// `Vec::new()`
+ New,
+ /// `Vec::default()` or `Default::default()`
+ Default,
+ /// `Vec::with_capacity(123)`
+ WithLiteralCapacity(u64),
+ /// `Vec::with_capacity(slice.len())`
+ WithExprCapacity(HirId),
+}
+
+/// Checks if given expression is an initialization of `Vec` and returns its kind.
+pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
+ if let ExprKind::Call(func, args) = expr.kind {
+ match func.kind {
+ ExprKind::Path(QPath::TypeRelative(ty, name))
+ if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) =>
+ {
+ if name.ident.name == sym::new {
+ return Some(VecInitKind::New);
+ } else if name.ident.name == symbol::kw::Default {
+ return Some(VecInitKind::Default);
+ } else if name.ident.name.as_str() == "with_capacity" {
+ let arg = args.get(0)?;
+ if_chain! {
+ if let ExprKind::Lit(lit) = &arg.kind;
+ if let LitKind::Int(num, _) = lit.node;
+ then {
+ return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?))
+ }
+ }
+ return Some(VecInitKind::WithExprCapacity(arg.hir_id));
+ }
- }
++ },
+ ExprKind::Path(QPath::Resolved(_, path))
+ if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
+ && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
+ {
+ return Some(VecInitKind::Default);
- }
++ },
+ _ => (),
+ }
+ }
+ None
+}
--- /dev/null
- pub mod camel_case;
+#![feature(box_patterns)]
+#![feature(in_band_lifetimes)]
+#![feature(iter_zip)]
+#![feature(rustc_private)]
+#![feature(control_flow_enum)]
+#![recursion_limit = "512"]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
+// warn on the same lints as `clippy_lints`
+#![warn(trivial_casts, trivial_numeric_casts)]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![warn(rustc::internal)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate rustc_ast;
+extern crate rustc_ast_pretty;
+extern crate rustc_attr;
+extern crate rustc_data_structures;
+extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_infer;
+extern crate rustc_lexer;
+extern crate rustc_lint;
+extern crate rustc_middle;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_target;
+extern crate rustc_trait_selection;
+extern crate rustc_typeck;
+
+#[macro_use]
+pub mod sym_helper;
+
+#[allow(clippy::module_name_repetitions)]
+pub mod ast_utils;
+pub mod attrs;
- /// * Yield/Return statments.
+pub mod comparisons;
+pub mod consts;
+pub mod diagnostics;
+pub mod eager_or_lazy;
+pub mod higher;
+mod hir_utils;
+pub mod msrvs;
+pub mod numeric_literal;
+pub mod paths;
+pub mod ptr;
+pub mod qualify_min_const_fn;
+pub mod source;
++pub mod str_utils;
+pub mod sugg;
+pub mod ty;
+pub mod usage;
+pub mod visitors;
+
+pub use self::attrs::*;
+pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash};
+
+use std::collections::hash_map::Entry;
+use std::hash::BuildHasherDefault;
+
+use if_chain::if_chain;
+use rustc_ast::ast::{self, Attribute, LitKind};
+use rustc_data_structures::unhash::UnhashMap;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::hir_id::{HirIdMap, HirIdSet};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::itemlikevisit::ItemLikeVisitor;
+use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
+use rustc_hir::{
+ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs,
+ HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node,
+ Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
+ UnOp,
+};
+use rustc_lint::{LateContext, Level, Lint, LintContext};
+use rustc_middle::hir::exports::Export;
+use rustc_middle::hir::map::Map;
+use rustc_middle::hir::place::PlaceBase;
+use rustc_middle::ty as rustc_ty;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::binding::BindingMode;
+use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
+use rustc_semver::RustcVersion;
+use rustc_session::Session;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::source_map::original_sp;
+use rustc_span::sym;
+use rustc_span::symbol::{kw, Symbol};
+use rustc_span::{Span, DUMMY_SP};
+use rustc_target::abi::Integer;
+
+use crate::consts::{constant, Constant};
+use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
+
+pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
+ if let Ok(version) = RustcVersion::parse(msrv) {
+ return Some(version);
+ } else if let Some(sess) = sess {
+ if let Some(span) = span {
+ sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
+ }
+ }
+ None
+}
+
+pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
+ msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
+}
+
+#[macro_export]
+macro_rules! extract_msrv_attr {
+ (LateContext) => {
+ extract_msrv_attr!(@LateContext, ());
+ };
+ (EarlyContext) => {
+ extract_msrv_attr!(@EarlyContext);
+ };
+ (@$context:ident$(, $call:tt)?) => {
+ fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
+ use $crate::get_unique_inner_attr;
+ match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
+ Some(msrv_attr) => {
+ if let Some(msrv) = msrv_attr.value_str() {
+ self.msrv = $crate::parse_msrv(
+ &msrv.to_string(),
+ Some(cx.sess$($call)?),
+ Some(msrv_attr.span),
+ );
+ } else {
+ cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute");
+ }
+ },
+ _ => (),
+ }
+ }
+ };
+}
+
+/// Returns `true` if the two spans come from differing expansions (i.e., one is
+/// from a macro and one isn't).
+#[must_use]
+pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
+ rhs.ctxt() != lhs.ctxt()
+}
+
+/// If the given expression is a local binding, find the initializer expression.
+/// If that initializer expression is another local binding, find its initializer again.
+/// This process repeats as long as possible (but usually no more than once). Initializer
+/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
+/// instead.
+///
+/// Examples:
+/// ```ignore
+/// let abc = 1;
+/// // ^ output
+/// let def = abc;
+/// dbg!(def)
+/// // ^^^ input
+///
+/// // or...
+/// let abc = 1;
+/// let def = abc + 2;
+/// // ^^^^^^^ output
+/// dbg!(def)
+/// // ^^^ input
+/// ```
+pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
+ while let Some(init) = path_to_local(expr)
+ .and_then(|id| find_binding_init(cx, id))
+ .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
+ {
+ expr = init;
+ }
+ expr
+}
+
+/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
+/// By only considering immutable bindings, we guarantee that the returned expression represents the
+/// value of the binding wherever it is referenced.
+///
+/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
+/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
+/// canonical binding `HirId`.
+pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
+ let hir = cx.tcx.hir();
+ if_chain! {
+ if let Some(Node::Binding(pat)) = hir.find(hir_id);
+ if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
+ let parent = hir.get_parent_node(hir_id);
+ if let Some(Node::Local(local)) = hir.find(parent);
+ then {
+ return local.init;
+ }
+ }
+ None
+}
+
+/// Returns `true` if the given `NodeId` is inside a constant context
+///
+/// # Example
+///
+/// ```rust,ignore
+/// if in_constant(cx, expr.hir_id) {
+/// // Do something
+/// }
+/// ```
+pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
+ let parent_id = cx.tcx.hir().get_parent_item(id);
+ match cx.tcx.hir().get(parent_id) {
+ Node::Item(&Item {
+ kind: ItemKind::Const(..) | ItemKind::Static(..),
+ ..
+ })
+ | Node::TraitItem(&TraitItem {
+ kind: TraitItemKind::Const(..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Const(..),
+ ..
+ })
+ | Node::AnonConst(_) => true,
+ Node::Item(&Item {
+ kind: ItemKind::Fn(ref sig, ..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(ref sig, _),
+ ..
+ }) => sig.header.constness == Constness::Const,
+ _ => false,
+ }
+}
+
+/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
+/// For example, use this to check whether a function call or a pattern is `Some(..)`.
+pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
+ if let QPath::Resolved(_, path) = qpath {
+ if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
+ if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
+ return cx.tcx.parent(ctor_id) == Some(item_id);
+ }
+ }
+ }
+ false
+}
+
+/// Returns `true` if this `span` was expanded by any macro.
+#[must_use]
+pub fn in_macro(span: Span) -> bool {
+ span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
+}
+
+pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
+ matches!(
+ expr.kind,
+ ExprKind::Block(
+ Block {
+ stmts: [],
+ expr: None,
+ ..
+ },
+ _
+ ) | ExprKind::Tup([])
+ )
+}
+
+/// Checks if given pattern is a wildcard (`_`)
+pub fn is_wild(pat: &Pat<'_>) -> bool {
+ matches!(pat.kind, PatKind::Wild)
+}
+
+/// Checks if the first type parameter is a lang item.
+pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
+ let ty = get_qpath_generic_tys(qpath).next()?;
+
+ if let TyKind::Path(qpath) = &ty.kind {
+ cx.qpath_res(qpath, ty.hir_id)
+ .opt_def_id()
+ .map_or(false, |id| {
+ cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
+ })
+ .then(|| ty)
+ } else {
+ None
+ }
+}
+
+/// Checks if the first type parameter is a diagnostic item.
+pub fn is_ty_param_diagnostic_item(
+ cx: &LateContext<'_>,
+ qpath: &QPath<'tcx>,
+ item: Symbol,
+) -> Option<&'tcx hir::Ty<'tcx>> {
+ let ty = get_qpath_generic_tys(qpath).next()?;
+
+ if let TyKind::Path(qpath) = &ty.kind {
+ cx.qpath_res(qpath, ty.hir_id)
+ .opt_def_id()
+ .map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
+ .then(|| ty)
+ } else {
+ None
+ }
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+/// This is a deprecated function, consider using [`is_trait_method`].
+pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
+ let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
+ let trt_id = cx.tcx.trait_of_item(def_id);
+ trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
+}
+
+/// Checks if a method is defined in an impl of a diagnostic item
+pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+ if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+ return cx.tcx.is_diagnostic_item(diag_item, adt.did);
+ }
+ }
+ false
+}
+
+/// Checks if a method is in a diagnostic item trait
+pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
+ return cx.tcx.is_diagnostic_item(diag_item, trait_did);
+ }
+ false
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ cx.typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
+}
+
+/// Checks if the given expression is a path referring an item on the trait
+/// that is marked with the given diagnostic item.
+///
+/// For checking method call expressions instead of path expressions, use
+/// [`is_trait_method`].
+///
+/// For example, this can be used to find if an expression like `u64::default`
+/// refers to an item of the trait `Default`, which is associated with the
+/// `diag_item` of `sym::Default`.
+pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ if let hir::ExprKind::Path(ref qpath) = expr.kind {
+ cx.qpath_res(qpath, expr.hir_id)
+ .opt_def_id()
+ .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
+ } else {
+ false
+ }
+}
+
+pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
+ match *path {
+ QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
+ QPath::TypeRelative(_, seg) => seg,
+ QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
+ }
+}
+
+pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
+ match path {
+ QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
+ QPath::TypeRelative(_, s) => s.args,
+ QPath::LangItem(..) => None,
+ }
+}
+
+pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
+ get_qpath_generics(path)
+ .map_or([].as_ref(), |a| a.args)
+ .iter()
+ .filter_map(|a| {
+ if let hir::GenericArg::Type(ty) = a {
+ Some(ty)
+ } else {
+ None
+ }
+ })
+}
+
+pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+ match *path {
+ QPath::Resolved(_, path) => path.segments.get(0),
+ QPath::TypeRelative(_, seg) => Some(seg),
+ QPath::LangItem(..) => None,
+ }
+}
+
+/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
+/// `QPath::Resolved.1.res.opt_def_id()`.
+///
+/// Matches a `QPath` against a slice of segment string literals.
+///
+/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
+/// `rustc_hir::QPath`.
+///
+/// # Examples
+/// ```rust,ignore
+/// match_qpath(path, &["std", "rt", "begin_unwind"])
+/// ```
+pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
+ match *path {
+ QPath::Resolved(_, path) => match_path(path, segments),
+ QPath::TypeRelative(ty, segment) => match ty.kind {
+ TyKind::Path(ref inner_path) => {
+ if let [prefix @ .., end] = segments {
+ if match_qpath(inner_path, prefix) {
+ return segment.ident.name.as_str() == *end;
+ }
+ }
+ false
+ },
+ _ => false,
+ },
+ QPath::LangItem(..) => false,
+ }
+}
+
+/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
+pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
+ if let ExprKind::Path(p) = &expr.kind {
+ cx.qpath_res(p, expr.hir_id)
+ } else {
+ Res::Err
+ }
+}
+
+/// Resolves the path to a `DefId` and checks if it matches the given path.
+pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
+ cx.qpath_res(path, hir_id)
+ .opt_def_id()
+ .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
+///
+/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
+pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
+ expr_path_res(cx, expr)
+ .opt_def_id()
+ .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
+/// diagnostic item.
+pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ expr_path_res(cx, expr)
+ .opt_def_id()
+ .map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+}
+
+/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
+/// `QPath::Resolved.1.res.opt_def_id()`.
+///
+/// Matches a `Path` against a slice of segment string literals.
+///
+/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
+/// `rustc_hir::Path`.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// if match_path(&trait_ref.path, &paths::HASH) {
+/// // This is the `std::hash::Hash` trait.
+/// }
+///
+/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
+/// // This is a `rustc_middle::lint::Lint`.
+/// }
+/// ```
+pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
+ path.segments
+ .iter()
+ .rev()
+ .zip(segments.iter().rev())
+ .all(|(a, b)| a.ident.name.as_str() == *b)
+}
+
+/// If the expression is a path to a local, returns the canonical `HirId` of the local.
+pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
+ if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
+ if let Res::Local(id) = path.res {
+ return Some(id);
+ }
+ }
+ None
+}
+
+/// Returns true if the expression is a path to a local with the specified `HirId`.
+/// Use this function to see if an expression matches a function argument or a match binding.
+pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
+ path_to_local(expr) == Some(id)
+}
+
+/// Gets the definition associated to a path.
+pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
+ macro_rules! try_res {
+ ($e:expr) => {
+ match $e {
+ Some(e) => e,
+ None => return Res::Err,
+ }
+ };
+ }
+ fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> {
+ tcx.item_children(def_id)
+ .iter()
+ .find(|item| item.ident.name.as_str() == name)
+ }
+
+ let (krate, first, path) = match *path {
+ [krate, first, ref path @ ..] => (krate, first, path),
+ [primitive] => {
+ return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
+ },
+ _ => return Res::Err,
+ };
+ let tcx = cx.tcx;
+ let crates = tcx.crates(());
+ let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
+ let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
+ let last = path
+ .iter()
+ .copied()
+ // `get_def_path` seems to generate these empty segments for extern blocks.
+ // We can just ignore them.
+ .filter(|segment| !segment.is_empty())
+ // for each segment, find the child item
+ .try_fold(first, |item, segment| {
+ let def_id = item.res.def_id();
+ if let Some(item) = item_child_by_name(tcx, def_id, segment) {
+ Some(item)
+ } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
+ // it is not a child item so check inherent impl items
+ tcx.inherent_impls(def_id)
+ .iter()
+ .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
+ } else {
+ None
+ }
+ });
+ try_res!(last).res.expect_non_local()
+}
+
+/// Convenience function to get the `DefId` of a trait by path.
+/// It could be a trait or trait alias.
+pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
+ match path_to_res(cx, path) {
+ Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
+ _ => None,
+ }
+}
+
+/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
+///
+/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
+///
+/// ```rust
+/// struct Point(isize, isize);
+///
+/// impl std::ops::Add for Point {
+/// type Output = Self;
+///
+/// fn add(self, other: Self) -> Self {
+/// Point(0, 0)
+/// }
+/// }
+/// ```
+pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
+ // Get the implemented trait for the current function
+ let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
+ if_chain! {
+ if parent_impl != hir::CRATE_HIR_ID;
+ if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
+ if let hir::ItemKind::Impl(impl_) = &item.kind;
+ then { return impl_.of_trait.as_ref(); }
+ }
+ None
+}
+
+/// This method will return tuple of projection stack and root of the expression,
+/// used in `can_mut_borrow_both`.
+///
+/// For example, if `e` represents the `v[0].a.b[x]`
+/// this method will return a tuple, composed of a `Vec`
+/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
+/// and an `Expr` for root of them, `v`
+fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
+ let mut result = vec![];
+ let root = loop {
+ match e.kind {
+ ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
+ result.push(e);
+ e = ep;
+ },
+ _ => break e,
+ };
+ };
+ result.reverse();
+ (result, root)
+}
+
+/// Checks if two expressions can be mutably borrowed simultaneously
+/// and they aren't dependent on borrowing same thing twice
+pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
+ let (s1, r1) = projection_stack(e1);
+ let (s2, r2) = projection_stack(e2);
+ if !eq_expr_value(cx, r1, r2) {
+ return true;
+ }
+ for (x1, x2) in s1.iter().zip(s2.iter()) {
+ match (&x1.kind, &x2.kind) {
+ (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
+ if i1 != i2 {
+ return true;
+ }
+ },
+ (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
+ if !eq_expr_value(cx, i1, i2) {
+ return false;
+ }
+ },
+ _ => return false,
+ }
+ }
+ false
+}
+
+/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
+/// constructor from the std library
+fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
+ let std_types_symbols = &[
+ sym::String,
+ sym::Vec,
+ sym::VecDeque,
+ sym::LinkedList,
+ sym::HashMap,
+ sym::BTreeMap,
+ sym::HashSet,
+ sym::BTreeSet,
+ sym::BinaryHeap,
+ ];
+
+ if let QPath::TypeRelative(_, method) = path {
+ if method.ident.name == sym::new {
+ if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+ if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+ return std_types_symbols
+ .iter()
+ .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
+ }
+ }
+ }
+ }
+ false
+}
+
+/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
+/// It doesn't cover all cases, for example indirect function calls (some of std
+/// functions are supported) but it is the best we have.
+pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ match &e.kind {
+ ExprKind::Lit(lit) => match lit.node {
+ LitKind::Bool(false) | LitKind::Int(0, _) => true,
+ LitKind::Str(s, _) => s.is_empty(),
+ _ => false,
+ },
+ ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
+ ExprKind::Repeat(x, y) => if_chain! {
+ if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(y.body).value.kind;
+ if let LitKind::Int(v, _) = const_lit.node;
+ if v <= 32 && is_default_equivalent(cx, x);
+ then {
+ true
+ }
+ else {
+ false
+ }
+ },
+ ExprKind::Call(repl_func, _) => if_chain! {
+ if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
+ if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+ if is_diag_trait_item(cx, repl_def_id, sym::Default)
+ || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
+ then {
+ true
+ }
+ else {
+ false
+ }
+ },
+ ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
+ ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
+ _ => false,
+ }
+}
+
+/// Checks if the top level expression can be moved into a closure as is.
+/// Currently checks for:
+/// * Break/Continue outside the given loop HIR ids.
- if let [Adjustment {
- kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
- target,
- }, ref adjust @ ..] = *cx
++/// * Yield/Return statements.
+/// * Inline assembly.
+/// * Usages of a field of a local where the type of the local can be partially moved.
+///
+/// For example, given the following function:
+///
+/// ```
+/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
+/// for item in iter {
+/// let s = item.1;
+/// if item.0 > 10 {
+/// continue;
+/// } else {
+/// s.clear();
+/// }
+/// }
+/// }
+/// ```
+///
+/// When called on the expression `item.0` this will return false unless the local `item` is in the
+/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
+/// isn't always safe to move into a closure when only a single field is needed.
+///
+/// When called on the `continue` expression this will return false unless the outer loop expression
+/// is in the `loop_ids` set.
+///
+/// Note that this check is not recursive, so passing the `if` expression will always return true
+/// even though sub-expressions might return false.
+pub fn can_move_expr_to_closure_no_visit(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ loop_ids: &[HirId],
+ ignore_locals: &HirIdSet,
+) -> bool {
+ match expr.kind {
+ ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
+ | ExprKind::Continue(Destination { target_id: Ok(id), .. })
+ if loop_ids.contains(&id) =>
+ {
+ true
+ },
+ ExprKind::Break(..)
+ | ExprKind::Continue(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Yield(..)
+ | ExprKind::InlineAsm(_)
+ | ExprKind::LlvmInlineAsm(_) => false,
+ // Accessing a field of a local value can only be done if the type isn't
+ // partially moved.
+ ExprKind::Field(
+ &Expr {
+ hir_id,
+ kind:
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ res: Res::Local(local_id),
+ ..
+ },
+ )),
+ ..
+ },
+ _,
+ ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
+ // TODO: check if the local has been partially moved. Assume it has for now.
+ false
+ },
+ _ => true,
+ }
+}
+
+/// How a local is captured by a closure
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureKind {
+ Value,
+ Ref(Mutability),
+}
+impl CaptureKind {
+ pub fn is_imm_ref(self) -> bool {
+ self == Self::Ref(Mutability::Not)
+ }
+}
+impl std::ops::BitOr for CaptureKind {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self::Output {
+ match (self, rhs) {
+ (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
+ (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
+ | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
+ (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
+ }
+ }
+}
+impl std::ops::BitOrAssign for CaptureKind {
+ fn bitor_assign(&mut self, rhs: Self) {
+ *self = *self | rhs;
+ }
+}
+
+/// Given an expression referencing a local, determines how it would be captured in a closure.
+/// Note as this will walk up to parent expressions until the capture can be determined it should
+/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
+/// function argument (other than a receiver).
+pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
+ fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
+ let mut capture = CaptureKind::Ref(Mutability::Not);
+ pat.each_binding_or_first(&mut |_, id, span, _| match cx
+ .typeck_results()
+ .extract_binding_mode(cx.sess(), id, span)
+ .unwrap()
+ {
+ BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
+ capture = CaptureKind::Value;
+ },
+ BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
+ capture = CaptureKind::Ref(Mutability::Mut);
+ },
+ _ => (),
+ });
+ capture
+ }
+
+ debug_assert!(matches!(
+ e.kind,
+ ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
+ ));
+
+ let mut child_id = e.hir_id;
+ let mut capture = CaptureKind::Value;
+ let mut capture_expr_ty = e;
+
+ for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
- e
- @
- Expr {
++ if let [
++ Adjustment {
++ kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
++ target,
++ },
++ ref adjust @ ..,
++ ] = *cx
+ .typeck_results()
+ .adjustments()
+ .get(child_id)
+ .map_or(&[][..], |x| &**x)
+ {
+ if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
+ *adjust.last().map_or(target, |a| a.target).kind()
+ {
+ return CaptureKind::Ref(mutability);
+ }
+ }
+
+ match parent {
+ Node::Expr(e) => match e.kind {
+ ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
+ ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
+ ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
+ return CaptureKind::Ref(Mutability::Mut);
+ },
+ ExprKind::Field(..) => {
+ if capture == CaptureKind::Value {
+ capture_expr_ty = e;
+ }
+ },
+ ExprKind::Let(pat, ..) => {
+ let mutability = match pat_capture_kind(cx, pat) {
+ CaptureKind::Value => Mutability::Not,
+ CaptureKind::Ref(m) => m,
+ };
+ return CaptureKind::Ref(mutability);
+ },
+ ExprKind::Match(_, arms, _) => {
+ let mut mutability = Mutability::Not;
+ for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
+ match capture {
+ CaptureKind::Value => break,
+ CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
+ CaptureKind::Ref(Mutability::Not) => (),
+ }
+ }
+ return CaptureKind::Ref(mutability);
+ },
+ _ => break,
+ },
+ Node::Local(l) => match pat_capture_kind(cx, l.pat) {
+ CaptureKind::Value => break,
+ capture @ CaptureKind::Ref(_) => return capture,
+ },
+ _ => break,
+ }
+
+ child_id = parent_id;
+ }
+
+ if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
+ // Copy types are never automatically captured by value.
+ CaptureKind::Ref(Mutability::Not)
+ } else {
+ capture
+ }
+}
+
+/// Checks if the expression can be moved into a closure as is. This will return a list of captures
+/// if so, otherwise, `None`.
+pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ // Stack of potential break targets contained in the expression.
+ loops: Vec<HirId>,
+ /// Local variables created in the expression. These don't need to be captured.
+ locals: HirIdSet,
+ /// Whether this expression can be turned into a closure.
+ allow_closure: bool,
+ /// Locals which need to be captured, and whether they need to be by value, reference, or
+ /// mutable reference.
+ captures: HirIdMap<CaptureKind>,
+ }
+ impl Visitor<'tcx> for V<'_, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if !self.allow_closure {
+ return;
+ }
+
+ match e.kind {
+ ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
+ if !self.locals.contains(&l) {
+ let cap = capture_local_usage(self.cx, e);
+ self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
+ }
+ },
+ ExprKind::Closure(..) => {
+ let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id).to_def_id();
+ for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
+ let local_id = match capture.place.base {
+ PlaceBase::Local(id) => id,
+ PlaceBase::Upvar(var) => var.var_path.hir_id,
+ _ => continue,
+ };
+ if !self.locals.contains(&local_id) {
+ let capture = match capture.info.capture_kind {
+ UpvarCapture::ByValue(_) => CaptureKind::Value,
+ UpvarCapture::ByRef(borrow) => match borrow.kind {
+ BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
+ BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
+ CaptureKind::Ref(Mutability::Mut)
+ },
+ },
+ };
+ self.captures
+ .entry(local_id)
+ .and_modify(|e| *e |= capture)
+ .or_insert(capture);
+ }
+ }
+ },
+ ExprKind::Loop(b, ..) => {
+ self.loops.push(e.hir_id);
+ self.visit_block(b);
+ self.loops.pop();
+ },
+ _ => {
+ self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
+ walk_expr(self, e);
+ },
+ }
+ }
+
+ fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
+ p.each_binding_or_first(&mut |_, id, _, _| {
+ self.locals.insert(id);
+ });
+ }
+ }
+
+ let mut v = V {
+ cx,
+ allow_closure: true,
+ loops: Vec::new(),
+ locals: HirIdSet::default(),
+ captures: HirIdMap::default(),
+ };
+ v.visit_expr(expr);
+ v.allow_closure.then(|| v.captures)
+}
+
+/// Returns the method names and argument list of nested method call expressions that make up
+/// `expr`. method/span lists are sorted with the most recent call first.
+pub fn method_calls<'tcx>(
+ expr: &'tcx Expr<'tcx>,
+ max_depth: usize,
+) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
+ let mut method_names = Vec::with_capacity(max_depth);
+ let mut arg_lists = Vec::with_capacity(max_depth);
+ let mut spans = Vec::with_capacity(max_depth);
+
+ let mut current = expr;
+ for _ in 0..max_depth {
+ if let ExprKind::MethodCall(path, span, args, _) = ¤t.kind {
+ if args.iter().any(|e| e.span.from_expansion()) {
+ break;
+ }
+ method_names.push(path.ident.name);
+ arg_lists.push(&**args);
+ spans.push(*span);
+ current = &args[0];
+ } else {
+ break;
+ }
+ }
+
+ (method_names, arg_lists, spans)
+}
+
+/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
+///
+/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
+/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
+/// containing the `Expr`s for
+/// `.bar()` and `.baz()`
+pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
+ let mut current = expr;
+ let mut matched = Vec::with_capacity(methods.len());
+ for method_name in methods.iter().rev() {
+ // method chains are stored last -> first
+ if let ExprKind::MethodCall(path, _, args, _) = current.kind {
+ if path.ident.name.as_str() == *method_name {
+ if args.iter().any(|e| e.span.from_expansion()) {
+ return None;
+ }
+ matched.push(args); // build up `matched` backwards
+ current = &args[0]; // go to parent expression
+ } else {
+ return None;
+ }
+ } else {
+ return None;
+ }
+ }
+ // Reverse `matched` so that it is in the same order as `methods`.
+ matched.reverse();
+ Some(matched)
+}
+
+/// Returns `true` if the provided `def_id` is an entrypoint to a program.
+pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
+ cx.tcx
+ .entry_fn(())
+ .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
+}
+
+/// Returns `true` if the expression is in the program's `#[panic_handler]`.
+pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ let parent = cx.tcx.hir().get_parent_item(e.hir_id);
+ let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
+ Some(def_id) == cx.tcx.lang_items().panic_impl()
+}
+
+/// Gets the name of the item the expression is in, if available.
+pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
+ let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
+ match cx.tcx.hir().find(parent_id) {
+ Some(
+ Node::Item(Item { ident, .. })
+ | Node::TraitItem(TraitItem { ident, .. })
+ | Node::ImplItem(ImplItem { ident, .. }),
+ ) => Some(ident.name),
+ _ => None,
+ }
+}
+
+pub struct ContainsName {
+ pub name: Symbol,
+ pub result: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for ContainsName {
+ type Map = Map<'tcx>;
+
+ fn visit_name(&mut self, _: Span, name: Symbol) {
+ if self.name == name {
+ self.result = true;
+ }
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Checks if an `Expr` contains a certain name.
+pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
+ let mut cn = ContainsName { name, result: false };
+ cn.visit_expr(expr);
+ cn.result
+}
+
+/// Returns `true` if `expr` contains a return expression
+pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
+ struct RetCallFinder {
+ found: bool,
+ }
+
+ impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
+ if self.found {
+ return;
+ }
+ if let hir::ExprKind::Ret(..) = &expr.kind {
+ self.found = true;
+ } else {
+ hir::intravisit::walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
+ hir::intravisit::NestedVisitorMap::None
+ }
+ }
+
+ let mut visitor = RetCallFinder { found: false };
+ visitor.visit_expr(expr);
+ visitor.found
+}
+
+struct FindMacroCalls<'a, 'b> {
+ names: &'a [&'b str],
+ result: Vec<Span>,
+}
+
+impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
+ self.result.push(expr.span);
+ }
+ // and check sub-expressions
+ intravisit::walk_expr(self, expr);
+ }
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+}
+
+/// Finds calls of the specified macros in a function body.
+pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
+ let mut fmc = FindMacroCalls {
+ names,
+ result: Vec::new(),
+ };
+ fmc.visit_expr(&body.value);
+ fmc.result
+}
+
+/// Extends the span to the beginning of the spans line, incl. whitespaces.
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^
+/// // will be converted to
+/// let x = ();
+/// // ^^^^^^^^^^^^^^
+/// ```
+fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
+ let span = original_sp(span, DUMMY_SP);
+ let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
+ let line_no = source_map_and_line.line;
+ let line_start = source_map_and_line.sf.lines[line_no];
+ span.with_lo(line_start)
+}
+
+/// Gets the parent node, if any.
+pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
+ tcx.hir().parent_iter(id).next().map(|(_, node)| node)
+}
+
+/// Gets the parent expression, if any –- this is useful to constrain a lint.
+pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ get_parent_expr_for_hir(cx, e.hir_id)
+}
+
+/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
+/// constraint lints
+pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
+ match get_parent_node(cx.tcx, hir_id) {
+ Some(Node::Expr(parent)) => Some(parent),
+ _ => None,
+ }
+}
+
+pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
+ let map = &cx.tcx.hir();
+ let enclosing_node = map
+ .get_enclosing_scope(hir_id)
+ .and_then(|enclosing_id| map.find(enclosing_id));
+ enclosing_node.and_then(|node| match node {
+ Node::Block(block) => Some(block),
+ Node::Item(&Item {
+ kind: ItemKind::Fn(_, _, eid),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(_, eid),
+ ..
+ }) => match cx.tcx.hir().body(eid).value.kind {
+ ExprKind::Block(block, _) => Some(block),
+ _ => None,
+ },
+ _ => None,
+ })
+}
+
+/// Gets the loop or closure enclosing the given expression, if any.
+pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
+ match node {
+ Node::Expr(
- &[Expr {
- kind: ExprKind::Closure(_, _, body, _, _),
- ..
- }],
++ e @ Expr {
+ kind: ExprKind::Loop(..) | ExprKind::Closure(..),
+ ..
+ },
+ ) => return Some(e),
+ Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
+ _ => break,
+ }
+ }
+ None
+}
+
+/// Gets the parent node if it's an impl block.
+pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
+ match tcx.hir().parent_iter(id).next() {
+ Some((
+ _,
+ Node::Item(Item {
+ kind: ItemKind::Impl(imp),
+ ..
+ }),
+ )) => Some(imp),
+ _ => None,
+ }
+}
+
+/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
+pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ let mut iter = tcx.hir().parent_iter(expr.hir_id);
+ match iter.next() {
+ Some((
+ _,
+ Node::Expr(Expr {
+ kind: ExprKind::If(_, _, Some(else_expr)),
+ ..
+ }),
+ )) => else_expr.hir_id == expr.hir_id,
+ _ => false,
+ }
+}
+
+/// Checks whether the given expression is a constant integer of the given value.
+/// unlike `is_integer_literal`, this version does const folding
+pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
+ if is_integer_literal(e, value) {
+ return true;
+ }
+ let enclosing_body = cx.tcx.hir().local_def_id(cx.tcx.hir().enclosing_body_owner(e.hir_id));
+ if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
+ return value == v;
+ }
+ false
+}
+
+/// Checks whether the given expression is a constant literal of the given value.
+pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
+ // FIXME: use constant folding
+ if let ExprKind::Lit(ref spanned) = expr.kind {
+ if let LitKind::Int(v, _) = spanned.node {
+ return v == value;
+ }
+ }
+ false
+}
+
+/// Returns `true` if the given `Expr` has been coerced before.
+///
+/// Examples of coercions can be found in the Nomicon at
+/// <https://doc.rust-lang.org/nomicon/coercions.html>.
+///
+/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
+/// information on adjustments and coercions.
+pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ cx.typeck_results().adjustments().get(e.hir_id).is_some()
+}
+
+/// Returns the pre-expansion span if is this comes from an expansion of the
+/// macro `name`.
+/// See also [`is_direct_expn_of`].
+#[must_use]
+pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
+ loop {
+ if span.from_expansion() {
+ let data = span.ctxt().outer_expn_data();
+ let new_span = data.call_site;
+
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+ if mac_name.as_str() == name {
+ return Some(new_span);
+ }
+ }
+
+ span = new_span;
+ } else {
+ return None;
+ }
+ }
+}
+
+/// Returns the pre-expansion span if the span directly comes from an expansion
+/// of the macro `name`.
+/// The difference with [`is_expn_of`] is that in
+/// ```rust
+/// # macro_rules! foo { ($e:tt) => { $e } }; macro_rules! bar { ($e:expr) => { $e } }
+/// foo!(bar!(42));
+/// ```
+/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
+/// from `bar!` by `is_direct_expn_of`.
+#[must_use]
+pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
+ if span.from_expansion() {
+ let data = span.ctxt().outer_expn_data();
+ let new_span = data.call_site;
+
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+ if mac_name.as_str() == name {
+ return Some(new_span);
+ }
+ }
+ }
+
+ None
+}
+
+/// Convenience function to get the return type of a function.
+pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
+ let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
+ let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
+ cx.tcx.erase_late_bound_regions(ret_ty)
+}
+
+/// Checks if an expression is constructing a tuple-like enum variant or struct
+pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let ExprKind::Call(fun, _) = expr.kind {
+ if let ExprKind::Path(ref qp) = fun.kind {
+ let res = cx.qpath_res(qp, fun.hir_id);
+ return match res {
+ def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
+ def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
+ _ => false,
+ };
+ }
+ }
+ false
+}
+
+/// Returns `true` if a pattern is refutable.
+// TODO: should be implemented using rustc/mir_build/thir machinery
+pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
+ fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
+ matches!(
+ cx.qpath_res(qpath, id),
+ def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
+ )
+ }
+
+ fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
+ i.into_iter().any(|pat| is_refutable(cx, pat))
+ }
+
+ match pat.kind {
+ PatKind::Wild => false,
+ PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
+ PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
+ PatKind::Lit(..) | PatKind::Range(..) => true,
+ PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
+ PatKind::Or(pats) => {
+ // TODO: should be the honest check, that pats is exhaustive set
+ are_refutable(cx, pats)
+ },
+ PatKind::Tuple(pats, _) => are_refutable(cx, pats),
+ PatKind::Struct(ref qpath, fields, _) => {
+ is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
+ },
+ PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
+ PatKind::Slice(head, middle, tail) => {
+ match &cx.typeck_results().node_type(pat.hir_id).kind() {
+ rustc_ty::Slice(..) => {
+ // [..] is the only irrefutable slice pattern.
+ !head.is_empty() || middle.is_none() || !tail.is_empty()
+ },
+ rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
+ _ => {
+ // unreachable!()
+ true
+ },
+ }
+ },
+ }
+}
+
+/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
+/// the function once on the given pattern.
+pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
+ if let PatKind::Or(pats) = pat.kind {
+ pats.iter().for_each(f);
+ } else {
+ f(pat);
+ }
+}
+
+/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
+/// implementations have.
+pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
+ attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
+}
+
+/// Remove blocks around an expression.
+///
+/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
+/// themselves.
+pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
+ while let ExprKind::Block(block, ..) = expr.kind {
+ match (block.stmts.is_empty(), block.expr.as_ref()) {
+ (true, Some(e)) => expr = e,
+ _ => break,
+ }
+ }
+ expr
+}
+
+pub fn is_self(slf: &Param<'_>) -> bool {
+ if let PatKind::Binding(.., name, _) = slf.pat.kind {
+ name.name == kw::SelfLower
+ } else {
+ false
+ }
+}
+
+pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
+ if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
+ if let Res::SelfTy(..) = path.res {
+ return true;
+ }
+ }
+ false
+}
+
+pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
+ (0..decl.inputs.len()).map(move |i| &body.params[i])
+}
+
+/// Checks if a given expression is a match expression expanded from the `?`
+/// operator or the `try` macro.
+pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ if_chain! {
+ if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind;
+ if is_lang_ctor(cx, path, ResultOk);
+ if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
+ if path_to_local_id(arm.body, hir_id);
+ then {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
+ is_lang_ctor(cx, path, ResultErr)
+ } else {
+ false
+ }
+ }
+
+ if let ExprKind::Match(_, arms, ref source) = expr.kind {
+ // desugared from a `?` operator
+ if *source == MatchSource::TryDesugar {
+ return Some(expr);
+ }
+
+ if_chain! {
+ if arms.len() == 2;
+ if arms[0].guard.is_none();
+ if arms[1].guard.is_none();
+ if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
+ (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
+ then {
+ return Some(expr);
+ }
+ }
+ }
+
+ None
+}
+
+/// Returns `true` if the lint is allowed in the current context
+///
+/// Useful for skipping long running code when it's unnecessary
+pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
+ cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
+}
+
+pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
+ while let PatKind::Ref(subpat, _) = pat.kind {
+ pat = subpat;
+ }
+ pat
+}
+
+pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
+ Integer::from_int_ty(&tcx, ity).size().bits()
+}
+
+#[allow(clippy::cast_possible_wrap)]
+/// Turn a constant int byte representation into an i128
+pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
+ let amt = 128 - int_bits(tcx, ity);
+ ((u as i128) << amt) >> amt
+}
+
+#[allow(clippy::cast_sign_loss)]
+/// clip unused bytes
+pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
+ let amt = 128 - int_bits(tcx, ity);
+ ((u as u128) << amt) >> amt
+}
+
+/// clip unused bytes
+pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
+ let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
+ let amt = 128 - bits;
+ (u << amt) >> amt
+}
+
+pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+ let map = &tcx.hir();
+ let mut prev_enclosing_node = None;
+ let mut enclosing_node = node;
+ while Some(enclosing_node) != prev_enclosing_node {
+ if is_automatically_derived(map.attrs(enclosing_node)) {
+ return true;
+ }
+ prev_enclosing_node = Some(enclosing_node);
+ enclosing_node = map.get_parent_item(enclosing_node);
+ }
+ false
+}
+
+/// Matches a function call with the given path and returns the arguments.
+///
+/// Usage:
+///
+/// ```rust,ignore
+/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
+/// ```
+pub fn match_function_call<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ path: &[&str],
+) -> Option<&'tcx [Expr<'tcx>]> {
+ if_chain! {
+ if let ExprKind::Call(fun, args) = expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ if match_def_path(cx, fun_def_id, path);
+ then {
+ return Some(args)
+ }
+ };
+ None
+}
+
+/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
+/// any.
+///
+/// Please use `match_any_diagnostic_items` if the targets are all diagnostic items.
+pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
+ let search_path = cx.get_def_path(did);
+ paths
+ .iter()
+ .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
+}
+
+/// Checks if the given `DefId` matches any of provided diagnostic items. Returns the index of
+/// matching path, if any.
+pub fn match_any_diagnostic_items(cx: &LateContext<'_>, def_id: DefId, diag_items: &[Symbol]) -> Option<usize> {
+ diag_items
+ .iter()
+ .position(|item| cx.tcx.is_diagnostic_item(*item, def_id))
+}
+
+/// Checks if the given `DefId` matches the path.
+pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
+ // We should probably move to Symbols in Clippy as well rather than interning every time.
+ let path = cx.get_def_path(did);
+ syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
+}
+
+pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Call(func, [arg]) = expr.kind {
+ expr_path_res(cx, func)
+ .opt_def_id()
+ .map_or(false, |id| match_panic_def_id(cx, id))
+ .then(|| arg)
+ } else {
+ None
+ }
+}
+
+pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
+ match_any_def_paths(
+ cx,
+ did,
+ &[
+ &paths::BEGIN_PANIC,
+ &paths::PANIC_ANY,
+ &paths::PANICKING_PANIC,
+ &paths::PANICKING_PANIC_FMT,
+ &paths::PANICKING_PANIC_STR,
+ ],
+ )
+ .is_some()
+}
+
+/// Returns the list of condition expressions and the list of blocks in a
+/// sequence of `if/else`.
+/// E.g., this returns `([a, b], [c, d, e])` for the expression
+/// `if a { c } else if b { d } else { e }`.
+pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
+ let mut conds = Vec::new();
+ let mut blocks: Vec<&Block<'_>> = Vec::new();
+
+ while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
+ conds.push(&*cond);
+ if let ExprKind::Block(block, _) = then.kind {
+ blocks.push(block);
+ } else {
+ panic!("ExprKind::If node is not an ExprKind::Block");
+ }
+
+ if let Some(else_expr) = r#else {
+ expr = else_expr;
+ } else {
+ break;
+ }
+ }
+
+ // final `else {..}`
+ if !blocks.is_empty() {
+ if let ExprKind::Block(block, _) = expr.kind {
+ blocks.push(block);
+ }
+ }
+
+ (conds, blocks)
+}
+
+/// Checks if the given function kind is an async function.
+pub fn is_async_fn(kind: FnKind<'_>) -> bool {
+ matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
+}
+
+/// Peels away all the compiler generated code surrounding the body of an async function,
+pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Call(
+ _,
- /// Checks whether item either has `test` attribute appelied, or
++ &[
++ Expr {
++ kind: ExprKind::Closure(_, _, body, _, _),
++ ..
++ },
++ ],
+ ) = body.value.kind
+ {
+ if let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr:
+ Some(Expr {
+ kind: ExprKind::DropTemps(expr),
+ ..
+ }),
+ ..
+ },
+ _,
+ ) = tcx.hir().body(body).value.kind
+ {
+ return Some(expr);
+ }
+ };
+ None
+}
+
+// Finds the `#[must_use]` attribute, if any
+pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
+ attrs.iter().find(|a| a.has_name(sym::must_use))
+}
+
+// check if expr is calling method or function with #[must_use] attribute
+pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let did = match expr.kind {
+ ExprKind::Call(path, _) => if_chain! {
+ if let ExprKind::Path(ref qpath) = path.kind;
+ if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
+ then {
+ Some(did)
+ } else {
+ None
+ }
+ },
+ ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+ _ => None,
+ };
+
+ did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
+}
+
+/// Checks if an expression represents the identity function
+/// Only examines closures and `std::convert::identity`
+pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ /// Checks if a function's body represents the identity function. Looks for bodies of the form:
+ /// * `|x| x`
+ /// * `|x| return x`
+ /// * `|x| { return x }`
+ /// * `|x| { return x; }`
+ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+ let id = if_chain! {
+ if let [param] = func.params;
+ if let PatKind::Binding(_, id, _, _) = param.pat.kind;
+ then {
+ id
+ } else {
+ return false;
+ }
+ };
+
+ let mut expr = &func.value;
+ loop {
+ match expr.kind {
+ #[rustfmt::skip]
+ ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
+ | ExprKind::Ret(Some(e)) => expr = e,
+ #[rustfmt::skip]
+ ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
+ if_chain! {
+ if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
+ if let ExprKind::Ret(Some(ret_val)) = e.kind;
+ then {
+ expr = ret_val;
+ } else {
+ return false;
+ }
+ }
+ },
+ _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
+ }
+ }
+ }
+
+ match expr.kind {
+ ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
+ ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
+ _ => false,
+ }
+}
+
+/// Gets the node where an expression is either used, or it's type is unified with another branch.
+pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
+ let mut child_id = expr.hir_id;
+ let mut iter = tcx.hir().parent_iter(child_id);
+ loop {
+ match iter.next() {
+ None => break None,
+ Some((id, Node::Block(_))) => child_id = id,
+ Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
+ Some((_, Node::Expr(expr))) => match expr.kind {
+ ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
+ ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
+ ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
+ _ => break Some(Node::Expr(expr)),
+ },
+ Some((_, node)) => break Some(node),
+ }
+ }
+}
+
+/// Checks if the result of an expression is used, or it's type is unified with another branch.
+pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ !matches!(
+ get_expr_use_or_unification_node(tcx, expr),
+ None | Some(Node::Stmt(Stmt {
+ kind: StmtKind::Expr(_)
+ | StmtKind::Semi(_)
+ | StmtKind::Local(Local {
+ pat: Pat {
+ kind: PatKind::Wild,
+ ..
+ },
+ ..
+ }),
+ ..
+ }))
+ )
+}
+
+/// Checks if the expression is the final expression returned from a block.
+pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
+}
+
+pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
+ cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
+ if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
+ attr.path == sym::no_std
+ } else {
+ false
+ }
+ })
+}
+
+/// Check if parent of a hir node is a trait implementation block.
+/// For example, `f` in
+/// ```rust,ignore
+/// impl Trait for S {
+/// fn f() {}
+/// }
+/// ```
+pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+ if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
+ matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
+ } else {
+ false
+ }
+}
+
+/// Check if it's even possible to satisfy the `where` clause for the item.
+///
+/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
+///
+/// ```ignore
+/// fn foo() where i32: Iterator {
+/// for _ in 2i32 {}
+/// }
+/// ```
+pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
+ use rustc_trait_selection::traits;
+ let predicates = cx
+ .tcx
+ .predicates_of(did)
+ .predicates
+ .iter()
+ .filter_map(|(p, _)| if p.is_global(cx.tcx) { Some(*p) } else { None });
+ traits::impossible_predicates(
+ cx.tcx,
+ traits::elaborate_predicates(cx.tcx, predicates)
+ .map(|o| o.predicate)
+ .collect::<Vec<_>>(),
+ )
+}
+
+/// Returns the `DefId` of the callee if the given expression is a function or method call.
+pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
+ match &expr.kind {
+ ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(qpath),
+ hir_id: path_hir_id,
+ ..
+ },
+ ..,
+ ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(),
+ _ => None,
+ }
+}
+
+/// Returns Option<String> where String is a textual representation of the type encapsulated in the
+/// slice iff the given expression is a slice of primitives (as defined in the
+/// `is_recursively_primitive_type` function) and None otherwise.
+pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
+ let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
+ let expr_kind = expr_type.kind();
+ let is_primitive = match expr_kind {
+ rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type),
+ rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
+ if let rustc_ty::Slice(element_type) = inner_ty.kind() {
+ is_recursively_primitive_type(element_type)
+ } else {
+ unreachable!()
+ }
+ },
+ _ => false,
+ };
+
+ if is_primitive {
+ // if we have wrappers like Array, Slice or Tuple, print these
+ // and get the type enclosed in the slice ref
+ match expr_type.peel_refs().walk(cx.tcx).nth(1).unwrap().expect_ty().kind() {
+ rustc_ty::Slice(..) => return Some("slice".into()),
+ rustc_ty::Array(..) => return Some("array".into()),
+ rustc_ty::Tuple(..) => return Some("tuple".into()),
+ _ => {
+ // is_recursively_primitive_type() should have taken care
+ // of the rest and we can rely on the type that is found
+ let refs_peeled = expr_type.peel_refs();
+ return Some(refs_peeled.walk(cx.tcx).last().unwrap().to_string());
+ },
+ }
+ }
+ None
+}
+
+/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
+/// `hash` must be comformed with `eq`
+pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
+where
+ Hash: Fn(&T) -> u64,
+ Eq: Fn(&T, &T) -> bool,
+{
+ match exprs {
+ [a, b] if eq(a, b) => return vec![(a, b)],
+ _ if exprs.len() <= 2 => return vec![],
+ _ => {},
+ }
+
+ let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
+
+ let mut map: UnhashMap<u64, Vec<&_>> =
+ UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
+
+ for expr in exprs {
+ match map.entry(hash(expr)) {
+ Entry::Occupied(mut o) => {
+ for o in o.get() {
+ if eq(o, expr) {
+ match_expr_list.push((o, expr));
+ }
+ }
+ o.get_mut().push(expr);
+ },
+ Entry::Vacant(v) => {
+ v.insert(vec![expr]);
+ },
+ }
+ }
+
+ match_expr_list
+}
+
+/// Peels off all references on the pattern. Returns the underlying pattern and the number of
+/// references removed.
+pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
+ fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
+ if let PatKind::Ref(pat, _) = pat.kind {
+ peel(pat, count + 1)
+ } else {
+ (pat, count)
+ }
+ }
+ peel(pat, 0)
+}
+
+/// Peels of expressions while the given closure returns `Some`.
+pub fn peel_hir_expr_while<'tcx>(
+ mut expr: &'tcx Expr<'tcx>,
+ mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
+) -> &'tcx Expr<'tcx> {
+ while let Some(e) = f(expr) {
+ expr = e;
+ }
+ expr
+}
+
+/// Peels off up to the given number of references on the expression. Returns the underlying
+/// expression and the number of references removed.
+pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
+ let mut remaining = count;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
+ remaining -= 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (e, count - remaining)
+}
+
+/// Peels off all references on the expression. Returns the underlying expression and the number of
+/// references removed.
+pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
+ let mut count = 0;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
+ count += 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (e, count)
+}
+
+/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
+/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
+pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
+ loop {
+ match expr.kind {
+ ExprKind::AddrOf(_, _, e) => expr = e,
+ ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
+ _ => break,
+ }
+ }
+ expr
+}
+
+#[macro_export]
+macro_rules! unwrap_cargo_metadata {
+ ($cx: ident, $lint: ident, $deps: expr) => {{
+ let mut command = cargo_metadata::MetadataCommand::new();
+ if !$deps {
+ command.no_deps();
+ }
+
+ match command.exec() {
+ Ok(metadata) => metadata,
+ Err(err) => {
+ span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
+ return;
+ },
+ }
+ }};
+}
+
+pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+ if let Res::Def(_, def_id) = path.res {
+ return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
+ }
+ }
+ false
+}
+
+struct VisitConstTestStruct<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ names: Vec<Symbol>,
+ found: bool,
+}
+impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> {
+ fn visit_item(&mut self, item: &Item<'_>) {
+ if let ItemKind::Const(ty, _body) = item.kind {
+ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+ // We could also check for the type name `test::TestDescAndFn`
+ // and the `#[rustc_test_marker]` attribute?
+ if let Res::Def(DefKind::Struct, _) = path.res {
+ let has_test_marker = self
+ .tcx
+ .hir()
+ .attrs(item.hir_id())
+ .iter()
+ .any(|a| a.has_name(sym::rustc_test_marker));
+ if has_test_marker && self.names.contains(&item.ident.name) {
+ self.found = true;
+ }
+ }
+ }
+ }
+ }
+ fn visit_trait_item(&mut self, _: &TraitItem<'_>) {}
+ fn visit_impl_item(&mut self, _: &ImplItem<'_>) {}
+ fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
+}
+
+/// Checks if the function containing the given `HirId` is a `#[test]` function
+///
+/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
+pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
+ let names: Vec<_> = tcx
+ .hir()
+ .parent_iter(id)
+ // Since you can nest functions we need to collect all until we leave
+ // function scope
+ .filter_map(|(_id, node)| {
+ if let Node::Item(item) = node {
+ if let ItemKind::Fn(_, _, _) = item.kind {
+ return Some(item.ident.name);
+ }
+ }
+ None
+ })
+ .collect();
+ let parent_mod = tcx.parent_module(id);
+ let mut vis = VisitConstTestStruct {
+ tcx,
+ names,
+ found: false,
+ };
+ tcx.hir().visit_item_likes_in_module(parent_mod, &mut vis);
+ vis.found
+}
+
++/// Checks whether item either has `test` attribute applied, or
+/// is a module with `test` in its name.
+///
+/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
+pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
+ is_in_test_function(tcx, item.hir_id())
+ || matches!(item.kind, ItemKind::Mod(..))
+ && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
+}
+
+macro_rules! op_utils {
+ ($($name:ident $assign:ident)*) => {
+ /// Binary operation traits like `LangItem::Add`
+ pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
+
+ /// Operator-Assign traits like `LangItem::AddAssign`
+ pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
+
+ /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
+ pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
+ match kind {
+ $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
+ _ => None,
+ }
+ }
+ };
+}
+
+op_utils! {
+ Add AddAssign
+ Sub SubAssign
+ Mul MulAssign
+ Div DivAssign
+ Rem RemAssign
+ BitXor BitXorAssign
+ BitAnd BitAndAssign
+ BitOr BitOrAssign
+ Shl ShlAssign
+ Shr ShrAssign
+}
--- /dev/null
--- /dev/null
++/// Dealing with sting indices can be hard, this struct ensures that both the
++/// character and byte index are provided for correct indexing.
++#[derive(Debug, Default, PartialEq, Eq)]
++pub struct StrIndex {
++ pub char_index: usize,
++ pub byte_index: usize,
++}
++
++impl StrIndex {
++ pub fn new(char_index: usize, byte_index: usize) -> Self {
++ Self { char_index, byte_index }
++ }
++}
++
++/// Returns the index of the character after the first camel-case component of `s`.
++///
++/// ```
++/// assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
++/// assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
++/// assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3));
++/// assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7));
++/// ```
++#[must_use]
++pub fn camel_case_until(s: &str) -> StrIndex {
++ let mut iter = s.char_indices().enumerate();
++ if let Some((_char_index, (_, first))) = iter.next() {
++ if !first.is_uppercase() {
++ return StrIndex::new(0, 0);
++ }
++ } else {
++ return StrIndex::new(0, 0);
++ }
++ let mut up = true;
++ let mut last_index = StrIndex::new(0, 0);
++ for (char_index, (byte_index, c)) in iter {
++ if up {
++ if c.is_lowercase() {
++ up = false;
++ } else {
++ return last_index;
++ }
++ } else if c.is_uppercase() {
++ up = true;
++ last_index.byte_index = byte_index;
++ last_index.char_index = char_index;
++ } else if !c.is_lowercase() {
++ return StrIndex::new(char_index, byte_index);
++ }
++ }
++
++ if up {
++ last_index
++ } else {
++ StrIndex::new(s.chars().count(), s.len())
++ }
++}
++
++/// Returns index of the last camel-case component of `s`.
++///
++/// ```
++/// assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
++/// assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
++/// assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4));
++/// assert_eq!(camel_case_start("abcd"), StrIndex::new(4, 4));
++/// assert_eq!(camel_case_start("\u{f6}\u{f6}cd"), StrIndex::new(4, 6));
++/// ```
++#[must_use]
++pub fn camel_case_start(s: &str) -> StrIndex {
++ let char_count = s.chars().count();
++ let range = 0..char_count;
++ let mut iter = range.rev().zip(s.char_indices().rev());
++ if let Some((char_index, (_, first))) = iter.next() {
++ if !first.is_lowercase() {
++ return StrIndex::new(char_index, s.len());
++ }
++ } else {
++ return StrIndex::new(char_count, s.len());
++ }
++ let mut down = true;
++ let mut last_index = StrIndex::new(char_count, s.len());
++ for (char_index, (byte_index, c)) in iter {
++ if down {
++ if c.is_uppercase() {
++ down = false;
++ last_index.byte_index = byte_index;
++ last_index.char_index = char_index;
++ } else if !c.is_lowercase() {
++ return last_index;
++ }
++ } else if c.is_lowercase() {
++ down = true;
++ } else if c.is_uppercase() {
++ last_index.byte_index = byte_index;
++ last_index.char_index = char_index;
++ } else {
++ return last_index;
++ }
++ }
++ last_index
++}
++
++/// Dealing with sting comparison can be complicated, this struct ensures that both the
++/// character and byte count are provided for correct indexing.
++#[derive(Debug, Default, PartialEq, Eq)]
++pub struct StrCount {
++ pub char_count: usize,
++ pub byte_count: usize,
++}
++
++impl StrCount {
++ pub fn new(char_count: usize, byte_count: usize) -> Self {
++ Self { char_count, byte_count }
++ }
++}
++
++/// Returns the number of chars that match from the start
++///
++/// ```
++/// assert_eq!(count_match_start("hello_mouse", "hello_penguin"), StrCount::new(6, 6));
++/// assert_eq!(count_match_start("hello_clippy", "bye_bugs"), StrCount::new(0, 0));
++/// assert_eq!(count_match_start("hello_world", "hello_world"), StrCount::new(11, 11));
++/// assert_eq!(count_match_start("T\u{f6}ffT\u{f6}ff", "T\u{f6}ff"), StrCount::new(4, 5));
++/// ```
++#[must_use]
++pub fn count_match_start(str1: &str, str2: &str) -> StrCount {
++ // (char_index, char1)
++ let char_count = str1.chars().count();
++ let iter1 = (0..=char_count).zip(str1.chars());
++ // (byte_index, char2)
++ let iter2 = str2.char_indices();
++
++ iter1
++ .zip(iter2)
++ .take_while(|((_, c1), (_, c2))| c1 == c2)
++ .last()
++ .map_or_else(StrCount::default, |((char_index, _), (byte_index, character))| {
++ StrCount::new(char_index + 1, byte_index + character.len_utf8())
++ })
++}
++
++/// Returns the number of chars and bytes that match from the end
++///
++/// ```
++/// assert_eq!(count_match_end("hello_cat", "bye_cat"), StrCount::new(4, 4));
++/// assert_eq!(count_match_end("if_item_thing", "enum_value"), StrCount::new(0, 0));
++/// assert_eq!(count_match_end("Clippy", "Clippy"), StrCount::new(6, 6));
++/// assert_eq!(count_match_end("MyT\u{f6}ff", "YourT\u{f6}ff"), StrCount::new(4, 5));
++/// ```
++#[must_use]
++pub fn count_match_end(str1: &str, str2: &str) -> StrCount {
++ let char_count = str1.chars().count();
++ if char_count == 0 {
++ return StrCount::default();
++ }
++
++ // (char_index, char1)
++ let iter1 = (0..char_count).rev().zip(str1.chars().rev());
++ // (byte_index, char2)
++ let byte_count = str2.len();
++ let iter2 = str2.char_indices().rev();
++
++ iter1
++ .zip(iter2)
++ .take_while(|((_, c1), (_, c2))| c1 == c2)
++ .last()
++ .map_or_else(StrCount::default, |((char_index, _), (byte_index, _))| {
++ StrCount::new(char_count - char_index, byte_count - byte_index)
++ })
++}
++
++#[cfg(test)]
++mod test {
++ use super::*;
++
++ #[test]
++ fn camel_case_start_full() {
++ assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
++ assert_eq!(camel_case_start("Abc"), StrIndex::new(0, 0));
++ assert_eq!(camel_case_start("ABcd"), StrIndex::new(0, 0));
++ assert_eq!(camel_case_start("ABcdEf"), StrIndex::new(0, 0));
++ assert_eq!(camel_case_start("AabABcd"), StrIndex::new(0, 0));
++ }
++
++ #[test]
++ fn camel_case_start_partial() {
++ assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
++ assert_eq!(camel_case_start("aDbc"), StrIndex::new(1, 1));
++ assert_eq!(camel_case_start("aabABcd"), StrIndex::new(3, 3));
++ assert_eq!(camel_case_start("\u{f6}\u{f6}AabABcd"), StrIndex::new(2, 4));
++ }
++
++ #[test]
++ fn camel_case_start_not() {
++ assert_eq!(camel_case_start("AbcDef_"), StrIndex::new(7, 7));
++ assert_eq!(camel_case_start("AbcDD"), StrIndex::new(5, 5));
++ assert_eq!(camel_case_start("all_small"), StrIndex::new(9, 9));
++ assert_eq!(camel_case_start("\u{f6}_all_small"), StrIndex::new(11, 12));
++ }
++
++ #[test]
++ fn camel_case_start_caps() {
++ assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4));
++ }
++
++ #[test]
++ fn camel_case_until_full() {
++ assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
++ assert_eq!(camel_case_until("Abc"), StrIndex::new(3, 3));
++ assert_eq!(camel_case_until("Abc\u{f6}\u{f6}\u{f6}"), StrIndex::new(6, 9));
++ }
++
++ #[test]
++ fn camel_case_until_not() {
++ assert_eq!(camel_case_until("abcDef"), StrIndex::new(0, 0));
++ assert_eq!(camel_case_until("aDbc"), StrIndex::new(0, 0));
++ }
++
++ #[test]
++ fn camel_case_until_partial() {
++ assert_eq!(camel_case_until("AbcDef_"), StrIndex::new(6, 6));
++ assert_eq!(camel_case_until("CallTypeC"), StrIndex::new(8, 8));
++ assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3));
++ assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7));
++ }
++
++ #[test]
++ fn until_caps() {
++ assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
++ }
++}
--- /dev/null
- `clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to
- register the new lint. For cargo lints, two project hierarchies (fail/pass) will
- be created by default under `tests/ui-cargo`.
+# Adding a new lint
+
+You are probably here because you want to add a new lint to Clippy. If this is
+the first time you're contributing to Clippy, this document guides you through
+creating an example lint from scratch.
+
+To get started, we will create a lint that detects functions called `foo`,
+because that's clearly a non-descriptive name.
+
+- [Adding a new lint](#adding-a-new-lint)
+ - [Setup](#setup)
+ - [Getting Started](#getting-started)
+ - [Testing](#testing)
+ - [Cargo lints](#cargo-lints)
+ - [Rustfix tests](#rustfix-tests)
+ - [Edition 2018 tests](#edition-2018-tests)
+ - [Testing manually](#testing-manually)
+ - [Lint declaration](#lint-declaration)
++ - [Lint registration](#lint-registration)
+ - [Lint passes](#lint-passes)
+ - [Emitting a lint](#emitting-a-lint)
+ - [Adding the lint logic](#adding-the-lint-logic)
+ - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
+ - [Author lint](#author-lint)
+ - [Documentation](#documentation)
+ - [Running rustfmt](#running-rustfmt)
+ - [Debugging](#debugging)
+ - [PR Checklist](#pr-checklist)
+ - [Adding configuration to a lint](#adding-configuration-to-a-lint)
+ - [Cheatsheet](#cheatsheet)
+
+## Setup
+
+See the [Basics](basics.md#get-the-code) documentation.
+
+## Getting Started
+
+There is a bit of boilerplate code that needs to be set up when creating a new
+lint. Fortunately, you can use the clippy dev tools to handle this for you. We
+are naming our new lint `foo_functions` (lints are generally written in snake
+case), and we don't need type information so it will have an early pass type
+(more on this later on). If you're not sure if the name you chose fits the lint,
+take a look at our [lint naming guidelines][lint_naming]. To get started on this
+lint you can run `cargo dev new_lint --name=foo_functions --pass=early
+--category=pedantic` (category will default to nursery if not provided). This
+command will create two files: `tests/ui/foo_functions.rs` and
- Normally after declaring the lint, we have to run `cargo dev update_lints`,
- which updates some files, so Clippy knows about the new lint. Since we used
- `cargo dev new_lint ...` to generate the lint declaration, this was done
- automatically. While `update_lints` automates most of the things, it doesn't
- automate everything. We will have to register our lint pass manually in the
- `register_plugins` function in `clippy_lints/src/lib.rs`:
++`clippy_lints/src/foo_functions.rs`, as well as
++[registering the lint](#lint-registration). For cargo lints, two project
++hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
+
+Next, we'll open up these files and add our lint!
+
+## Testing
+
+Let's write some tests first that we can execute while we iterate on our lint.
+
+Clippy uses UI tests for testing. UI tests check that the output of Clippy is
+exactly as expected. Each test is just a plain Rust file that contains the code
+we want to check. The output of Clippy is compared against a `.stderr` file.
+Note that you don't have to create this file yourself, we'll get to
+generating the `.stderr` files further down.
+
+We start by opening the test file created at `tests/ui/foo_functions.rs`.
+
+Update the file with some examples to get started:
+
+```rust
+#![warn(clippy::foo_functions)]
+
+// Impl methods
+struct A;
+impl A {
+ pub fn fo(&self) {}
+ pub fn foo(&self) {}
+ pub fn food(&self) {}
+}
+
+// Default trait methods
+trait B {
+ fn fo(&self) {}
+ fn foo(&self) {}
+ fn food(&self) {}
+}
+
+// Plain functions
+fn fo() {}
+fn foo() {}
+fn food() {}
+
+fn main() {
+ // We also don't want to lint method calls
+ foo();
+ let a = A;
+ a.foo();
+}
+```
+
+Now we can run the test with `TESTNAME=foo_functions cargo uitest`,
+currently this test is meaningless though.
+
+While we are working on implementing our lint, we can keep running the UI
+test. That allows us to check if the output is turning into what we want.
+
+Once we are satisfied with the output, we need to run
+`cargo dev bless` to update the `.stderr` file for our lint.
+Please note that, we should run `TESTNAME=foo_functions cargo uitest`
+every time before running `cargo dev bless`.
+Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
+our lint, we need to commit the generated `.stderr` files, too. In general, you
+should only commit files changed by `cargo dev bless` for the
+specific lint you are creating/editing. Note that if the generated files are
+empty, they should be removed.
+
+Note that you can run multiple test files by specifying a comma separated list:
+`TESTNAME=foo_functions,test2,test3`.
+
+### Cargo lints
+
+For cargo lints, the process of testing differs in that we are interested in
+the `Cargo.toml` manifest file. We also need a minimal crate associated
+with that manifest.
+
+If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
+we will find by default two new crates, each with its manifest file:
+
+* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
+* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint.
+
+If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
+
+The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
+variable to `cargo uitest` works too.
+
+## Rustfix tests
+
+If the lint you are working on is making use of structured suggestions, the
+test file should include a `// run-rustfix` comment at the top. This will
+additionally run [rustfix] for that test. Rustfix will apply the suggestions
+from the lint to the code of the test file and compare that to the contents of
+a `.fixed` file.
+
+Use `cargo dev bless` to automatically generate the
+`.fixed` file after running the tests.
+
+[rustfix]: https://github.com/rust-lang/rustfix
+
+## Edition 2018 tests
+
+Some features require the 2018 edition to work (e.g. `async_await`), but
+compile-test tests run on the 2015 edition by default. To change this behavior
+add `// edition:2018` at the top of the test file (note that it's space-sensitive).
+
+## Testing manually
+
+Manually testing against an example file can be useful if you have added some
+`println!`s and the test suite output becomes unreadable. To try Clippy with
+your local modifications, run
+
+```
+env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs
+```
+
+from the working copy root. With tests in place, let's have a look at
+implementing our lint now.
+
+## Lint declaration
+
+Let's start by opening the new file created in the `clippy_lints` crate
+at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
+lint code is. This file has already imported some initial things we will need:
+
+```rust
+use rustc_lint::{EarlyLintPass, EarlyContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_ast::ast::*;
+```
+
+The next step is to update the lint declaration. Lints are declared using the
+[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
+the auto-generated lint declaration to have a real description, something like this:
+
+```rust
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// ### Why is this bad?
+ ///
+ /// ### Example
+ /// ```rust
+ /// // example code
+ /// ```
+ pub FOO_FUNCTIONS,
+ pedantic,
+ "function named `foo`, which is not a descriptive name"
+}
+```
+
+* The section of lines prefixed with `///` constitutes the lint documentation
+ section. This is the default documentation style and will be displayed
+ [like this][example_lint_page]. To render and open this documentation locally
+ in a browser, run `cargo dev serve`.
+* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
+ [lint naming guidelines][lint_naming] here when naming your lint.
+ In short, the name should state the thing that is being checked for and
+ read well when used with `allow`/`warn`/`deny`.
+* `pedantic` sets the lint level to `Allow`.
+ The exact mapping can be found [here][category_level_mapping]
+* The last part should be a text that explains what exactly is wrong with the
+ code
+
+The rest of this file contains an empty implementation for our lint pass,
+which in this case is `EarlyLintPass` and should look like this:
+
+```rust
+// clippy_lints/src/foo_functions.rs
+
+// .. imports and lint declaration ..
+
+declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
+
+impl EarlyLintPass for FooFunctions {}
+```
+
- store.register_early_pass(|| box foo_functions::FooFunctions);
++[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
++[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
++[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
++
++## Lint registration
++
++When using `cargo dev new_lint`, the lint is automatically registered and
++nothing more has to be done.
++
++When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
++pass may have to be registered manually in the `register_plugins` function in
++`clippy_lints/src/lib.rs`:
+
+```rust
- One reason that `cargo dev` does not automate this step is that multiple lints
- can use the same lint pass, so registering the lint pass may already be done
- when adding a new lint. Another reason that this step is not automated is that
- the order that the passes are registered determines the order the passes
- actually run, which in turn affects the order that any emitted lints are output
- in.
-
- [declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
- [example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
- [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
- [category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
++store.register_early_pass(|| Box::new(foo_functions::FooFunctions));
+```
+
+As one may expect, there is a corresponding `register_late_pass` method
+available as well. Without a call to one of `register_early_pass` or
+`register_late_pass`, the lint pass in question will not be run.
+
- The doc comment will be automatically added to the lint documentation.
++One reason that `cargo dev update_lints` does not automate this step is that
++multiple lints can use the same lint pass, so registering the lint pass may
++already be done when adding a new lint. Another reason that this step is not
++automated is that the order that the passes are registered determines the order
++the passes actually run, which in turn affects the order that any emitted lints
++are output in.
+
+## Lint passes
+
+Writing a lint that only checks for the name of a function means that we only
+have to deal with the AST and don't have to deal with the type system at all.
+This is good, because it makes writing this particular lint less complicated.
+
+We have to make this decision with every new Clippy lint. It boils down to using
+either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
+
+In short, the `LateLintPass` has access to type information while the
+`EarlyLintPass` doesn't. If you don't need access to type information, use the
+`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
+hasn't really been a concern with Clippy so far.
+
+Since we don't need type information for checking the function name, we used
+`--pass=early` when running the new lint automation and all the imports were
+added accordingly.
+
+[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
+[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+
+## Emitting a lint
+
+With UI tests and the lint declaration in place, we can start working on the
+implementation of the lint logic.
+
+Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
+
+```rust
+impl EarlyLintPass for FooFunctions {
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
+ // TODO: Emit lint here
+ }
+}
+```
+
+We implement the [`check_fn`][check_fn] method from the
+[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
+information about the function that is currently being checked. More on that in
+the next section. Let's worry about the details later and emit our lint for
+*every* function definition first.
+
+Depending on how complex we want our lint message to be, we can choose from a
+variety of lint emission functions. They can all be found in
+[`clippy_utils/src/diagnostics.rs`][diagnostics].
+
+`span_lint_and_help` seems most appropriate in this case. It allows us to
+provide an extra help message and we can't really suggest a better name
+automatically. This is how it looks:
+
+```rust
+impl EarlyLintPass for FooFunctions {
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
+ span_lint_and_help(
+ cx,
+ FOO_FUNCTIONS,
+ span,
+ "function named `foo`",
+ None,
+ "consider using a more meaningful name"
+ );
+ }
+}
+```
+
+Running our UI test should now produce output that contains the lint message.
+
+According to [the rustc-dev-guide], the text should be matter of fact and avoid
+capitalization and periods, unless multiple sentences are needed.
+When code or an identifier must appear in a message or label, it should be
+surrounded with single grave accents \`.
+
+[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
+[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
+[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
+
+## Adding the lint logic
+
+Writing the logic for your lint will most likely be different from our example,
+so this section is kept rather short.
+
+Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
+that has the [`FnKind::Fn`] variant. It provides access to the name of the
+function/method via an [`Ident`][ident].
+
+With that we can expand our `check_fn` method to:
+
+```rust
+impl EarlyLintPass for FooFunctions {
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
+ if is_foo_fn(fn_kind) {
+ span_lint_and_help(
+ cx,
+ FOO_FUNCTIONS,
+ span,
+ "function named `foo`",
+ None,
+ "consider using a more meaningful name"
+ );
+ }
+ }
+}
+```
+
+We separate the lint conditional from the lint emissions because it makes the
+code a bit easier to read. In some cases this separation would also allow to
+write some unit tests (as opposed to only UI tests) for the separate function.
+
+In our example, `is_foo_fn` looks like:
+
+```rust
+// use statements, impl EarlyLintPass, check_fn, ..
+
+fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
+ match fn_kind {
+ FnKind::Fn(_, ident, ..) => {
+ // check if `fn` name is `foo`
+ ident.name.as_str() == "foo"
+ }
+ // ignore closures
+ FnKind::Closure(..) => false
+ }
+}
+```
+
+Now we should also run the full test suite with `cargo test`. At this point
+running `cargo test` should produce the expected output. Remember to run
+`cargo dev bless` to update the `.stderr` file.
+
+`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
+implementation is not violating any Clippy lints itself.
+
+That should be it for the lint implementation. Running `cargo test` should now
+pass.
+
+[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
+[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
+[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
+
+## Specifying the lint's minimum supported Rust version (MSRV)
+
+Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
+using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
+ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
+required, just use the one with a lower MSRV.
+
+First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
+accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
+
+```rust
+msrv_aliases! {
+ ..
+ 1,45,0 { STR_STRIP_PREFIX }
+}
+```
+
+In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
+constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
+
+```rust
+pub struct ManualStrip {
+ msrv: Option<RustcVersion>,
+}
+
+impl ManualStrip {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self { msrv }
+ }
+}
+```
+
+The project's MSRV can then be matched against the feature MSRV in the LintPass
+using the `meets_msrv` utility function.
+
+``` rust
+if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
+ return;
+}
+```
+
+The project's MSRV can also be specified as an inner attribute, which overrides
+the value from `clippy.toml`. This can be accounted for using the
+`extract_msrv_attr!(LintContext)` macro and passing
+`LateContext`/`EarlyContext`.
+
+```rust
+impl<'tcx> LateLintPass<'tcx> for ManualStrip {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ ...
+ }
+ extract_msrv_attr!(LateContext);
+}
+```
+
+Once the `msrv` is added to the lint, a relevant test case should be added to
+`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted
+if the project's MSRV is lower.
+
+As a last step, the lint should be added to the lint documentation. This is done
+in `clippy_lints/src/utils/conf.rs`:
+
+```rust
+define_Conf! {
+ /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
+ (msrv: Option<String> = None),
+ ...
+}
+```
+
+## Author lint
+
+If you have trouble implementing your lint, there is also the internal `author`
+lint to generate Clippy code that detects the offending pattern. It does not
+work for all of the Rust syntax, but can give a good starting point.
+
+The quickest way to use it, is the
+[Rust playground: play.rust-lang.org][author_example].
+Put the code you want to lint into the editor and add the `#[clippy::author]`
+attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
+see the generated code in the output below.
+
+[Here][author_example] is an example on the playground.
+
+If the command was executed successfully, you can copy the code over to where
+you are implementing your lint.
+
+[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
+
+## Documentation
+
+The final thing before submitting our PR is to add some documentation to our
+lint declaration.
+
+Please document your lint with a doc comment akin to the following:
+
+```rust
+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,ignore
+ /// // 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 FOO_FUNCTIONS,
+ pedantic,
+ "function named `foo`, which is not a descriptive name"
+}
+```
+
+Once your lint is merged, this documentation will show up in the [lint
+list][lint_list].
+
+[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
+
+## Running rustfmt
+
+[Rustfmt] is a tool for formatting Rust code according to style guidelines.
+Your code has to be formatted by `rustfmt` before a PR can be merged.
+Clippy uses nightly `rustfmt` in the CI.
+
+It can be installed via `rustup`:
+
+```bash
+rustup component add rustfmt --toolchain=nightly
+```
+
+Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
+installed for the nightly toolchain.
+
+[Rustfmt]: https://github.com/rust-lang/rustfmt
+
+## Debugging
+
+If you want to debug parts of your lint implementation, you can use the [`dbg!`]
+macro anywhere in your code. Running the tests should then include the debug
+output in the `stdout` part.
+
+[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
+
+## PR Checklist
+
+Before submitting your PR make sure you followed all of the basic requirements:
+
+<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
+
+- \[ ] Followed [lint naming conventions][lint_naming]
+- \[ ] Added passing UI tests (including committed `.stderr` file)
+- \[ ] `cargo test` passes locally
+- \[ ] Executed `cargo dev update_lints`
+- \[ ] Added lint documentation
+- \[ ] Run `cargo dev fmt`
+
+## Adding configuration to a lint
+
+Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace
+directory. Adding a configuration to a lint can be useful for thresholds or to constrain some
+behavior that can be seen as a false positive for some users. Adding a configuration is done
+in the following steps:
+
+1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
+ like this:
+ ```rust
+ /// Lint: LINT_NAME.
+ ///
+ /// <The configuration field doc comment>
+ (configuration_ident: Type = DefaultValue),
+ ```
++ The doc comment is automatically added to the documentation of the listed lints. The default
++ value will be formatted using the `Debug` implementation of the type.
+2. Adding the configuration value to the lint impl struct:
+ 1. This first requires the definition of a lint impl struct. Lint impl structs are usually
+ generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
+ to add some kind of metadata to it:
+ ```rust
+ // Generated struct definition
+ declare_lint_pass!(StructName => [
+ LINT_NAME
+ ]);
+
+ // New manual definition struct
+ #[derive(Copy, Clone)]
+ pub struct StructName {}
+
+ impl_lint_pass!(StructName => [
+ LINT_NAME
+ ]);
+ ```
+
+ 2. Next add the configuration value and a corresponding creation method like this:
+ ```rust
+ #[derive(Copy, Clone)]
+ pub struct StructName {
+ configuration_ident: Type,
+ }
+
+ // ...
+
+ impl StructName {
+ pub fn new(configuration_ident: Type) -> Self {
+ Self {
+ configuration_ident,
+ }
+ }
+ }
+ ```
+3. Passing the configuration value to the lint impl struct:
+
+ First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs).
+ The configuration value is now cloned or copied into a local value that is then passed to the
+ impl struct like this:
+ ```rust
+ // Default generated registration:
+ store.register_*_pass(|| box module::StructName);
+
+ // New registration with configuration value
+ let configuration_ident = conf.configuration_ident.clone();
+ store.register_*_pass(move || box module::StructName::new(configuration_ident));
+ ```
+
+ Congratulations the work is almost done. The configuration value can now be accessed
+ in the linting code via `self.configuration_ident`.
+
+4. Adding tests:
+ 1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui).
+ 2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml).
+ Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file
+ with the configuration value and a rust file that should be linted by Clippy. The test can
+ otherwise be written as usual.
+
+## Cheatsheet
+
+Here are some pointers to things you are likely going to need for every lint:
+
+* [Clippy utils][utils] - Various helper functions. Maybe the function you need
+ is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
+* [Clippy diagnostics][diagnostics]
+* [The `if_chain` macro][if_chain]
+* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
+* [`Span`][span]
+* [`Applicability`][applicability]
+* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
+* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
+* [The nightly rustc docs][nightly_docs] which has been linked to throughout
+ this guide
+
+For `EarlyLintPass` lints:
+
+* [`EarlyLintPass`][early_lint_pass]
+* [`rustc_ast::ast`][ast]
+
+For `LateLintPass` lints:
+
+* [`LateLintPass`][late_lint_pass]
+* [`Ty::TyKind`][ty]
+
+While most of Clippy's lint utils are documented, most of rustc's internals lack
+documentation currently. This is unfortunate, but in most cases you can probably
+get away with copying things from existing similar lints. If you are stuck,
+don't hesitate to ask on [Zulip] or in the issue/PR.
+
+[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
+[if_chain]: https://docs.rs/if_chain/*/if_chain/
+[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
+[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
+[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
+[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
+[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
+[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
+[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
+[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
+[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
--- /dev/null
- channel = "nightly-2021-10-21"
+[toolchain]
++channel = "nightly-2021-11-04"
+components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
--- /dev/null
- let mut config = compiletest::Config::default();
+#![feature(test)] // compiletest_rs requires this attribute
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
+use compiletest_rs as compiletest;
+use compiletest_rs::common::Mode as TestMode;
+
+use std::collections::HashMap;
+use std::env::{self, remove_var, set_var, var_os};
+use std::ffi::{OsStr, OsString};
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+
+mod cargo;
+
+// whether to run internal tests or not
+const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
+
+/// All crates used in UI tests are listed here
+static TEST_DEPENDENCIES: &[&str] = &[
+ "clippy_utils",
+ "derive_new",
+ "if_chain",
+ "itertools",
+ "quote",
+ "regex",
+ "serde",
+ "serde_derive",
+ "syn",
+];
+
+// Test dependencies may need an `extern crate` here to ensure that they show up
+// in the depinfo file (otherwise cargo thinks they are unused)
+#[allow(unused_extern_crates)]
+extern crate clippy_utils;
+#[allow(unused_extern_crates)]
+extern crate derive_new;
+#[allow(unused_extern_crates)]
+extern crate if_chain;
+#[allow(unused_extern_crates)]
+extern crate itertools;
+#[allow(unused_extern_crates)]
+extern crate quote;
+#[allow(unused_extern_crates)]
+extern crate syn;
+
+/// Produces a string with an `--extern` flag for all UI test crate
+/// dependencies.
+///
+/// The dependency files are located by parsing the depinfo file for this test
+/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
+/// dependencies must be added to Cargo.toml at the project root. Test
+/// dependencies that are not *directly* used by this test module require an
+/// `extern crate` declaration.
+fn extern_flags() -> String {
+ let current_exe_depinfo = {
+ let mut path = env::current_exe().unwrap();
+ path.set_extension("d");
+ std::fs::read_to_string(path).unwrap()
+ };
+ let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
+ for line in current_exe_depinfo.lines() {
+ // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
+ let parse_name_path = || {
+ if line.starts_with(char::is_whitespace) {
+ return None;
+ }
+ let path_str = line.strip_suffix(':')?;
+ let path = Path::new(path_str);
+ if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
+ return None;
+ }
+ let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
+ // the "lib" prefix is not present for dll files
+ let name = name.strip_prefix("lib").unwrap_or(name);
+ Some((name, path_str))
+ };
+ if let Some((name, path)) = parse_name_path() {
+ if TEST_DEPENDENCIES.contains(&name) {
+ // A dependency may be listed twice if it is available in sysroot,
+ // and the sysroot dependencies are listed first. As of the writing,
+ // this only seems to apply to if_chain.
+ crates.insert(name, path);
+ }
+ }
+ }
+ let not_found: Vec<&str> = TEST_DEPENDENCIES
+ .iter()
+ .copied()
+ .filter(|n| !crates.contains_key(n))
+ .collect();
+ assert!(
+ not_found.is_empty(),
+ "dependencies not found in depinfo: {:?}\n\
+ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
+ help: Try adding to dev-dependencies in Cargo.toml",
+ not_found
+ );
+ crates
+ .into_iter()
+ .map(|(name, path)| format!(" --extern {}={}", name, path))
+ .collect()
+}
+
+fn default_config() -> compiletest::Config {
++ let mut config = compiletest::Config {
++ edition: Some("2021".into()),
++ ..compiletest::Config::default()
++ };
+
+ if let Ok(filters) = env::var("TESTNAME") {
+ config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
+ }
+
+ if let Some(path) = option_env!("RUSTC_LIB_PATH") {
+ let path = PathBuf::from(path);
+ config.run_lib_path = path.clone();
+ config.compile_lib_path = path;
+ }
+ let current_exe_path = std::env::current_exe().unwrap();
+ let deps_path = current_exe_path.parent().unwrap();
+ let profile_path = deps_path.parent().unwrap();
+
+ // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
+ // This is valuable because a) it allows us to monitor what external dependencies are used
+ // and b) it ensures that conflicting rlibs are resolved properly.
+ let host_libs = option_env!("HOST_LIBS")
+ .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
+ .unwrap_or_default();
+ config.target_rustcflags = Some(format!(
+ "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
+ deps_path.display(),
+ host_libs,
+ extern_flags(),
+ ));
+
+ config.build_base = profile_path.join("test");
+ config.rustc_path = profile_path.join(if cfg!(windows) {
+ "clippy-driver.exe"
+ } else {
+ "clippy-driver"
+ });
+ config
+}
+
+fn run_ui(cfg: &mut compiletest::Config) {
+ cfg.mode = TestMode::Ui;
+ cfg.src_base = Path::new("tests").join("ui");
+ // use tests/clippy.toml
+ let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
+ compiletest::run_tests(cfg);
+}
+
+fn run_ui_test(cfg: &mut compiletest::Config) {
+ cfg.mode = TestMode::Ui;
+ cfg.src_base = Path::new("tests").join("ui_test");
+ let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
+ let rustcflags = cfg.target_rustcflags.get_or_insert_with(Default::default);
+ let len = rustcflags.len();
+ rustcflags.push_str(" --test");
+ compiletest::run_tests(cfg);
+ if let Some(ref mut flags) = &mut cfg.target_rustcflags {
+ flags.truncate(len);
+ }
+}
+
+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_test(&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
- /*
- Test for missing files.
-
- Since rs files are alphabetically before stderr/stdout, we can sort by the full name
- and iter in that order. If we've seen the file stem for the first time and it's not
- a rust file, it means the rust file has to be missing.
- */
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+#![allow(clippy::assertions_on_constants)]
++#![feature(path_file_prefix)]
+
++use std::cmp::Ordering;
++use std::ffi::OsStr;
+use std::fs::{self, DirEntry};
+use std::path::Path;
+
+#[test]
+fn test_missing_tests() {
+ let missing_files = explore_directory(Path::new("./tests"));
+ if !missing_files.is_empty() {
+ assert!(
+ false,
+ "Didn't see a test file for the following files:\n\n{}\n",
+ missing_files
+ .iter()
+ .map(|s| format!("\t{}", s))
+ .collect::<Vec<_>>()
+ .join("\n")
+ );
+ }
+}
+
- files.sort_by_key(std::fs::DirEntry::path);
++// Test for missing files.
+fn explore_directory(dir: &Path) -> Vec<String> {
+ let mut missing_files: Vec<String> = Vec::new();
+ let mut current_file = String::new();
+ let mut files: Vec<DirEntry> = fs::read_dir(dir).unwrap().filter_map(Result::ok).collect();
- let file_stem = path.file_stem().unwrap().to_str().unwrap().to_string();
++ files.sort_by(|x, y| {
++ match x.path().file_prefix().cmp(&y.path().file_prefix()) {
++ Ordering::Equal => (),
++ ord => return ord,
++ }
++ // Sort rs files before the others if they share the same prefix. So when we see
++ // the file prefix for the first time and it's not a rust file, it means the rust
++ // file has to be missing.
++ match (
++ x.path().extension().and_then(OsStr::to_str),
++ y.path().extension().and_then(OsStr::to_str),
++ ) {
++ (Some("rs"), _) => Ordering::Less,
++ (_, Some("rs")) => Ordering::Greater,
++ _ => Ordering::Equal,
++ }
++ });
+ for entry in &files {
+ let path = entry.path();
+ if path.is_dir() {
+ missing_files.extend(explore_directory(&path));
+ } else {
- "rs" => current_file = file_stem.clone(),
++ let file_prefix = path.file_prefix().unwrap().to_str().unwrap().to_string();
+ if let Some(ext) = path.extension() {
+ match ext.to_str().unwrap() {
- if file_stem != current_file {
++ "rs" => current_file = file_prefix.clone(),
+ "stderr" | "stdout" => {
++ if file_prefix != current_file {
+ missing_files.push(path.to_str().unwrap().to_string());
+ }
+ },
+ _ => continue,
+ };
+ }
+ }
+ }
+ missing_files
+}
--- /dev/null
- // edition:2018
-
+#![warn(clippy::too_many_lines)]
+
+// This function should be considered one line.
+fn many_comments_but_one_line_of_code() {
+ /* println!("This is good."); */
+ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* println!("This is good.");
+ println!("This is good.");
+ println!("This is good."); */
+ println!("This is good.");
+}
+
+// This should be considered two and a fail.
+fn too_many_lines() {
+ println!("This is bad.");
+ println!("This is bad.");
+}
+
+// This should only fail once (#7517).
+async fn async_too_many_lines() {
+ println!("This is bad.");
+ println!("This is bad.");
+}
+
+// This should fail only once, without failing on the closure.
+fn closure_too_many_lines() {
+ let _ = {
+ println!("This is bad.");
+ println!("This is bad.");
+ };
+}
+
+// This should be considered one line.
+#[rustfmt::skip]
+fn comment_starts_after_code() {
+ let _ = 5; /* closing comment. */ /*
+ this line shouldn't be counted theoretically.
+ */
+}
+
+// This should be considered one line.
+fn comment_after_code() {
+ let _ = 5; /* this line should get counted once. */
+}
+
+// This should fail since it is technically two lines.
+#[rustfmt::skip]
+fn comment_before_code() {
+ let _ = "test";
+ /* This comment extends to the front of
+ the code but this line should still count. */ let _ = 5;
+}
+
+// This should be considered one line.
+fn main() {}
--- /dev/null
- --> $DIR/test.rs:20:1
+error: this function has too many lines (2/1)
- --> $DIR/test.rs:26:1
++ --> $DIR/test.rs:18:1
+ |
+LL | / fn too_many_lines() {
+LL | | println!("This is bad.");
+LL | | println!("This is bad.");
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::too-many-lines` implied by `-D warnings`
+
+error: this function has too many lines (4/1)
- --> $DIR/test.rs:32:1
++ --> $DIR/test.rs:24:1
+ |
+LL | / async fn async_too_many_lines() {
+LL | | println!("This is bad.");
+LL | | println!("This is bad.");
+LL | | }
+ | |_^
+
+error: this function has too many lines (4/1)
- --> $DIR/test.rs:54:1
++ --> $DIR/test.rs:30:1
+ |
+LL | / fn closure_too_many_lines() {
+LL | | let _ = {
+LL | | println!("This is bad.");
+LL | | println!("This is bad.");
+LL | | };
+LL | | }
+ | |_^
+
+error: this function has too many lines (2/1)
++ --> $DIR/test.rs:52:1
+ |
+LL | / fn comment_before_code() {
+LL | | let _ = "test";
+LL | | /* This comment extends to the front of
+LL | | the code but this line should still count. */ let _ = 5;
+LL | | }
+ | |_^
+
+error: aborting due to 4 previous errors
+
--- /dev/null
-
++//FIXME: suggestions are wrongly expanded, this should be fixed along with #7843
+#![allow(non_fmt_panics)]
+
+macro_rules! assert_const {
+ ($len:expr) => {
+ assert!($len > 0);
+ debug_assert!($len < 0);
+ };
+}
- assert!(false, msg.to_uppercase());
+fn main() {
+ assert!(true);
+ assert!(false);
+ assert!(true, "true message");
+ assert!(false, "false message");
+
+ let msg = "panic message";
++ assert!(false, "{}", msg.to_uppercase());
+
+ const B: bool = true;
+ assert!(B);
+
+ const C: bool = false;
+ assert!(C);
+ assert!(C, "C message");
+
+ debug_assert!(true);
+ // Don't lint this, since there is no better way for expressing "Only panic in debug mode".
+ debug_assert!(false); // #3948
+ assert_const!(3);
+ assert_const!(-1);
+
+ // Don't lint on this:
+ assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
+}
--- /dev/null
- error: `assert!(false, "false message")` should probably be replaced
+error: `assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:11:5
+ |
+LL | assert!(true);
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::assertions-on-constants` implied by `-D warnings`
+ = help: remove it
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `assert!(false)` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:12:5
+ |
+LL | assert!(false);
+ | ^^^^^^^^^^^^^^
+ |
+ = help: use `panic!()` or `unreachable!()`
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:13:5
+ |
+LL | assert!(true, "true message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove it
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
- = help: use `panic!("false message")` or `unreachable!("false message")`
- = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
-
- error: `assert!(false, msg.to_uppercase())` should probably be replaced
- --> $DIR/assertions_on_constants.rs:17:5
- |
- LL | assert!(false, msg.to_uppercase());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: use `panic!(msg.to_uppercase())` or `unreachable!(msg.to_uppercase())`
++error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:14:5
+ |
+LL | assert!(false, "false message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
- error: `assert!(false, "C message")` should probably be replaced
++ = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))`
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:20:5
+ |
+LL | assert!(B);
+ | ^^^^^^^^^^
+ |
+ = help: remove it
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `assert!(false)` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:23:5
+ |
+LL | assert!(C);
+ | ^^^^^^^^^^
+ |
+ = help: use `panic!()` or `unreachable!()`
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
- = help: use `panic!("C message")` or `unreachable!("C message")`
++error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:24:5
+ |
+LL | assert!(C, "C message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
- error: aborting due to 9 previous errors
++ = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))`
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `debug_assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:26:5
+ |
+LL | debug_assert!(true);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove it
+ = note: this error originates in the macro `$crate::assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
++error: aborting due to 8 previous errors
+
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![feature(async_closure)]
+#![warn(clippy::async_yields_async)]
+
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+struct CustomFutureType;
+
+impl Future for CustomFutureType {
+ type Output = u8;
+
+ fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
+ Poll::Ready(3)
+ }
+}
+
+fn custom_future_type_ctor() -> CustomFutureType {
+ CustomFutureType
+}
+
+async fn f() -> CustomFutureType {
+ // Don't warn for functions since you have to explicitly declare their
+ // return types.
+ CustomFutureType
+}
+
+#[rustfmt::skip]
+fn main() {
+ let _f = {
+ 3
+ };
+ let _g = async {
+ 3
+ };
+ let _h = async {
+ async {
+ 3
+ }.await
+ };
+ let _i = async {
+ CustomFutureType.await
+ };
+ let _i = async || {
+ 3
+ };
+ let _j = async || {
+ async {
+ 3
+ }.await
+ };
+ let _k = async || {
+ CustomFutureType.await
+ };
+ let _l = async || CustomFutureType.await;
+ let _m = async || {
+ println!("I'm bored");
+ // Some more stuff
+
+ // Finally something to await
+ CustomFutureType.await
+ };
+ let _n = async || custom_future_type_ctor();
+ let _o = async || f();
+}
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![feature(async_closure)]
+#![warn(clippy::async_yields_async)]
+
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+struct CustomFutureType;
+
+impl Future for CustomFutureType {
+ type Output = u8;
+
+ fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
+ Poll::Ready(3)
+ }
+}
+
+fn custom_future_type_ctor() -> CustomFutureType {
+ CustomFutureType
+}
+
+async fn f() -> CustomFutureType {
+ // Don't warn for functions since you have to explicitly declare their
+ // return types.
+ CustomFutureType
+}
+
+#[rustfmt::skip]
+fn main() {
+ let _f = {
+ 3
+ };
+ let _g = async {
+ 3
+ };
+ let _h = async {
+ async {
+ 3
+ }
+ };
+ let _i = async {
+ CustomFutureType
+ };
+ let _i = async || {
+ 3
+ };
+ let _j = async || {
+ async {
+ 3
+ }
+ };
+ let _k = async || {
+ CustomFutureType
+ };
+ let _l = async || CustomFutureType;
+ let _m = async || {
+ println!("I'm bored");
+ // Some more stuff
+
+ // Finally something to await
+ CustomFutureType
+ };
+ let _n = async || custom_future_type_ctor();
+ let _o = async || f();
+}
--- /dev/null
- --> $DIR/async_yields_async.rs:40:9
+error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:45:9
++ --> $DIR/async_yields_async.rs:39:9
+ |
+LL | let _h = async {
+ | ____________________-
+LL | | async {
+ | |_________^
+LL | || 3
+LL | || }
+ | ||_________^ awaitable value not awaited
+LL | | };
+ | |_____- outer async construct
+ |
+ = note: `-D clippy::async-yields-async` implied by `-D warnings`
+help: consider awaiting this value
+ |
+LL ~ async {
+LL + 3
+LL + }.await
+ |
+
+error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:51:9
++ --> $DIR/async_yields_async.rs:44:9
+ |
+LL | let _i = async {
+ | ____________________-
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:56:9
++ --> $DIR/async_yields_async.rs:50:9
+ |
+LL | let _j = async || {
+ | _______________________-
+LL | | async {
+ | |_________^
+LL | || 3
+LL | || }
+ | ||_________^ awaitable value not awaited
+LL | | };
+ | |_____- outer async construct
+ |
+help: consider awaiting this value
+ |
+LL ~ async {
+LL + 3
+LL + }.await
+ |
+
+error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:58:23
++ --> $DIR/async_yields_async.rs:55:9
+ |
+LL | let _k = async || {
+ | _______________________-
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:64:9
++ --> $DIR/async_yields_async.rs:57:23
+ |
+LL | let _l = async || CustomFutureType;
+ | ^^^^^^^^^^^^^^^^
+ | |
+ | outer async construct
+ | awaitable value not awaited
+ | help: consider awaiting this value: `CustomFutureType.await`
+
+error: an async construct yields a type which is itself awaitable
++ --> $DIR/async_yields_async.rs:63:9
+ |
+LL | let _m = async || {
+ | _______________________-
+LL | | println!("I'm bored");
+LL | | // Some more stuff
+LL | |
+LL | | // Finally something to await
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: aborting due to 6 previous errors
+
--- /dev/null
- // edition:2018
+#![warn(clippy::await_holding_lock)]
+
+use std::sync::Mutex;
+
+async fn bad(x: &Mutex<u32>) -> u32 {
+ let guard = x.lock().unwrap();
+ baz().await
+}
+
+async fn good(x: &Mutex<u32>) -> u32 {
+ {
+ let guard = x.lock().unwrap();
+ let y = *guard + 1;
+ }
+ baz().await;
+ let guard = x.lock().unwrap();
+ 47
+}
+
+async fn baz() -> u32 {
+ 42
+}
+
+async fn also_bad(x: &Mutex<u32>) -> u32 {
+ let first = baz().await;
+
+ let guard = x.lock().unwrap();
+
+ let second = baz().await;
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+async fn not_good(x: &Mutex<u32>) -> u32 {
+ let first = baz().await;
+
+ let second = {
+ let guard = x.lock().unwrap();
+ baz().await
+ };
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+#[allow(clippy::manual_async_fn)]
+fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
+ async move {
+ let guard = x.lock().unwrap();
+ baz().await
+ }
+}
+
+fn main() {
+ let m = Mutex::new(100);
+ good(&m);
+ bad(&m);
+ also_bad(&m);
+ not_good(&m);
+ block_bad(&m);
+}
--- /dev/null
- --> $DIR/await_holding_lock.rs:7:9
+error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
- --> $DIR/await_holding_lock.rs:7:5
++ --> $DIR/await_holding_lock.rs:6:9
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+ = note: `-D clippy::await-holding-lock` implied by `-D warnings`
+note: these are all the await points this lock is held through
- --> $DIR/await_holding_lock.rs:28:9
++ --> $DIR/await_holding_lock.rs:6:5
+ |
+LL | / let guard = x.lock().unwrap();
+LL | | baz().await
+LL | | }
+ | |_^
+
+error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
- --> $DIR/await_holding_lock.rs:28:5
++ --> $DIR/await_holding_lock.rs:27:9
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+note: these are all the await points this lock is held through
- --> $DIR/await_holding_lock.rs:41:13
++ --> $DIR/await_holding_lock.rs:27:5
+ |
+LL | / let guard = x.lock().unwrap();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_^
+
+error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
- --> $DIR/await_holding_lock.rs:41:9
++ --> $DIR/await_holding_lock.rs:40:13
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+note: these are all the await points this lock is held through
- --> $DIR/await_holding_lock.rs:53:13
++ --> $DIR/await_holding_lock.rs:40:9
+ |
+LL | / let guard = x.lock().unwrap();
+LL | | baz().await
+LL | | };
+ | |_____^
+
+error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
- --> $DIR/await_holding_lock.rs:53:9
++ --> $DIR/await_holding_lock.rs:52:13
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+note: these are all the await points this lock is held through
++ --> $DIR/await_holding_lock.rs:52:9
+ |
+LL | / let guard = x.lock().unwrap();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: aborting due to 4 previous errors
+
--- /dev/null
- // edition:2018
+#![warn(clippy::await_holding_refcell_ref)]
+
+use std::cell::RefCell;
+
+async fn bad(x: &RefCell<u32>) -> u32 {
+ let b = x.borrow();
+ baz().await
+}
+
+async fn bad_mut(x: &RefCell<u32>) -> u32 {
+ let b = x.borrow_mut();
+ baz().await
+}
+
+async fn good(x: &RefCell<u32>) -> u32 {
+ {
+ let b = x.borrow_mut();
+ let y = *b + 1;
+ }
+ baz().await;
+ let b = x.borrow_mut();
+ 47
+}
+
+async fn baz() -> u32 {
+ 42
+}
+
+async fn also_bad(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let b = x.borrow_mut();
+
+ let second = baz().await;
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+async fn less_bad(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let b = x.borrow_mut();
+
+ let second = baz().await;
+
+ drop(b);
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+async fn not_good(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let second = {
+ let b = x.borrow_mut();
+ baz().await
+ };
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+#[allow(clippy::manual_async_fn)]
+fn block_bad(x: &RefCell<u32>) -> impl std::future::Future<Output = u32> + '_ {
+ async move {
+ let b = x.borrow_mut();
+ baz().await
+ }
+}
+
+fn main() {
+ let rc = RefCell::new(100);
+ good(&rc);
+ bad(&rc);
+ bad_mut(&rc);
+ also_bad(&rc);
+ less_bad(&rc);
+ not_good(&rc);
+ block_bad(&rc);
+}
--- /dev/null
- --> $DIR/await_holding_refcell_ref.rs:7:9
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
- --> $DIR/await_holding_refcell_ref.rs:7:5
++ --> $DIR/await_holding_refcell_ref.rs:6:9
+ |
+LL | let b = x.borrow();
+ | ^
+ |
+ = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings`
+note: these are all the await points this ref is held through
- --> $DIR/await_holding_refcell_ref.rs:12:9
++ --> $DIR/await_holding_refcell_ref.rs:6:5
+ |
+LL | / let b = x.borrow();
+LL | | baz().await
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
- --> $DIR/await_holding_refcell_ref.rs:12:5
++ --> $DIR/await_holding_refcell_ref.rs:11:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
- --> $DIR/await_holding_refcell_ref.rs:33:9
++ --> $DIR/await_holding_refcell_ref.rs:11:5
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
- --> $DIR/await_holding_refcell_ref.rs:33:5
++ --> $DIR/await_holding_refcell_ref.rs:32:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
- --> $DIR/await_holding_refcell_ref.rs:45:9
++ --> $DIR/await_holding_refcell_ref.rs:32:5
+ |
+LL | / let b = x.borrow_mut();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
- --> $DIR/await_holding_refcell_ref.rs:45:5
++ --> $DIR/await_holding_refcell_ref.rs:44:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
- --> $DIR/await_holding_refcell_ref.rs:60:13
++ --> $DIR/await_holding_refcell_ref.rs:44:5
+ |
+LL | / let b = x.borrow_mut();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
- --> $DIR/await_holding_refcell_ref.rs:60:9
++ --> $DIR/await_holding_refcell_ref.rs:59:13
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
- --> $DIR/await_holding_refcell_ref.rs:72:13
++ --> $DIR/await_holding_refcell_ref.rs:59:9
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | };
+ | |_____^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
- --> $DIR/await_holding_refcell_ref.rs:72:9
++ --> $DIR/await_holding_refcell_ref.rs:71:13
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
++ --> $DIR/await_holding_refcell_ref.rs:71:9
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+#[warn(
+ clippy::cast_precision_loss,
+ clippy::cast_possible_truncation,
+ clippy::cast_sign_loss,
+ clippy::cast_possible_wrap
+)]
+#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+fn main() {
+ // Test clippy::cast_precision_loss
+ let x0 = 1i32;
+ x0 as f32;
+ let x1 = 1i64;
+ x1 as f32;
+ x1 as f64;
+ let x2 = 1u32;
+ x2 as f32;
+ let x3 = 1u64;
+ x3 as f32;
+ x3 as f64;
+ // Test clippy::cast_possible_truncation
+ 1f32 as i32;
+ 1f32 as u32;
+ 1f64 as f32;
+ 1i32 as i8;
+ 1i32 as u8;
+ 1f64 as isize;
+ 1f64 as usize;
+ // Test clippy::cast_possible_wrap
+ 1u8 as i8;
+ 1u16 as i16;
+ 1u32 as i32;
+ 1u64 as i64;
+ 1usize as isize;
+ // Test clippy::cast_sign_loss
+ 1i32 as u32;
+ -1i32 as u32;
+ 1isize as usize;
+ -1isize as usize;
+ 0i8 as u8;
+ i8::MAX as u8;
+ i16::MAX as u16;
+ i32::MAX as u32;
+ i64::MAX as u64;
+ i128::MAX as u128;
+
+ (-1i8).abs() as u8;
+ (-1i16).abs() as u16;
+ (-1i32).abs() as u32;
+ (-1i64).abs() as u64;
+ (-1isize).abs() as usize;
+
+ (-1i8).checked_abs().unwrap() as u8;
+ (-1i16).checked_abs().unwrap() as u16;
+ (-1i32).checked_abs().unwrap() as u32;
+ (-1i64).checked_abs().unwrap() as u64;
+ (-1isize).checked_abs().unwrap() as usize;
+
+ (-1i8).rem_euclid(1i8) as u8;
+ (-1i8).rem_euclid(1i8) as u16;
+ (-1i16).rem_euclid(1i16) as u16;
+ (-1i16).rem_euclid(1i16) as u32;
+ (-1i32).rem_euclid(1i32) as u32;
+ (-1i32).rem_euclid(1i32) as u64;
+ (-1i64).rem_euclid(1i64) as u64;
+ (-1i64).rem_euclid(1i64) as u128;
+ (-1isize).rem_euclid(1isize) as usize;
+ (1i8).rem_euclid(-1i8) as u8;
+ (1i8).rem_euclid(-1i8) as u16;
+ (1i16).rem_euclid(-1i16) as u16;
+ (1i16).rem_euclid(-1i16) as u32;
+ (1i32).rem_euclid(-1i32) as u32;
+ (1i32).rem_euclid(-1i32) as u64;
+ (1i64).rem_euclid(-1i64) as u64;
+ (1i64).rem_euclid(-1i64) as u128;
+ (1isize).rem_euclid(-1isize) as usize;
+
+ (-1i8).checked_rem_euclid(1i8).unwrap() as u8;
+ (-1i8).checked_rem_euclid(1i8).unwrap() as u16;
+ (-1i16).checked_rem_euclid(1i16).unwrap() as u16;
+ (-1i16).checked_rem_euclid(1i16).unwrap() as u32;
+ (-1i32).checked_rem_euclid(1i32).unwrap() as u32;
+ (-1i32).checked_rem_euclid(1i32).unwrap() as u64;
+ (-1i64).checked_rem_euclid(1i64).unwrap() as u64;
+ (-1i64).checked_rem_euclid(1i64).unwrap() as u128;
+ (-1isize).checked_rem_euclid(1isize).unwrap() as usize;
+ (1i8).checked_rem_euclid(-1i8).unwrap() as u8;
+ (1i8).checked_rem_euclid(-1i8).unwrap() as u16;
+ (1i16).checked_rem_euclid(-1i16).unwrap() as u16;
+ (1i16).checked_rem_euclid(-1i16).unwrap() as u32;
+ (1i32).checked_rem_euclid(-1i32).unwrap() as u32;
+ (1i32).checked_rem_euclid(-1i32).unwrap() as u64;
+ (1i64).checked_rem_euclid(-1i64).unwrap() as u64;
+ (1i64).checked_rem_euclid(-1i64).unwrap() as u128;
+ (1isize).checked_rem_euclid(-1isize).unwrap() as usize;
++
++ // no lint for `cast_possible_truncation`
++ // with `signum` method call (see issue #5395)
++ let x: i64 = 5;
++ let _ = x.signum() as i32;
++
++ let s = x.signum();
++ let _ = s as i32;
++
++ // Test for signed min
++ (-99999999999i64).min(1) as i8; // should be linted because signed
++
++ // Test for various operations that remove enough bits for the result to fit
++ (999999u64 & 1) as u8;
++ (999999u64 % 15) as u8;
++ (999999u64 / 0x1_0000_0000_0000) as u16;
++ ({ 999999u64 >> 56 }) as u8;
++ ({
++ let x = 999999u64;
++ x.min(1)
++ }) as u8;
++ 999999u64.clamp(0, 255) as u8;
++ 999999u64.clamp(0, 256) as u8; // should still be linted
+}
--- /dev/null
- error: aborting due to 22 previous errors
+error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:11:5
+ |
+LL | x0 as f32;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
+
+error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:13:5
+ |
+LL | x1 as f32;
+ | ^^^^^^^^^
+
+error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast.rs:14:5
+ |
+LL | x1 as f64;
+ | ^^^^^^^^^
+
+error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:16:5
+ |
+LL | x2 as f32;
+ | ^^^^^^^^^
+
+error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:18:5
+ |
+LL | x3 as f32;
+ | ^^^^^^^^^
+
+error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast.rs:19:5
+ |
+LL | x3 as f64;
+ | ^^^^^^^^^
+
+error: casting `f32` to `i32` may truncate the value
+ --> $DIR/cast.rs:21:5
+ |
+LL | 1f32 as i32;
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+
+error: casting `f32` to `u32` may truncate the value
+ --> $DIR/cast.rs:22:5
+ |
+LL | 1f32 as u32;
+ | ^^^^^^^^^^^
+
+error: casting `f32` to `u32` may lose the sign of the value
+ --> $DIR/cast.rs:22:5
+ |
+LL | 1f32 as u32;
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
+
+error: casting `f64` to `f32` may truncate the value
+ --> $DIR/cast.rs:23:5
+ |
+LL | 1f64 as f32;
+ | ^^^^^^^^^^^
+
+error: casting `i32` to `i8` may truncate the value
+ --> $DIR/cast.rs:24:5
+ |
+LL | 1i32 as i8;
+ | ^^^^^^^^^^
+
+error: casting `i32` to `u8` may truncate the value
+ --> $DIR/cast.rs:25:5
+ |
+LL | 1i32 as u8;
+ | ^^^^^^^^^^
+
+error: casting `f64` to `isize` may truncate the value
+ --> $DIR/cast.rs:26:5
+ |
+LL | 1f64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `f64` to `usize` may truncate the value
+ --> $DIR/cast.rs:27:5
+ |
+LL | 1f64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `f64` to `usize` may lose the sign of the value
+ --> $DIR/cast.rs:27:5
+ |
+LL | 1f64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u8` to `i8` may wrap around the value
+ --> $DIR/cast.rs:29:5
+ |
+LL | 1u8 as i8;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
+
+error: casting `u16` to `i16` may wrap around the value
+ --> $DIR/cast.rs:30:5
+ |
+LL | 1u16 as i16;
+ | ^^^^^^^^^^^
+
+error: casting `u32` to `i32` may wrap around the value
+ --> $DIR/cast.rs:31:5
+ |
+LL | 1u32 as i32;
+ | ^^^^^^^^^^^
+
+error: casting `u64` to `i64` may wrap around the value
+ --> $DIR/cast.rs:32:5
+ |
+LL | 1u64 as i64;
+ | ^^^^^^^^^^^
+
+error: casting `usize` to `isize` may wrap around the value
+ --> $DIR/cast.rs:33:5
+ |
+LL | 1usize as isize;
+ | ^^^^^^^^^^^^^^^
+
+error: casting `i32` to `u32` may lose the sign of the value
+ --> $DIR/cast.rs:36:5
+ |
+LL | -1i32 as u32;
+ | ^^^^^^^^^^^^
+
+error: casting `isize` to `usize` may lose the sign of the value
+ --> $DIR/cast.rs:38:5
+ |
+LL | -1isize as usize;
+ | ^^^^^^^^^^^^^^^^
+
++error: casting `i64` to `i8` may truncate the value
++ --> $DIR/cast.rs:105:5
++ |
++LL | (-99999999999i64).min(1) as i8; // should be linted because signed
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: casting `u64` to `u8` may truncate the value
++ --> $DIR/cast.rs:117:5
++ |
++LL | 999999u64.clamp(0, 256) as u8; // should still be linted
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 24 previous errors
+
--- /dev/null
--- /dev/null
++fn zero() {
++ unsafe { 0 };
++}
--- /dev/null
-
+// https://github.com/rust-lang/rust-clippy/issues/3969
+// used to crash: error: internal compiler error:
+// src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `<i32 as
+// std::iter::Iterator>::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs
+
+// Check that tautalogically false bounds are accepted, and are used
+// in type inference.
+#![feature(trivial_bounds)]
+#![allow(unused)]
- for<'a> Dst<A + 'a>: Sized,
+trait A {}
+
+impl A for i32 {}
+
+struct Dst<X: ?Sized> {
+ x: X,
+}
+
+struct TwoStrs(str, str)
+where
+ str: Sized;
+
+fn unsized_local()
+where
- let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
++ for<'a> Dst<dyn A + 'a>: Sized,
+{
++ let x: Dst<dyn A> = *(Box::new(Dst { x: 1 }) as Box<Dst<dyn A>>);
+}
+
+fn return_str() -> str
+where
+ str: Sized,
+{
+ *"Sized".to_string().into_boxed_str()
+}
+
+fn use_op(s: String) -> String
+where
+ String: ::std::ops::Neg<Output = String>,
+{
+ -s
+}
+
+fn use_for()
+where
+ i32: Iterator,
+{
+ for _ in 2i32 {}
+}
+
+fn main() {}
--- /dev/null
- error: trait objects without an explicit `dyn` are deprecated
- --> $DIR/ice-3969.rs:25:17
++error: trait bound str: std::marker::Sized does not depend on any type or lifetime parameters
++ --> $DIR/ice-3969.rs:20:10
+ |
- LL | for<'a> Dst<A + 'a>: Sized,
- | ^^^^^^ help: use `dyn`: `dyn A + 'a`
++LL | str: Sized;
++ | ^^^^^
+ |
- = note: `-D bare-trait-objects` implied by `-D warnings`
- = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
- = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
++ = note: `-D trivial-bounds` implied by `-D warnings`
+
- error: trait objects without an explicit `dyn` are deprecated
- --> $DIR/ice-3969.rs:27:16
++error: trait bound for<'a> Dst<(dyn A + 'a)>: std::marker::Sized does not depend on any type or lifetime parameters
++ --> $DIR/ice-3969.rs:24:30
+ |
- LL | let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
- | ^ help: use `dyn`: `dyn A`
++LL | for<'a> Dst<dyn A + 'a>: Sized,
++ | ^^^^^
++
++error: trait bound str: std::marker::Sized does not depend on any type or lifetime parameters
++ --> $DIR/ice-3969.rs:31:10
+ |
- = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
- = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
++LL | str: Sized,
++ | ^^^^^
+
- error: trait objects without an explicit `dyn` are deprecated
- --> $DIR/ice-3969.rs:27:57
++error: trait bound std::string::String: std::ops::Neg does not depend on any type or lifetime parameters
++ --> $DIR/ice-3969.rs:38:13
+ |
- LL | let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
- | ^ help: use `dyn`: `dyn A`
++LL | String: ::std::ops::Neg<Output = String>,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: trait bound i32: std::iter::Iterator does not depend on any type or lifetime parameters
++ --> $DIR/ice-3969.rs:45:10
+ |
- = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
- = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
++LL | i32: Iterator,
++ | ^^^^^^^^
+
- error: aborting due to 3 previous errors
++error: aborting due to 5 previous errors
+
--- /dev/null
- // edition:2018
-
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/5207
+
+pub async fn bar<'a, T: 'a>(_: T) {}
+
+fn main() {}
--- /dev/null
-
+// originally from glacier fixed/77919.rs
+// encountered errors resolving bounds after type-checking
+trait TypeVal<T> {
+ const VAL: T;
+}
+struct Five;
+struct Multiply<N, M> {
+ _n: PhantomData,
+}
+impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+
+fn main() {
+ [1; <Multiply<Five, Five>>::VAL];
+}
--- /dev/null
- --> $DIR/ice-6252.rs:9:9
+error[E0412]: cannot find type `PhantomData` in this scope
- help: consider importing this struct
++ --> $DIR/ice-6252.rs:8:9
+ |
+LL | _n: PhantomData,
+ | ^^^^^^^^^^^ not found in this scope
+ |
- --> $DIR/ice-6252.rs:11:63
++help: consider importing one of these items
++ |
++LL | use core::marker::PhantomData;
++ |
++LL | use serde::__private::PhantomData;
+ |
+LL | use std::marker::PhantomData;
+ |
+
+error[E0412]: cannot find type `VAL` in this scope
- --> $DIR/ice-6252.rs:11:1
++ --> $DIR/ice-6252.rs:10:63
+ |
+LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | - ^^^ not found in this scope
+ | |
+ | help: you might be missing a type parameter: `, VAL`
+
+error[E0046]: not all trait items implemented, missing: `VAL`
++ --> $DIR/ice-6252.rs:10:1
+ |
+LL | const VAL: T;
+ | ------------- `VAL` from trait
+...
+LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0046, E0412.
+For more information about an error, try `rustc --explain E0046`.
--- /dev/null
- // edition:2018
+#![allow(clippy::never_loop)]
+
+async fn f() {
+ loop {
+ break;
+ }
+}
+
+fn main() {}
--- /dev/null
--- /dev/null
++#![warn(clippy::undocumented_unsafe_blocks)]
++#![allow(clippy::no_effect)]
++
++#[path = "auxiliary/ice-7868-aux.rs"]
++mod zero;
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: unsafe block missing a safety comment
++ --> $DIR/auxiliary/ice-7868-aux.rs:2:5
++ |
++LL | unsafe { 0 };
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
++help: consider adding a safety comment
++ |
++LL ~ // Safety: ...
++LL ~ unsafe { 0 };
++ |
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++enum Tila {
++ TyöAlkoi,
++ TyöKeskeytyi,
++ TyöValmis,
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: all variants have the same prefix: `Työ`
++ --> $DIR/ice-7869.rs:1:1
++ |
++LL | / enum Tila {
++LL | | TyöAlkoi,
++LL | | TyöKeskeytyi,
++LL | | TyöValmis,
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::enum-variant-names` implied by `-D warnings`
++ = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: aborting due to previous error
++
--- /dev/null
- // edition:2018
-
+use serde::Deserialize;
+
+/// Tests that we do not lint for unused underscores in a `MacroAttribute`
+/// expansion
+#[deny(clippy::used_underscore_binding)]
+#[derive(Deserialize)]
+struct MacroAttributesTest {
+ _foo: u32,
+}
+
+#[test]
+fn macro_attributes_test() {
+ let _ = MacroAttributesTest { _foo: 0 };
+}
+
+fn main() {}
--- /dev/null
- // compile-flags: --edition=2018
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+#![warn(clippy::debug_assert_with_mut_call)]
+#![allow(clippy::redundant_closure_call)]
+
++
+struct S;
+
+impl S {
+ fn bool_self_ref(&self) -> bool { false }
+ fn bool_self_mut(&mut self) -> bool { false }
+ fn bool_self_ref_arg_ref(&self, _: &u32) -> bool { false }
+ fn bool_self_ref_arg_mut(&self, _: &mut u32) -> bool { false }
+ fn bool_self_mut_arg_ref(&mut self, _: &u32) -> bool { false }
+ fn bool_self_mut_arg_mut(&mut self, _: &mut u32) -> bool { false }
+
+ fn u32_self_ref(&self) -> u32 { 0 }
+ fn u32_self_mut(&mut self) -> u32 { 0 }
+ fn u32_self_ref_arg_ref(&self, _: &u32) -> u32 { 0 }
+ fn u32_self_ref_arg_mut(&self, _: &mut u32) -> u32 { 0 }
+ fn u32_self_mut_arg_ref(&mut self, _: &u32) -> u32 { 0 }
+ fn u32_self_mut_arg_mut(&mut self, _: &mut u32) -> u32 { 0 }
+}
+
+fn bool_ref(_: &u32) -> bool { false }
+fn bool_mut(_: &mut u32) -> bool { false }
+fn u32_ref(_: &u32) -> u32 { 0 }
+fn u32_mut(_: &mut u32) -> u32 { 0 }
+
+fn func_non_mutable() {
+ debug_assert!(bool_ref(&3));
+ debug_assert!(!bool_ref(&3));
+
+ debug_assert_eq!(0, u32_ref(&3));
+ debug_assert_eq!(u32_ref(&3), 0);
+
+ debug_assert_ne!(1, u32_ref(&3));
+ debug_assert_ne!(u32_ref(&3), 1);
+}
+
+fn func_mutable() {
+ debug_assert!(bool_mut(&mut 3));
+ debug_assert!(!bool_mut(&mut 3));
+
+ debug_assert_eq!(0, u32_mut(&mut 3));
+ debug_assert_eq!(u32_mut(&mut 3), 0);
+
+ debug_assert_ne!(1, u32_mut(&mut 3));
+ debug_assert_ne!(u32_mut(&mut 3), 1);
+}
+
+fn method_non_mutable() {
+ debug_assert!(S.bool_self_ref());
+ debug_assert!(S.bool_self_ref_arg_ref(&3));
+
+ debug_assert_eq!(S.u32_self_ref(), 0);
+ debug_assert_eq!(S.u32_self_ref_arg_ref(&3), 0);
+
+ debug_assert_ne!(S.u32_self_ref(), 1);
+ debug_assert_ne!(S.u32_self_ref_arg_ref(&3), 1);
+}
+
+fn method_mutable() {
+ debug_assert!(S.bool_self_mut());
+ debug_assert!(!S.bool_self_mut());
+ debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
+ debug_assert!(S.bool_self_mut_arg_ref(&3));
+ debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
+
+ debug_assert_eq!(S.u32_self_mut(), 0);
+ debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
+ debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
+ debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
+
+ debug_assert_ne!(S.u32_self_mut(), 1);
+ debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
+ debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
+ debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
+}
+
+fn misc() {
+ // with variable
+ let mut v: Vec<u32> = vec![1, 2, 3, 4];
+ debug_assert_eq!(v.get(0), Some(&1));
+ debug_assert_ne!(v[0], 2);
+ debug_assert_eq!(v.pop(), Some(1));
+ debug_assert_ne!(Some(3), v.pop());
+
+ let a = &mut 3;
+ debug_assert!(bool_mut(a));
+
+ // nested
+ debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
+
+ // chained
+ debug_assert_eq!(v.pop().unwrap(), 3);
+
+ // format args
+ debug_assert!(bool_ref(&3), "w/o format");
+ debug_assert!(bool_mut(&mut 3), "w/o format");
+ debug_assert!(bool_ref(&3), "{} format", "w/");
+ debug_assert!(bool_mut(&mut 3), "{} format", "w/");
+
+ // sub block
+ let mut x = 42_u32;
+ debug_assert!({
+ bool_mut(&mut x);
+ x > 10
+ });
+
+ // closures
+ debug_assert!((|| {
+ let mut x = 42;
+ bool_mut(&mut x);
+ x > 10
+ })());
+}
+
+async fn debug_await() {
+ debug_assert!(async {
+ true
+ }.await);
+}
+
+fn main() {
+ func_non_mutable();
+ func_mutable();
+ method_non_mutable();
+ method_mutable();
+
+ misc();
+ debug_await();
+}
--- /dev/null
- #[warn(clippy::unstable_as_slice)]
- #[warn(clippy::unstable_as_mut_slice)]
- #[warn(clippy::misaligned_transmute)]
- #[warn(clippy::unused_collect)]
- #[warn(clippy::invalid_ref)]
- #[warn(clippy::into_iter_on_array)]
- #[warn(clippy::unused_label)]
- #[warn(clippy::regex_macro)]
- #[warn(clippy::drop_bounds)]
- #[warn(clippy::temporary_cstring_as_ptr)]
- #[warn(clippy::panic_params)]
- #[warn(clippy::unknown_clippy_lints)]
- #[warn(clippy::find_map)]
- #[warn(clippy::filter_map)]
- #[warn(clippy::pub_enum_variant_names)]
- #[warn(clippy::wrong_pub_self_convention)]
- #[warn(clippy::invalid_atomic_ordering)]
++#![warn(clippy::should_assert_eq)]
++#![warn(clippy::extend_from_slice)]
++#![warn(clippy::range_step_by_zero)]
++#![warn(clippy::unstable_as_slice)]
++#![warn(clippy::unstable_as_mut_slice)]
++#![warn(clippy::misaligned_transmute)]
++#![warn(clippy::assign_ops)]
++#![warn(clippy::if_let_redundant_pattern_matching)]
++#![warn(clippy::unsafe_vector_initialization)]
++#![warn(clippy::unused_collect)]
++#![warn(clippy::replace_consts)]
++#![warn(clippy::regex_macro)]
++#![warn(clippy::find_map)]
++#![warn(clippy::filter_map)]
++#![warn(clippy::pub_enum_variant_names)]
++#![warn(clippy::wrong_pub_self_convention)]
+
+fn main() {}
--- /dev/null
- error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
- --> $DIR/deprecated.rs:1:8
++error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011
++ --> $DIR/deprecated.rs:1:9
+ |
- LL | #[warn(clippy::unstable_as_slice)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::should_assert_eq)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
+
- error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
- --> $DIR/deprecated.rs:2:8
++error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice
++ --> $DIR/deprecated.rs:2:9
+ |
- LL | #[warn(clippy::unstable_as_mut_slice)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::extend_from_slice)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
- --> $DIR/deprecated.rs:3:8
++error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays
++ --> $DIR/deprecated.rs:3:9
+ |
- LL | #[warn(clippy::misaligned_transmute)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::range_step_by_zero)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
- --> $DIR/deprecated.rs:4:8
++error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
++ --> $DIR/deprecated.rs:4:9
+ |
- LL | #[warn(clippy::unused_collect)]
- | ^^^^^^^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::unstable_as_slice)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
- --> $DIR/deprecated.rs:5:8
++error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
++ --> $DIR/deprecated.rs:5:9
+ |
- LL | #[warn(clippy::invalid_ref)]
- | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
++LL | #![warn(clippy::unstable_as_mut_slice)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
- --> $DIR/deprecated.rs:6:8
++error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
++ --> $DIR/deprecated.rs:6:9
+ |
- LL | #[warn(clippy::into_iter_on_array)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
++LL | #![warn(clippy::misaligned_transmute)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::unused_label` has been renamed to `unused_labels`
- --> $DIR/deprecated.rs:7:8
++error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless
++ --> $DIR/deprecated.rs:7:9
+ |
- LL | #[warn(clippy::unused_label)]
- | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
++LL | #![warn(clippy::assign_ops)]
++ | ^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
- --> $DIR/deprecated.rs:8:8
++error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching
++ --> $DIR/deprecated.rs:8:9
+ |
- LL | #[warn(clippy::regex_macro)]
- | ^^^^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::if_let_redundant_pattern_matching)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
- --> $DIR/deprecated.rs:9:8
++error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior
++ --> $DIR/deprecated.rs:9:9
+ |
- LL | #[warn(clippy::drop_bounds)]
- | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
++LL | #![warn(clippy::unsafe_vector_initialization)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
- --> $DIR/deprecated.rs:10:8
++error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
++ --> $DIR/deprecated.rs:10:9
+ |
- LL | #[warn(clippy::temporary_cstring_as_ptr)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
++LL | #![warn(clippy::unused_collect)]
++ | ^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
- --> $DIR/deprecated.rs:11:8
++error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants
++ --> $DIR/deprecated.rs:11:9
+ |
- LL | #[warn(clippy::panic_params)]
- | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
++LL | #![warn(clippy::replace_consts)]
++ | ^^^^^^^^^^^^^^^^^^^^^^
+
- error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
- --> $DIR/deprecated.rs:12:8
++error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
++ --> $DIR/deprecated.rs:12:9
+ |
- LL | #[warn(clippy::unknown_clippy_lints)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
++LL | #![warn(clippy::regex_macro)]
++ | ^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
- --> $DIR/deprecated.rs:13:8
++ --> $DIR/deprecated.rs:13:9
+ |
- LL | #[warn(clippy::find_map)]
- | ^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::find_map)]
++ | ^^^^^^^^^^^^^^^^
+
+error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
- --> $DIR/deprecated.rs:14:8
++ --> $DIR/deprecated.rs:14:9
+ |
- LL | #[warn(clippy::filter_map)]
- | ^^^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::filter_map)]
++ | ^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
- --> $DIR/deprecated.rs:15:8
++ --> $DIR/deprecated.rs:15:9
+ |
- LL | #[warn(clippy::pub_enum_variant_names)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | #![warn(clippy::pub_enum_variant_names)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
- --> $DIR/deprecated.rs:16:8
- |
- LL | #[warn(clippy::wrong_pub_self_convention)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
- error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
- --> $DIR/deprecated.rs:17:8
++ --> $DIR/deprecated.rs:16:9
+ |
- LL | #[warn(clippy::invalid_atomic_ordering)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
++LL | #![warn(clippy::wrong_pub_self_convention)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
- error: aborting due to 17 previous errors
++error: aborting due to 16 previous errors
+
--- /dev/null
-
+#![warn(clippy::diverging_sub_expression)]
+#![allow(clippy::match_same_arms, clippy::logic_bug)]
+#[allow(clippy::empty_loop)]
+fn diverge() -> ! {
+ loop {}
+}
+
+struct A;
+
+impl A {
+ fn foo(&self) -> ! {
+ diverge()
+ }
+}
+
+#[allow(unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
+fn main() {
+ let b = true;
+ b || diverge();
+ b || A.foo();
+}
+
+#[allow(dead_code, unused_variables)]
+fn foobar() {
+ loop {
+ let x = match 5 {
+ 4 => return,
+ 5 => continue,
+ 6 => true || return,
+ 7 => true || continue,
+ 8 => break,
+ 9 => diverge(),
+ 3 => true || diverge(),
+ 10 => match 42 {
+ 99 => return,
+ _ => true || panic!("boo"),
+ },
+ _ => true || break,
+ };
+ }
+}
--- /dev/null
- --> $DIR/diverging_sub_expression.rs:20:10
+error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:21:10
++ --> $DIR/diverging_sub_expression.rs:19:10
+ |
+LL | b || diverge();
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
+
+error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:30:26
++ --> $DIR/diverging_sub_expression.rs:20:10
+ |
+LL | b || A.foo();
+ | ^^^^^^^
+
+error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:31:26
++ --> $DIR/diverging_sub_expression.rs:29:26
+ |
+LL | 6 => true || return,
+ | ^^^^^^
+
+error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:34:26
++ --> $DIR/diverging_sub_expression.rs:30:26
+ |
+LL | 7 => true || continue,
+ | ^^^^^^^^
+
+error: sub-expression diverges
- --> $DIR/diverging_sub_expression.rs:39:26
++ --> $DIR/diverging_sub_expression.rs:33:26
+ |
+LL | 3 => true || diverge(),
+ | ^^^^^^^^^
+
+error: sub-expression diverges
- error: aborting due to 6 previous errors
++ --> $DIR/diverging_sub_expression.rs:36:30
++ |
++LL | _ => true || panic!("boo"),
++ | ^^^^^^^^^^^^^
++ |
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: sub-expression diverges
++ --> $DIR/diverging_sub_expression.rs:38:26
+ |
+LL | _ => true || break,
+ | ^^^^^
+
++error: aborting due to 7 previous errors
+
--- /dev/null
--- /dev/null
++// run-rustfix
++//! This file tests for the `DOC_MARKDOWN` lint.
++
++#![allow(dead_code, incomplete_features)]
++#![warn(clippy::doc_markdown)]
++#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
++#![rustfmt::skip]
++
++/// The `foo_bar` function does _nothing_. See also `foo::bar`. (note the dot there)
++/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not `Foo::some_fun`
++/// which should be reported only once despite being __doubly bad__.
++/// Here be `::a::global:path`, and _`::another::global::path`_. :: is not a path though.
++/// Import an item from `::awesome::global::blob::` (Intended postfix)
++/// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
++/// That's not code ~`NotInCodeBlock`~.
++/// `be_sure_we_got_to_the_end_of_it`
++fn foo_bar() {
++}
++
++/// That one tests multiline ticks.
++/// ```rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ```
++///
++/// ~~~rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ~~~
++/// `be_sure_we_got_to_the_end_of_it`
++fn multiline_codeblock() {
++}
++
++/// This _is a test for
++/// multiline
++/// emphasis_.
++/// `be_sure_we_got_to_the_end_of_it`
++fn test_emphasis() {
++}
++
++/// This tests units. See also #835.
++/// kiB MiB GiB TiB PiB EiB
++/// kib Mib Gib Tib Pib Eib
++/// kB MB GB TB PB EB
++/// kb Mb Gb Tb Pb Eb
++/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
++/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
++/// 32kB 32MB 32GB 32TB 32PB 32EB
++/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
++/// NaN
++/// `be_sure_we_got_to_the_end_of_it`
++fn test_units() {
++}
++
++/// This tests allowed identifiers.
++/// KiB MiB GiB TiB PiB EiB
++/// DirectX
++/// ECMAScript
++/// GPLv2 GPLv3
++/// GitHub GitLab
++/// IPv4 IPv6
++/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
++/// NaN NaNs
++/// OAuth GraphQL
++/// OCaml
++/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
++/// WebGL
++/// TensorFlow
++/// TrueType
++/// iOS macOS FreeBSD
++/// TeX LaTeX BibTeX BibLaTeX
++/// MinGW
++/// CamelCase (see also #2395)
++/// `be_sure_we_got_to_the_end_of_it`
++fn test_allowed() {
++}
++
++/// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
++/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
++/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
++/// It can also be [`inline_link2`].
++///
++/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
++/// [inline_link]: https://foobar
++/// [inline_link2]: https://foobar
++/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
++/// `multiline_ticks` functions.
++///
++/// expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
++/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
++/// `be_sure_we_got_to_the_end_of_it`
++fn main() {
++ foo_bar();
++ multiline_codeblock();
++ test_emphasis();
++ test_units();
++}
++
++/// ## `CamelCaseThing`
++/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
++///
++/// # `CamelCaseThing`
++///
++/// Not a title #897 `CamelCaseThing`
++/// `be_sure_we_got_to_the_end_of_it`
++fn issue897() {
++}
++
++/// I am confused by brackets? (`x_y`)
++/// I am confused by brackets? (foo `x_y`)
++/// I am confused by brackets? (`x_y` foo)
++/// `be_sure_we_got_to_the_end_of_it`
++fn issue900() {
++}
++
++/// Diesel queries also have a similar problem to [Iterator][iterator], where
++/// /// More talking
++/// returning them from a function requires exposing the implementation of that
++/// function. The [`helper_types`][helper_types] module exists to help with this,
++/// but you might want to hide the return type or have it conditionally change.
++/// Boxing can achieve both.
++///
++/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
++/// [helper_types]: ../helper_types/index.html
++/// `be_sure_we_got_to_the_end_of_it`
++fn issue883() {
++}
++
++/// `foo_bar
++/// baz_quz`
++/// [foo
++/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html)
++fn multiline() {
++}
++
++/** E.g., serialization of an empty list: `FooBar`
++```
++That's in a code block: `PackedNode`
++```
++
++And `BarQuz` too.
++`be_sure_we_got_to_the_end_of_it`
++*/
++fn issue1073() {
++}
++
++/** E.g., serialization of an empty list: `FooBar`
++```
++That's in a code block: PackedNode
++```
++
++And `BarQuz` too.
++`be_sure_we_got_to_the_end_of_it`
++*/
++fn issue1073_alt() {
++}
++
++/// Tests more than three quotes:
++/// ````
++/// DoNotWarn
++/// ```
++/// StillDont
++/// ````
++/// `be_sure_we_got_to_the_end_of_it`
++fn four_quotes() {
++}
++
++#[cfg_attr(feature = "a", doc = " ```")]
++#[cfg_attr(not(feature = "a"), doc = " ```ignore")]
++/// fn main() {
++/// let s = "localhost:10000".to_string();
++/// println!("{}", s);
++/// }
++/// ```
++fn issue_1469() {}
++
++/**
++ * This is a doc comment that should not be a list
++ *This would also be an error under a strict common mark interpretation
++ */
++fn issue_1920() {}
++
++/// An iterator over `mycrate::Collection`'s values.
++/// It should not lint a `'static` lifetime in ticks.
++fn issue_2210() {}
++
++/// This should not cause the lint to trigger:
++/// #REQ-data-family.lint_partof_exists
++fn issue_2343() {}
++
++/// This should not cause an ICE:
++/// __|_ _|__||_|
++fn pulldown_cmark_crash() {}
++
++/// This should not lint
++/// (regression test for #7758)
++/// [plain text][path::to::item]
++fn intra_doc_link() {}
++
++// issue #7033 - generic_const_exprs ICE
++struct S<T, const N: usize>
++where [(); N.checked_next_power_of_two().unwrap()]: {
++ arr: [T; N.checked_next_power_of_two().unwrap()],
++ n: usize,
++}
++
++impl<T: Copy + Default, const N: usize> S<T, N>
++where [(); N.checked_next_power_of_two().unwrap()]: {
++ fn new() -> Self {
++ Self {
++ arr: [T::default(); N.checked_next_power_of_two().unwrap()],
++ n: 0,
++ }
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++//! This file tests for the `DOC_MARKDOWN` lint.
++
++#![allow(dead_code, incomplete_features)]
++#![warn(clippy::doc_markdown)]
++#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
++#![rustfmt::skip]
++
++/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun
++/// which should be reported only once despite being __doubly bad__.
++/// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
++/// Import an item from ::awesome::global::blob:: (Intended postfix)
++/// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
++/// That's not code ~NotInCodeBlock~.
++/// be_sure_we_got_to_the_end_of_it
++fn foo_bar() {
++}
++
++/// That one tests multiline ticks.
++/// ```rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ```
++///
++/// ~~~rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ~~~
++/// be_sure_we_got_to_the_end_of_it
++fn multiline_codeblock() {
++}
++
++/// This _is a test for
++/// multiline
++/// emphasis_.
++/// be_sure_we_got_to_the_end_of_it
++fn test_emphasis() {
++}
++
++/// This tests units. See also #835.
++/// kiB MiB GiB TiB PiB EiB
++/// kib Mib Gib Tib Pib Eib
++/// kB MB GB TB PB EB
++/// kb Mb Gb Tb Pb Eb
++/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
++/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
++/// 32kB 32MB 32GB 32TB 32PB 32EB
++/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
++/// NaN
++/// be_sure_we_got_to_the_end_of_it
++fn test_units() {
++}
++
++/// This tests allowed identifiers.
++/// KiB MiB GiB TiB PiB EiB
++/// DirectX
++/// ECMAScript
++/// GPLv2 GPLv3
++/// GitHub GitLab
++/// IPv4 IPv6
++/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
++/// NaN NaNs
++/// OAuth GraphQL
++/// OCaml
++/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
++/// WebGL
++/// TensorFlow
++/// TrueType
++/// iOS macOS FreeBSD
++/// TeX LaTeX BibTeX BibLaTeX
++/// MinGW
++/// CamelCase (see also #2395)
++/// be_sure_we_got_to_the_end_of_it
++fn test_allowed() {
++}
++
++/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
++/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
++/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
++/// It can also be [inline_link2].
++///
++/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
++/// [inline_link]: https://foobar
++/// [inline_link2]: https://foobar
++/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
++/// `multiline_ticks` functions.
++///
++/// expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
++/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
++/// be_sure_we_got_to_the_end_of_it
++fn main() {
++ foo_bar();
++ multiline_codeblock();
++ test_emphasis();
++ test_units();
++}
++
++/// ## CamelCaseThing
++/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
++///
++/// # CamelCaseThing
++///
++/// Not a title #897 CamelCaseThing
++/// be_sure_we_got_to_the_end_of_it
++fn issue897() {
++}
++
++/// I am confused by brackets? (`x_y`)
++/// I am confused by brackets? (foo `x_y`)
++/// I am confused by brackets? (`x_y` foo)
++/// be_sure_we_got_to_the_end_of_it
++fn issue900() {
++}
++
++/// Diesel queries also have a similar problem to [Iterator][iterator], where
++/// /// More talking
++/// returning them from a function requires exposing the implementation of that
++/// function. The [`helper_types`][helper_types] module exists to help with this,
++/// but you might want to hide the return type or have it conditionally change.
++/// Boxing can achieve both.
++///
++/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
++/// [helper_types]: ../helper_types/index.html
++/// be_sure_we_got_to_the_end_of_it
++fn issue883() {
++}
++
++/// `foo_bar
++/// baz_quz`
++/// [foo
++/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html)
++fn multiline() {
++}
++
++/** E.g., serialization of an empty list: FooBar
++```
++That's in a code block: `PackedNode`
++```
++
++And BarQuz too.
++be_sure_we_got_to_the_end_of_it
++*/
++fn issue1073() {
++}
++
++/** E.g., serialization of an empty list: FooBar
++```
++That's in a code block: PackedNode
++```
++
++And BarQuz too.
++be_sure_we_got_to_the_end_of_it
++*/
++fn issue1073_alt() {
++}
++
++/// Tests more than three quotes:
++/// ````
++/// DoNotWarn
++/// ```
++/// StillDont
++/// ````
++/// be_sure_we_got_to_the_end_of_it
++fn four_quotes() {
++}
++
++#[cfg_attr(feature = "a", doc = " ```")]
++#[cfg_attr(not(feature = "a"), doc = " ```ignore")]
++/// fn main() {
++/// let s = "localhost:10000".to_string();
++/// println!("{}", s);
++/// }
++/// ```
++fn issue_1469() {}
++
++/**
++ * This is a doc comment that should not be a list
++ *This would also be an error under a strict common mark interpretation
++ */
++fn issue_1920() {}
++
++/// An iterator over mycrate::Collection's values.
++/// It should not lint a `'static` lifetime in ticks.
++fn issue_2210() {}
++
++/// This should not cause the lint to trigger:
++/// #REQ-data-family.lint_partof_exists
++fn issue_2343() {}
++
++/// This should not cause an ICE:
++/// __|_ _|__||_|
++fn pulldown_cmark_crash() {}
++
++/// This should not lint
++/// (regression test for #7758)
++/// [plain text][path::to::item]
++fn intra_doc_link() {}
++
++// issue #7033 - generic_const_exprs ICE
++struct S<T, const N: usize>
++where [(); N.checked_next_power_of_two().unwrap()]: {
++ arr: [T; N.checked_next_power_of_two().unwrap()],
++ n: usize,
++}
++
++impl<T: Copy + Default, const N: usize> S<T, N>
++where [(); N.checked_next_power_of_two().unwrap()]: {
++ fn new() -> Self {
++ Self {
++ arr: [T::default(); N.checked_next_power_of_two().unwrap()],
++ n: 0,
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:9:9
++ |
++LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++ | ^^^^^^^ help: try: ``foo_bar``
++ |
++ = note: `-D clippy::doc-markdown` implied by `-D warnings`
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:9:51
++ |
++LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++ | ^^^^^^^^ help: try: ``foo::bar``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:10:83
++ |
++LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun
++ | ^^^^^^^^^^^^^ help: try: ``Foo::some_fun``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:12:13
++ |
++LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
++ | ^^^^^^^^^^^^^^^^ help: try: ``::a::global:path``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:12:36
++ |
++LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::another::global::path``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:13:25
++ |
++LL | /// Import an item from ::awesome::global::blob:: (Intended postfix)
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::awesome::global::blob::``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:14:31
++ |
++LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
++ | ^^^^^ help: try: ``::Cat``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:15:22
++ |
++LL | /// That's not code ~NotInCodeBlock~.
++ | ^^^^^^^^^^^^^^ help: try: ``NotInCodeBlock``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:16:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:30:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:37:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:51:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:74:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:78:22
++ |
++LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try: ``link_with_underscores``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:81:21
++ |
++LL | /// It can also be [inline_link2].
++ | ^^^^^^^^^^^^ help: try: ``inline_link2``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:91:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:99:8
++ |
++LL | /// ## CamelCaseThing
++ | ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:102:7
++ |
++LL | /// # CamelCaseThing
++ | ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:104:22
++ |
++LL | /// Not a title #897 CamelCaseThing
++ | ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:105:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:112:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:125:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:136:43
++ |
++LL | /** E.g., serialization of an empty list: FooBar
++ | ^^^^^^ help: try: ``FooBar``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:141:5
++ |
++LL | And BarQuz too.
++ | ^^^^^^ help: try: ``BarQuz``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:142:1
++ |
++LL | be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:147:43
++ |
++LL | /** E.g., serialization of an empty list: FooBar
++ | ^^^^^^ help: try: ``FooBar``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:152:5
++ |
++LL | And BarQuz too.
++ | ^^^^^^ help: try: ``BarQuz``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:153:1
++ |
++LL | be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:164:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
++
++error: item in documentation is missing backticks
++ --> $DIR/doc-fixable.rs:183:22
++ |
++LL | /// An iterator over mycrate::Collection's values.
++ | ^^^^^^^^^^^^^^^^^^^ help: try: ``mycrate::Collection``
++
++error: aborting due to 30 previous errors
++
--- /dev/null
--- /dev/null
++/// Ok: <http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels>
++///
++/// Not ok: http://www.unicode.org
++/// Not ok: https://www.unicode.org
++/// Not ok: http://www.unicode.org/
++/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
++fn issue_1832() {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++/// See [NIST SP 800-56A, revision 2].
++///
++/// [NIST SP 800-56A, revision 2]:
++/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419
++fn issue_902_comment() {}
++
++fn main() {}
--- /dev/null
- error: you should put `should_be` between ticks in the documentation
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:7:1
+ |
+LL | / /// This is a doc comment with `unbalanced_tick marks and several words that
+LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`.
+LL | | /// Because of the initial `unbalanced_tick` pair, the error message is
+LL | | /// very `confusing_and_misleading`.
+ | |____________________________________^
+ |
+ = note: `-D clippy::doc-markdown` implied by `-D warnings`
+ = help: a backtick may be missing a pair
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:13:1
+ |
+LL | /// This paragraph has `unbalanced_tick marks and should stop_linting.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
- | ^^^^^^^^^
++error: item in documentation is missing backticks
+ --> $DIR/unbalanced_ticks.rs:15:32
+ |
+LL | /// This paragraph is fine and should_be linted normally.
- error: you should put `not_fine` between ticks in the documentation
++ | ^^^^^^^^^ help: try: ``should_be``
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:17:1
+ |
+LL | /// Double unbalanced backtick from ``here to here` should lint.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
- | ^^^^^^^^
++error: item in documentation is missing backticks
+ --> $DIR/unbalanced_ticks.rs:30:8
+ |
+LL | /// ## not_fine
- error: you should put `backticks_here` between ticks in the documentation
++ | ^^^^^^^^ help: try: ``not_fine``
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:32:1
+ |
+LL | /// ### `unbalanced
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:34:1
+ |
+LL | /// - This `item has unbalanced tick marks
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
- | ^^^^^^^^^^^^^^
++error: item in documentation is missing backticks
+ --> $DIR/unbalanced_ticks.rs:35:23
+ |
+LL | /// - This item needs backticks_here
++ | ^^^^^^^^^^^^^^ help: try: ``backticks_here``
+
+error: aborting due to 8 previous errors
+
--- /dev/null
- // edition:2018
+#![warn(clippy::missing_errors_doc)]
+#![allow(clippy::result_unit_err)]
+#![allow(clippy::unnecessary_wraps)]
+
+use std::io;
+
+pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// This is not sufficiently documented.
+pub fn pub_fn_returning_io_result() -> io::Result<()> {
+ unimplemented!();
+}
+
+/// This is not sufficiently documented.
+pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
+ unimplemented!();
+}
+
+/// # Errors
+/// A description of the errors goes here.
+pub fn pub_fn_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// # Errors
+/// A description of the errors goes here.
+pub async fn async_pub_fn_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// This function doesn't require the documentation because it is private
+fn priv_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// This function doesn't require the documentation because it is private
+async fn async_priv_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+pub struct Struct1;
+
+impl Struct1 {
+ /// This is not sufficiently documented.
+ pub fn pub_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// This is not sufficiently documented.
+ pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// # Errors
+ /// A description of the errors goes here.
+ pub fn pub_method_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// # Errors
+ /// A description of the errors goes here.
+ pub async fn async_pub_method_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// This function doesn't require the documentation because it is private.
+ fn priv_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// This function doesn't require the documentation because it is private.
+ async fn async_priv_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+}
+
+pub trait Trait1 {
+ /// This is not sufficiently documented.
+ fn trait_method_missing_errors_header() -> Result<(), ()>;
+
+ /// # Errors
+ /// A description of the errors goes here.
+ fn trait_method_with_errors_header() -> Result<(), ()>;
+}
+
+impl Trait1 for Struct1 {
+ fn trait_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ fn trait_method_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+}
+
+fn main() -> Result<(), ()> {
+ Ok(())
+}
--- /dev/null
- --> $DIR/doc_errors.rs:8:1
+error: docs for function returning `Result` missing `# Errors` section
- --> $DIR/doc_errors.rs:12:1
++ --> $DIR/doc_errors.rs:7:1
+ |
+LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::missing-errors-doc` implied by `-D warnings`
+
+error: docs for function returning `Result` missing `# Errors` section
- --> $DIR/doc_errors.rs:17:1
++ --> $DIR/doc_errors.rs:11:1
+ |
+LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+
+error: docs for function returning `Result` missing `# Errors` section
- --> $DIR/doc_errors.rs:22:1
++ --> $DIR/doc_errors.rs:16:1
+ |
+LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+
+error: docs for function returning `Result` missing `# Errors` section
- --> $DIR/doc_errors.rs:52:5
++ --> $DIR/doc_errors.rs:21:1
+ |
+LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+
+error: docs for function returning `Result` missing `# Errors` section
- --> $DIR/doc_errors.rs:57:5
++ --> $DIR/doc_errors.rs:51:5
+ |
+LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: docs for function returning `Result` missing `# Errors` section
- --> $DIR/doc_errors.rs:86:5
++ --> $DIR/doc_errors.rs:56:5
+ |
+LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:85:5
+ |
+LL | fn trait_method_missing_errors_header() -> Result<(), ()>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
--- /dev/null
+// aux-build:doc_unsafe_macros.rs
+
+#[macro_use]
+extern crate doc_unsafe_macros;
+
+/// This is not sufficiently documented
+pub unsafe fn destroy_the_planet() {
+ unimplemented!();
+}
+
+/// This one is
+///
+/// # Safety
+///
+/// This function shouldn't be called unless the horsemen are ready
+pub unsafe fn apocalypse(universe: &mut ()) {
+ unimplemented!();
+}
+
+/// This is a private function, so docs aren't necessary
+unsafe fn you_dont_see_me() {
+ unimplemented!();
+}
+
+mod private_mod {
+ pub unsafe fn only_crate_wide_accessible() {
+ unimplemented!();
+ }
+
+ pub unsafe fn republished() {
+ unimplemented!();
+ }
+}
+
+pub use private_mod::republished;
+
+pub trait SafeTraitUnsafeMethods {
+ unsafe fn woefully_underdocumented(self);
+
+ /// # Safety
+ unsafe fn at_least_somewhat_documented(self);
+}
+
+pub unsafe trait UnsafeTrait {
+ fn method();
+}
+
+/// # Safety
+pub unsafe trait DocumentedUnsafeTrait {
+ fn method2();
+}
+
+pub struct Struct;
+
+impl SafeTraitUnsafeMethods for Struct {
+ unsafe fn woefully_underdocumented(self) {
+ // all is well
+ }
+
+ unsafe fn at_least_somewhat_documented(self) {
+ // all is still well
+ }
+}
+
+unsafe impl UnsafeTrait for Struct {
+ fn method() {}
+}
+
+unsafe impl DocumentedUnsafeTrait for Struct {
+ fn method2() {}
+}
+
+impl Struct {
+ pub unsafe fn more_undocumented_unsafe() -> Self {
+ unimplemented!();
+ }
+
+ /// # Safety
+ pub unsafe fn somewhat_documented(&self) {
+ unimplemented!();
+ }
+
+ unsafe fn private(&self) {
+ unimplemented!();
+ }
+}
+
+macro_rules! very_unsafe {
+ () => {
+ pub unsafe fn whee() {
+ unimplemented!()
+ }
+
+ /// # Safety
+ ///
+ /// Please keep the seat belt fastened
+ pub unsafe fn drive() {
+ whee()
+ }
+ };
+}
+
+very_unsafe!();
+
+// we don't lint code from external macros
+undocd_unsafe!();
+
+fn main() {
+ unsafe {
+ you_dont_see_me();
+ destroy_the_planet();
+ let mut universe = ();
+ apocalypse(&mut universe);
+ private_mod::only_crate_wide_accessible();
+ drive();
+ }
+}
+
+// do not lint if any parent has `#[doc(hidden)]` attribute
+// see #7347
+#[doc(hidden)]
+pub mod __macro {
+ pub struct T;
+ impl T {
+ pub unsafe fn f() {}
+ }
+}
++
++/// # Implementation safety
++pub unsafe trait DocumentedUnsafeTraitWithImplementationHeader {
++ fn method();
++}
--- /dev/null
- error: all variants have the same prefix: `With`
+error: variant name ends with the enum's name
+ --> $DIR/enum_variants.rs:15:5
+ |
+LL | cFoo,
+ | ^^^^
+ |
+ = note: `-D clippy::enum-variant-names` implied by `-D warnings`
+
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:26:5
+ |
+LL | FoodGood,
+ | ^^^^^^^^
+
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:27:5
+ |
+LL | FoodMiddle,
+ | ^^^^^^^^^^
+
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:28:5
+ |
+LL | FoodBad,
+ | ^^^^^^^
+
+error: all variants have the same prefix: `Food`
+ --> $DIR/enum_variants.rs:25:1
+ |
+LL | / enum Food {
+LL | | FoodGood,
+LL | | FoodMiddle,
+LL | | FoodBad,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `CallType`
+ --> $DIR/enum_variants.rs:35:1
+ |
+LL | / enum BadCallType {
+LL | | CallTypeCall,
+LL | | CallTypeCreate,
+LL | | CallTypeDestroy,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `Constant`
+ --> $DIR/enum_variants.rs:47:1
+ |
+LL | / enum Consts {
+LL | | ConstantInt,
+LL | | ConstantCake,
+LL | | ConstantLie,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
++error: all variants have the same prefix: `WithOut`
+ --> $DIR/enum_variants.rs:81:1
+ |
+LL | / enum Seallll {
+LL | | WithOutCake,
+LL | | WithOutTea,
+LL | | WithOut,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `Prefix`
+ --> $DIR/enum_variants.rs:87:1
+ |
+LL | / enum NonCaps {
+LL | | Prefix的,
+LL | | PrefixTea,
+LL | | PrefixCake,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same postfix: `IData`
+ --> $DIR/enum_variants.rs:136:1
+ |
+LL | / enum IDataRequest {
+LL | | PutIData(String),
+LL | | GetIData(String),
+LL | | DeleteUnpubIData(String),
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same postfix: `HIData`
+ --> $DIR/enum_variants.rs:142:1
+ |
+LL | / enum HIDataRequest {
+LL | | PutHIData(String),
+LL | | GetHIData(String),
+LL | | DeleteUnpubHIData(String),
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: aborting due to 11 previous errors
+
--- /dev/null
- // 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:16:9
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:14:9
++ --> $DIR/eval_order_dependence.rs:14: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:19:5
++ --> $DIR/eval_order_dependence.rs:12:9
+ |
+LL | x = 1;
+ | ^^^^^
+
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:20:9
++ --> $DIR/eval_order_dependence.rs:17:5
+ |
+LL | x += {
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:32:12
++ --> $DIR/eval_order_dependence.rs:18:9
+ |
+LL | x = 20;
+ | ^^^^^^
+
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:34:13
++ --> $DIR/eval_order_dependence.rs:30:12
+ |
+LL | a: x,
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:41:9
++ --> $DIR/eval_order_dependence.rs:32:13
+ |
+LL | x = 6;
+ | ^^^^^
+
+error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:42:13
++ --> $DIR/eval_order_dependence.rs:39:9
+ |
+LL | x += {
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
++ --> $DIR/eval_order_dependence.rs:40:13
+ |
+LL | x = 20;
+ | ^^^^^^
+
+error: aborting due to 4 previous errors
+
--- /dev/null
- #![allow(clippy::if_then_panic)]
+#![deny(clippy::fallible_impl_from)]
+
+// 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:6:1
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:8:13
++ --> $DIR/fallible_impl_from.rs:5: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:27:1
++ --> $DIR/fallible_impl_from.rs:7:13
+ |
+LL | Foo(s.parse().unwrap())
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:30:13
++ --> $DIR/fallible_impl_from.rs:26: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)
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/fallible_impl_from.rs:29:13
+ |
+LL | panic!();
+ | ^^^^^^^^
- --> $DIR/fallible_impl_from.rs:36:1
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:38:17
++ --> $DIR/fallible_impl_from.rs:35: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)
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/fallible_impl_from.rs:37: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);
+ | ^^^^^^^^^^^^^^^^^
- --> $DIR/fallible_impl_from.rs:54:1
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:56:12
++ --> $DIR/fallible_impl_from.rs:53: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)
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/fallible_impl_from.rs:55:12
+ |
+LL | if s.parse::<u32>().ok().unwrap() != 42 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | panic!("{:?}", s);
+ | ^^^^^^^^^^^^^^^^^
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+// run-rustfix
+
+#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)]
+#![warn(clippy::useless_format)]
+
+struct Foo(pub String);
+
+macro_rules! foo {
+ ($($t:tt)*) => (Foo(format!($($t)*)))
+}
+
+fn main() {
+ "foo".to_string();
+ "{}".to_string();
+ "{} abc {}".to_string();
+ r##"foo {}
+" bar"##.to_string();
+
++ let _ = String::new();
++
+ "foo".to_string();
+ format!("{:?}", "foo"); // Don't warn about `Debug`.
+ format!("{:8}", "foo");
+ format!("{:width$}", "foo", width = 8);
+ "foo".to_string(); // Warn when the format makes no difference.
+ "foo".to_string(); // Warn when the format makes no difference.
+ format!("foo {}", "bar");
+ format!("{} bar", "foo");
+
+ let arg: String = "".to_owned();
+ arg.to_string();
+ format!("{:?}", arg); // Don't warn about debug.
+ format!("{:8}", arg);
+ format!("{:width$}", arg, width = 8);
+ arg.to_string(); // Warn when the format makes no difference.
+ arg.to_string(); // Warn when the format makes no difference.
+ format!("foo {}", arg);
+ format!("{} bar", arg);
+
+ // We don’t want to warn for non-string args; see issue #697.
+ format!("{}", 42);
+ format!("{:?}", 42);
+ format!("{:+}", 42);
+ format!("foo {}", 42);
+ format!("{} bar", 42);
+
+ // We only want to warn about `format!` itself.
+ println!("foo");
+ println!("{}", "foo");
+ println!("foo {}", "foo");
+ println!("{}", 42);
+ println!("foo {}", 42);
+
+ // A `format!` inside a macro should not trigger a warning.
+ foo!("should not warn");
+
+ // Precision on string means slicing without panicking on size.
+ format!("{:.1}", "foo"); // Could be `"foo"[..1]`
+ format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
+ format!("{:.prec$}", "foo", prec = 1);
+ format!("{:.prec$}", "foo", prec = 10);
+
+ 42.to_string();
+ let x = std::path::PathBuf::from("/bar/foo/qux");
+ x.display().to_string();
+
+ // False positive
+ let a = "foo".to_string();
+ let _ = Some(a + "bar");
+
+ // Wrap it with braces
+ let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+ let _s: String = (&*v.join("\n")).to_string();
+
+ format!("prepend {:+}", "s");
+}
--- /dev/null
+// run-rustfix
+
+#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)]
+#![warn(clippy::useless_format)]
+
+struct Foo(pub String);
+
+macro_rules! foo {
+ ($($t:tt)*) => (Foo(format!($($t)*)))
+}
+
+fn main() {
+ format!("foo");
+ format!("{{}}");
+ format!("{{}} abc {{}}");
+ format!(
+ r##"foo {{}}
+" bar"##
+ );
+
++ let _ = format!("");
++
+ format!("{}", "foo");
+ format!("{:?}", "foo"); // Don't warn about `Debug`.
+ format!("{:8}", "foo");
+ format!("{:width$}", "foo", width = 8);
+ format!("{:+}", "foo"); // Warn when the format makes no difference.
+ format!("{:<}", "foo"); // Warn when the format makes no difference.
+ format!("foo {}", "bar");
+ format!("{} bar", "foo");
+
+ let arg: String = "".to_owned();
+ format!("{}", arg);
+ format!("{:?}", arg); // Don't warn about debug.
+ format!("{:8}", arg);
+ format!("{:width$}", arg, width = 8);
+ format!("{:+}", arg); // Warn when the format makes no difference.
+ format!("{:<}", arg); // Warn when the format makes no difference.
+ format!("foo {}", arg);
+ format!("{} bar", arg);
+
+ // We don’t want to warn for non-string args; see issue #697.
+ format!("{}", 42);
+ format!("{:?}", 42);
+ format!("{:+}", 42);
+ format!("foo {}", 42);
+ format!("{} bar", 42);
+
+ // We only want to warn about `format!` itself.
+ println!("foo");
+ println!("{}", "foo");
+ println!("foo {}", "foo");
+ println!("{}", 42);
+ println!("foo {}", 42);
+
+ // A `format!` inside a macro should not trigger a warning.
+ foo!("should not warn");
+
+ // Precision on string means slicing without panicking on size.
+ format!("{:.1}", "foo"); // Could be `"foo"[..1]`
+ format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
+ format!("{:.prec$}", "foo", prec = 1);
+ format!("{:.prec$}", "foo", prec = 10);
+
+ format!("{}", 42.to_string());
+ let x = std::path::PathBuf::from("/bar/foo/qux");
+ format!("{}", x.display().to_string());
+
+ // False positive
+ let a = "foo".to_string();
+ let _ = Some(format!("{}", a + "bar"));
+
+ // Wrap it with braces
+ let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+ let _s: String = format!("{}", &*v.join("\n"));
+
+ format!("prepend {:+}", "s");
+}
--- /dev/null
- --> $DIR/format.rs:21:5
+error: useless use of `format!`
+ --> $DIR/format.rs:13:5
+ |
+LL | format!("foo");
+ | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+ |
+ = note: `-D clippy::useless-format` implied by `-D warnings`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:14:5
+ |
+LL | format!("{{}}");
+ | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:15:5
+ |
+LL | format!("{{}} abc {{}}");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:16:5
+ |
+LL | / format!(
+LL | | r##"foo {{}}
+LL | | " bar"##
+LL | | );
+ | |_____^
+ |
+help: consider using `.to_string()`
+ |
+LL ~ r##"foo {}
+LL ~ " bar"##.to_string();
+ |
+
+error: useless use of `format!`
- --> $DIR/format.rs:25:5
++ --> $DIR/format.rs:21:13
++ |
++LL | let _ = format!("");
++ | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:23:5
+ |
+LL | format!("{}", "foo");
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:26:5
++ --> $DIR/format.rs:27:5
+ |
+LL | format!("{:+}", "foo"); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:31:5
++ --> $DIR/format.rs:28:5
+ |
+LL | format!("{:<}", "foo"); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:35:5
++ --> $DIR/format.rs:33:5
+ |
+LL | format!("{}", arg);
+ | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:36:5
++ --> $DIR/format.rs:37:5
+ |
+LL | format!("{:+}", arg); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:63:5
++ --> $DIR/format.rs:38:5
+ |
+LL | format!("{:<}", arg); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:65:5
++ --> $DIR/format.rs:65:5
+ |
+LL | format!("{}", 42.to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:69:18
++ --> $DIR/format.rs:67:5
+ |
+LL | format!("{}", x.display().to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
+
+error: useless use of `format!`
- --> $DIR/format.rs:73:22
++ --> $DIR/format.rs:71:18
+ |
+LL | let _ = Some(format!("{}", a + "bar"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
+
+error: useless use of `format!`
- error: aborting due to 14 previous errors
++ --> $DIR/format.rs:75:22
+ |
+LL | let _s: String = format!("{}", &*v.join("/n"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
+
++error: aborting due to 15 previous errors
+
--- /dev/null
+// run-rustfix
+
+#![allow(unreachable_code)]
+#![allow(unused_macros)]
+#![allow(unused_variables)]
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::eq_op)]
++#![allow(clippy::print_literal)]
+#![warn(clippy::to_string_in_format_args)]
+
+use std::io::{stdout, Write};
+use std::ops::Deref;
+use std::panic::Location;
+
+struct Somewhere;
+
+impl ToString for Somewhere {
+ fn to_string(&self) -> String {
+ String::from("somewhere")
+ }
+}
+
+struct X(u32);
+
+impl Deref for X {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+struct Y<'a>(&'a X);
+
+impl<'a> Deref for Y<'a> {
+ type Target = &'a X;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+struct Z(u32);
+
+impl Deref for Z {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+impl std::fmt::Display for Z {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Z")
+ }
+}
+
+macro_rules! my_macro {
+ () => {
+ // here be dragons, do not enter (or lint)
+ println!("error: something failed at {}", Location::caller().to_string());
+ };
+}
+
+macro_rules! my_other_macro {
+ () => {
+ Location::caller().to_string()
+ };
+}
+
+fn main() {
+ let x = &X(1);
+ let x_ref = &x;
+
+ let _ = format!("error: something failed at {}", Location::caller());
+ let _ = write!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller()
+ );
+ let _ = writeln!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller()
+ );
+ print!("error: something failed at {}", Location::caller());
+ println!("error: something failed at {}", Location::caller());
+ eprint!("error: something failed at {}", Location::caller());
+ eprintln!("error: something failed at {}", Location::caller());
+ let _ = format_args!("error: something failed at {}", Location::caller());
+ assert!(true, "error: something failed at {}", Location::caller());
+ assert_eq!(0, 0, "error: something failed at {}", Location::caller());
+ assert_ne!(0, 0, "error: something failed at {}", Location::caller());
+ panic!("error: something failed at {}", Location::caller());
+ println!("{}", *X(1));
+ println!("{}", ***Y(&X(1)));
+ println!("{}", Z(1));
+ println!("{}", **x);
+ println!("{}", ***x_ref);
++ // https://github.com/rust-lang/rust-clippy/issues/7903
++ println!("{foo}{bar}", foo = "foo", bar = "bar");
++ println!("{foo}{bar}", foo = "foo", bar = "bar");
++ println!("{foo}{bar}", bar = "bar", foo = "foo");
++ println!("{foo}{bar}", bar = "bar", foo = "foo");
+
++ // negative tests
+ println!("error: something failed at {}", Somewhere.to_string());
++ // The next two tests are negative because caching the string might be faster than calling `<X as
++ // Display>::fmt` twice.
+ println!("{} and again {0}", x.to_string());
++ println!("{foo}{foo}", foo = "foo".to_string());
+ my_macro!();
+ println!("error: something failed at {}", my_other_macro!());
++ // https://github.com/rust-lang/rust-clippy/issues/7903
++ println!("{foo}{foo:?}", foo = "foo".to_string());
+}
--- /dev/null
+// run-rustfix
+
+#![allow(unreachable_code)]
+#![allow(unused_macros)]
+#![allow(unused_variables)]
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::eq_op)]
++#![allow(clippy::print_literal)]
+#![warn(clippy::to_string_in_format_args)]
+
+use std::io::{stdout, Write};
+use std::ops::Deref;
+use std::panic::Location;
+
+struct Somewhere;
+
+impl ToString for Somewhere {
+ fn to_string(&self) -> String {
+ String::from("somewhere")
+ }
+}
+
+struct X(u32);
+
+impl Deref for X {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+struct Y<'a>(&'a X);
+
+impl<'a> Deref for Y<'a> {
+ type Target = &'a X;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+struct Z(u32);
+
+impl Deref for Z {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+impl std::fmt::Display for Z {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Z")
+ }
+}
+
+macro_rules! my_macro {
+ () => {
+ // here be dragons, do not enter (or lint)
+ println!("error: something failed at {}", Location::caller().to_string());
+ };
+}
+
+macro_rules! my_other_macro {
+ () => {
+ Location::caller().to_string()
+ };
+}
+
+fn main() {
+ let x = &X(1);
+ let x_ref = &x;
+
+ let _ = format!("error: something failed at {}", Location::caller().to_string());
+ let _ = write!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller().to_string()
+ );
+ let _ = writeln!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller().to_string()
+ );
+ print!("error: something failed at {}", Location::caller().to_string());
+ println!("error: something failed at {}", Location::caller().to_string());
+ eprint!("error: something failed at {}", Location::caller().to_string());
+ eprintln!("error: something failed at {}", Location::caller().to_string());
+ let _ = format_args!("error: something failed at {}", Location::caller().to_string());
+ assert!(true, "error: something failed at {}", Location::caller().to_string());
+ assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ panic!("error: something failed at {}", Location::caller().to_string());
+ println!("{}", X(1).to_string());
+ println!("{}", Y(&X(1)).to_string());
+ println!("{}", Z(1).to_string());
+ println!("{}", x.to_string());
+ println!("{}", x_ref.to_string());
++ // https://github.com/rust-lang/rust-clippy/issues/7903
++ println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
++ println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
++ println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
++ println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
+
++ // negative tests
+ println!("error: something failed at {}", Somewhere.to_string());
++ // The next two tests are negative because caching the string might be faster than calling `<X as
++ // Display>::fmt` twice.
+ println!("{} and again {0}", x.to_string());
++ println!("{foo}{foo}", foo = "foo".to_string());
+ my_macro!();
+ println!("error: something failed at {}", my_other_macro!());
++ // https://github.com/rust-lang/rust-clippy/issues/7903
++ println!("{foo}{foo:?}", foo = "foo".to_string());
+}
--- /dev/null
- --> $DIR/format_args.rs:75:72
+error: `to_string` applied to a type that implements `Display` in `format!` args
- --> $DIR/format_args.rs:79:27
++ --> $DIR/format_args.rs:76:72
+ |
+LL | let _ = format!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+ |
+ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
+
+error: `to_string` applied to a type that implements `Display` in `write!` args
- --> $DIR/format_args.rs:84:27
++ --> $DIR/format_args.rs:80:27
+ |
+LL | Location::caller().to_string()
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `writeln!` args
- --> $DIR/format_args.rs:86:63
++ --> $DIR/format_args.rs:85:27
+ |
+LL | Location::caller().to_string()
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `print!` args
- --> $DIR/format_args.rs:87:65
++ --> $DIR/format_args.rs:87:63
+ |
+LL | print!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:88:64
++ --> $DIR/format_args.rs:88:65
+ |
+LL | println!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `eprint!` args
- --> $DIR/format_args.rs:89:66
++ --> $DIR/format_args.rs:89:64
+ |
+LL | eprint!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `eprintln!` args
- --> $DIR/format_args.rs:90:77
++ --> $DIR/format_args.rs:90:66
+ |
+LL | eprintln!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `format_args!` args
- --> $DIR/format_args.rs:91:70
++ --> $DIR/format_args.rs:91:77
+ |
+LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `assert!` args
- --> $DIR/format_args.rs:92:73
++ --> $DIR/format_args.rs:92:70
+ |
+LL | assert!(true, "error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
- --> $DIR/format_args.rs:93:73
++ --> $DIR/format_args.rs:93:73
+ |
+LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
- --> $DIR/format_args.rs:94:63
++ --> $DIR/format_args.rs:94:73
+ |
+LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `panic!` args
- --> $DIR/format_args.rs:95:20
++ --> $DIR/format_args.rs:95:63
+ |
+LL | panic!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:96:20
++ --> $DIR/format_args.rs:96:20
+ |
+LL | println!("{}", X(1).to_string());
+ | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:97:24
++ --> $DIR/format_args.rs:97:20
+ |
+LL | println!("{}", Y(&X(1)).to_string());
+ | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:98:20
++ --> $DIR/format_args.rs:98:24
+ |
+LL | println!("{}", Z(1).to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:99:20
++ --> $DIR/format_args.rs:99:20
+ |
+LL | println!("{}", x.to_string());
+ | ^^^^^^^^^^^^^ help: use this: `**x`
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
- error: aborting due to 17 previous errors
++ --> $DIR/format_args.rs:100:20
+ |
+LL | println!("{}", x_ref.to_string());
+ | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
+
++error: `to_string` applied to a type that implements `Display` in `println!` args
++ --> $DIR/format_args.rs:102:39
++ |
++LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
++ | ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++ --> $DIR/format_args.rs:103:52
++ |
++LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
++ | ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++ --> $DIR/format_args.rs:104:39
++ |
++LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
++ | ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++ --> $DIR/format_args.rs:105:52
++ |
++LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
++ | ^^^^^^^^^^^^ help: remove this
++
++error: aborting due to 21 previous errors
+
--- /dev/null
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::eq_op)]
+#![warn(clippy::format_in_format_args)]
+#![warn(clippy::to_string_in_format_args)]
+
+use std::io::{stdout, Error, ErrorKind, Write};
+use std::ops::Deref;
+use std::panic::Location;
+
+macro_rules! my_macro {
+ () => {
+ // here be dragons, do not enter (or lint)
+ println!("error: {}", format!("something failed at {}", Location::caller()));
+ };
+}
+
+macro_rules! my_other_macro {
+ () => {
+ format!("something failed at {}", Location::caller())
+ };
+}
+
+fn main() {
+ let error = Error::new(ErrorKind::Other, "bad thing");
+ let x = 'x';
+
+ println!("error: {}", format!("something failed at {}", Location::caller()));
+ println!("{}: {}", error, format!("something failed at {}", Location::caller()));
+ println!("{:?}: {}", error, format!("something failed at {}", Location::caller()));
+ println!("{{}}: {}", format!("something failed at {}", Location::caller()));
+ println!(r#"error: "{}""#, format!("something failed at {}", Location::caller()));
+ println!("error: {}", format!(r#"something failed at "{}""#, Location::caller()));
+ println!("error: {}", format!("something failed at {} {0}", Location::caller()));
+ let _ = format!("error: {}", format!("something failed at {}", Location::caller()));
+ let _ = write!(
+ stdout(),
+ "error: {}",
+ format!("something failed at {}", Location::caller())
+ );
+ let _ = writeln!(
+ stdout(),
+ "error: {}",
+ format!("something failed at {}", Location::caller())
+ );
+ print!("error: {}", format!("something failed at {}", Location::caller()));
+ eprint!("error: {}", format!("something failed at {}", Location::caller()));
+ eprintln!("error: {}", format!("something failed at {}", Location::caller()));
+ let _ = format_args!("error: {}", format!("something failed at {}", Location::caller()));
+ assert!(true, "error: {}", format!("something failed at {}", Location::caller()));
+ assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
+ assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
+ panic!("error: {}", format!("something failed at {}", Location::caller()));
+
++ // negative tests
+ println!("error: {}", format_args!("something failed at {}", Location::caller()));
+ println!("error: {:>70}", format!("something failed at {}", Location::caller()));
+ println!("error: {} {0}", format!("something failed at {}", Location::caller()));
+ println!("{} and again {0}", format!("hi {}", x));
+ my_macro!();
+ println!("error: {}", my_other_macro!());
+}
--- /dev/null
- // edition:2018
+#![warn(clippy::future_not_send)]
+
+use std::cell::Cell;
+use std::rc::Rc;
+use std::sync::Arc;
+
+async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ async { true }.await
+}
+
+pub async fn public_future(rc: Rc<[u8]>) {
+ async { true }.await;
+}
+
+pub async fn public_send(arc: Arc<[u8]>) -> bool {
+ async { false }.await
+}
+
+async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ true
+}
+
+pub async fn public_future2(rc: Rc<[u8]>) {}
+
+pub async fn public_send2(arc: Arc<[u8]>) -> bool {
+ false
+}
+
+struct Dummy {
+ rc: Rc<[u8]>,
+}
+
+impl Dummy {
+ async fn private_future(&self) -> usize {
+ async { true }.await;
+ self.rc.len()
+ }
+
+ pub async fn public_future(&self) {
+ self.private_future().await;
+ }
+
+ #[allow(clippy::manual_async_fn)]
+ pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
+ async { false }
+ }
+}
+
+async fn generic_future<T>(t: T) -> T
+where
+ T: Send,
+{
+ let rt = &t;
+ async { true }.await;
+ t
+}
+
+async fn generic_future_send<T>(t: T)
+where
+ T: Send,
+{
+ async { true }.await;
+}
+
+async fn unclear_future<T>(t: T) {}
+
+fn main() {
+ let rc = Rc::new([1, 2, 3]);
+ private_future(rc.clone(), &Cell::new(42));
+ public_future(rc.clone());
+ let arc = Arc::new([4, 5, 6]);
+ public_send(arc);
+ generic_future(42);
+ generic_future_send(42);
+
+ let dummy = Dummy { rc };
+ dummy.public_future();
+ dummy.public_send();
+}
--- /dev/null
- --> $DIR/future_not_send.rs:8:62
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:9:5
++ --> $DIR/future_not_send.rs:7:62
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^ future returned by `private_future` is not `Send`
+ |
+ = note: `-D clippy::future-not-send` implied by `-D warnings`
+note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:9:5
++ --> $DIR/future_not_send.rs:8:5
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
+LL | async { true }.await
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
+LL | }
+ | - `rc` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:12:42
++ --> $DIR/future_not_send.rs:8:5
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ---- has type `&std::cell::Cell<usize>` which is not `Send`
+LL | async { true }.await
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `cell` maybe used later
+LL | }
+ | - `cell` is later dropped here
+ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:13:5
++ --> $DIR/future_not_send.rs:11:42
+ |
+LL | pub async fn public_future(rc: Rc<[u8]>) {
+ | ^ future returned by `public_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:20:63
++ --> $DIR/future_not_send.rs:12:5
+ |
+LL | pub async fn public_future(rc: Rc<[u8]>) {
+ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
+LL | }
+ | - `rc` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:20:26
++ --> $DIR/future_not_send.rs:19:63
+ |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^ future returned by `private_future2` is not `Send`
+ |
+note: captured value is not `Send`
- --> $DIR/future_not_send.rs:20:40
++ --> $DIR/future_not_send.rs:19:26
+ |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
- --> $DIR/future_not_send.rs:24:43
++ --> $DIR/future_not_send.rs:19:40
+ |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^ has type `&std::cell::Cell<usize>` which is not `Send`, because `std::cell::Cell<usize>` is not `Sync`
+ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:24:29
++ --> $DIR/future_not_send.rs:23:43
+ |
+LL | pub async fn public_future2(rc: Rc<[u8]>) {}
+ | ^ future returned by `public_future2` is not `Send`
+ |
+note: captured value is not `Send`
- --> $DIR/future_not_send.rs:35:39
++ --> $DIR/future_not_send.rs:23:29
+ |
+LL | pub async fn public_future2(rc: Rc<[u8]>) {}
+ | ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:36:9
++ --> $DIR/future_not_send.rs:34:39
+ |
+LL | async fn private_future(&self) -> usize {
+ | ^^^^^ future returned by `private_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:40:39
++ --> $DIR/future_not_send.rs:35:9
+ |
+LL | async fn private_future(&self) -> usize {
+ | ----- has type `&Dummy` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
+LL | self.rc.len()
+LL | }
+ | - `&self` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:41:9
++ --> $DIR/future_not_send.rs:39:39
+ |
+LL | pub async fn public_future(&self) {
+ | ^ future returned by `public_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:50:37
++ --> $DIR/future_not_send.rs:40:9
+ |
+LL | pub async fn public_future(&self) {
+ | ----- has type `&Dummy` which is not `Send`
+LL | self.private_future().await;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
+LL | }
+ | - `&self` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:55:5
++ --> $DIR/future_not_send.rs:49:37
+ |
+LL | async fn generic_future<T>(t: T) -> T
+ | ^ future returned by `generic_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:66:34
++ --> $DIR/future_not_send.rs:54:5
+ |
+LL | let rt = &t;
+ | -- has type `&T` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rt` maybe used later
+LL | t
+LL | }
+ | - `rt` is later dropped here
+ = note: `T` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:66:28
++ --> $DIR/future_not_send.rs:65:34
+ |
+LL | async fn unclear_future<T>(t: T) {}
+ | ^ future returned by `unclear_future` is not `Send`
+ |
+note: captured value is not `Send`
++ --> $DIR/future_not_send.rs:65:28
+ |
+LL | async fn unclear_future<T>(t: T) {}
+ | ^ has type `T` which is not `Send`
+ = note: `T` doesn't implement `std::marker::Send`
+
+error: aborting due to 8 previous errors
+
--- /dev/null
+#![warn(clippy::all)]
+#![warn(clippy::if_not_else)]
+
++fn foo() -> bool {
++ unimplemented!()
++}
+fn bla() -> bool {
+ unimplemented!()
+}
+
+fn main() {
+ if !bla() {
+ println!("Bugs");
+ } else {
+ println!("Bunny");
+ }
+ if 4 != 5 {
+ println!("Bugs");
+ } else {
+ println!("Bunny");
+ }
++ if !foo() {
++ println!("Foo");
++ } else if !bla() {
++ println!("Bugs");
++ } else {
++ println!("Bunny");
++ }
+}
--- /dev/null
- --> $DIR/if_not_else.rs:9:5
+error: unnecessary boolean `not` operation
- --> $DIR/if_not_else.rs:14:5
++ --> $DIR/if_not_else.rs:12:5
+ |
+LL | / if !bla() {
+LL | | println!("Bugs");
+LL | | } else {
+LL | | println!("Bunny");
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::if-not-else` implied by `-D warnings`
+ = help: remove the `!` and swap the blocks of the `if`/`else`
+
+error: unnecessary `!=` operation
++ --> $DIR/if_not_else.rs:17:5
+ |
+LL | / if 4 != 5 {
+LL | | println!("Bugs");
+LL | | } else {
+LL | | println!("Bunny");
+LL | | }
+ | |_____^
+ |
+ = help: change to `==` and swap the blocks of the `if`/`else`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
- // edition:2018
+// aux-build:implicit_hasher_macros.rs
+#![deny(clippy::implicit_hasher)]
+#![allow(unused)]
+
+#[macro_use]
+extern crate implicit_hasher_macros;
+
+use std::cmp::Eq;
+use std::collections::{HashMap, HashSet};
+use std::hash::{BuildHasher, Hash};
+
+pub trait Foo<T>: Sized {
+ fn make() -> (Self, Self);
+}
+
+impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
+ fn make() -> (Self, Self) {
+ // OK, don't suggest to modify these
+ let _: HashMap<i32, i32> = HashMap::new();
+ let _: HashSet<i32> = HashSet::new();
+
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+}
+impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
+ fn make() -> (Self, Self) {
+ ((HashMap::new(),), (HashMap::with_capacity(10),))
+ }
+}
+impl Foo<i16> for HashMap<String, String> {
+ fn make() -> (Self, Self) {
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+}
+
+impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
+ fn make() -> (Self, Self) {
+ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+ }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
+ fn make() -> (Self, Self) {
+ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+ }
+}
+
+impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
+ fn make() -> (Self, Self) {
+ (HashSet::new(), HashSet::with_capacity(10))
+ }
+}
+impl Foo<i16> for HashSet<String> {
+ fn make() -> (Self, Self) {
+ (HashSet::new(), HashSet::with_capacity(10))
+ }
+}
+
+impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
+ fn make() -> (Self, Self) {
+ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+ }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
+ fn make() -> (Self, Self) {
+ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+ }
+}
+
+pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+
+macro_rules! gen {
+ (impl) => {
+ impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
+ fn make() -> (Self, Self) {
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+ }
+ };
+
+ (fn $name:ident) => {
+ pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ };
+}
+#[rustfmt::skip]
+gen!(impl);
+gen!(fn bar);
+
+// When the macro is in a different file, the suggestion spans can't be combined properly
+// and should not cause an ICE
+// See #2707
+#[macro_use]
+#[path = "auxiliary/test_macro.rs"]
+pub mod test_macro;
+__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
+
+// #4260
+implicit_hasher_fn!();
+
+// #7712
+pub async fn election_vote(_data: HashMap<i32, i32>) {}
+
+fn main() {}
--- /dev/null
- --> $DIR/implicit_hasher.rs:17:35
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:3:9
++ --> $DIR/implicit_hasher.rs:16:35
+ |
+LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
+ | ^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
- --> $DIR/implicit_hasher.rs:26:36
++ --> $DIR/implicit_hasher.rs:2:9
+ |
+LL | #![deny(clippy::implicit_hasher)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:31:19
++ --> $DIR/implicit_hasher.rs:25:36
+ |
+LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
+ | ^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:48:32
++ --> $DIR/implicit_hasher.rs:30:19
+ |
+LL | impl Foo<i16> for HashMap<String, String> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
+ | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:53:19
++ --> $DIR/implicit_hasher.rs:47:32
+ |
+LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
+ | ^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:70:23
++ --> $DIR/implicit_hasher.rs:52:19
+ |
+LL | impl Foo<i16> for HashSet<String> {
+ | ^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
+ | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:70:53
++ --> $DIR/implicit_hasher.rs:69:23
+ |
+LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:74:43
++ --> $DIR/implicit_hasher.rs:69:53
+ |
+LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:82:33
++ --> $DIR/implicit_hasher.rs:73:43
+ |
+LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
+ | ^^^^^^^^^^^^^
+...
+LL | gen!(impl);
+ | ---------- in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:82:63
++ --> $DIR/implicit_hasher.rs:81:33
+ |
+LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | gen!(fn bar);
+ | ------------ in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:101:35
++ --> $DIR/implicit_hasher.rs:81:63
+ |
+LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^
+...
+LL | gen!(fn bar);
+ | ------------ in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:100:35
+ |
+LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 11 previous errors
+
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![warn(clippy::implicit_return)]
+#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+
+ return true
+}
+
+fn test_if_block() -> bool {
+ if true { return true } else { return false }
+}
+
+#[rustfmt::skip]
+fn test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => { return true },
+ }
+}
+
+fn test_match_with_unreachable(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => unreachable!(),
+ }
+}
+
+fn test_loop() -> bool {
+ loop {
+ return true;
+ }
+}
+
+fn test_loop_with_block() -> bool {
+ loop {
+ {
+ return true;
+ }
+ }
+}
+
+fn test_loop_with_nests() -> bool {
+ loop {
+ if true {
+ return true;
+ } else {
+ let _ = true;
+ }
+ }
+}
+
+#[allow(clippy::redundant_pattern_matching)]
+fn test_loop_with_if_let() -> bool {
+ loop {
+ if let Some(x) = Some(true) {
+ return x;
+ }
+ }
+}
+
+fn test_closure() {
+ #[rustfmt::skip]
+ let _ = || { return true };
+ let _ = || return true;
+}
+
+fn test_panic() -> bool {
+ panic!()
+}
+
+fn test_return_macro() -> String {
+ return format!("test {}", "test")
+}
+
+fn macro_branch_test() -> bool {
+ macro_rules! m {
+ ($t:expr, $f:expr) => {
+ if true { $t } else { $f }
+ };
+ }
+ return m!(true, false)
+}
+
+fn loop_test() -> bool {
+ 'outer: loop {
+ if true {
+ return true;
+ }
+
+ let _ = loop {
+ if false {
+ return false;
+ }
+ if true {
+ break true;
+ }
+ };
+ }
+}
+
+fn loop_macro_test() -> bool {
+ macro_rules! m {
+ ($e:expr) => {
+ break $e
+ };
+ }
+ return loop {
+ m!(true);
+ }
+}
+
+fn divergent_test() -> bool {
+ fn diverge() -> ! {
+ panic!()
+ }
+ diverge()
+}
+
+// issue #6940
+async fn foo() -> bool {
+ return true
+}
+
+fn main() {}
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![warn(clippy::implicit_return)]
+#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+
+ true
+}
+
+fn test_if_block() -> bool {
+ if true { true } else { false }
+}
+
+#[rustfmt::skip]
+fn test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => { true },
+ }
+}
+
+fn test_match_with_unreachable(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => unreachable!(),
+ }
+}
+
+fn test_loop() -> bool {
+ loop {
+ break true;
+ }
+}
+
+fn test_loop_with_block() -> bool {
+ loop {
+ {
+ break true;
+ }
+ }
+}
+
+fn test_loop_with_nests() -> bool {
+ loop {
+ if true {
+ break true;
+ } else {
+ let _ = true;
+ }
+ }
+}
+
+#[allow(clippy::redundant_pattern_matching)]
+fn test_loop_with_if_let() -> bool {
+ loop {
+ if let Some(x) = Some(true) {
+ return x;
+ }
+ }
+}
+
+fn test_closure() {
+ #[rustfmt::skip]
+ let _ = || { true };
+ let _ = || true;
+}
+
+fn test_panic() -> bool {
+ panic!()
+}
+
+fn test_return_macro() -> String {
+ format!("test {}", "test")
+}
+
+fn macro_branch_test() -> bool {
+ macro_rules! m {
+ ($t:expr, $f:expr) => {
+ if true { $t } else { $f }
+ };
+ }
+ m!(true, false)
+}
+
+fn loop_test() -> bool {
+ 'outer: loop {
+ if true {
+ break true;
+ }
+
+ let _ = loop {
+ if false {
+ break 'outer false;
+ }
+ if true {
+ break true;
+ }
+ };
+ }
+}
+
+fn loop_macro_test() -> bool {
+ macro_rules! m {
+ ($e:expr) => {
+ break $e
+ };
+ }
+ loop {
+ m!(true);
+ }
+}
+
+fn divergent_test() -> bool {
+ fn diverge() -> ! {
+ panic!()
+ }
+ diverge()
+}
+
+// issue #6940
+async fn foo() -> bool {
+ true
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/implicit_return.rs:13:5
+error: missing `return` statement
- --> $DIR/implicit_return.rs:17:15
++ --> $DIR/implicit_return.rs:12:5
+ |
+LL | true
+ | ^^^^ help: add `return` as shown: `return true`
+ |
+ = note: `-D clippy::implicit-return` implied by `-D warnings`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:17:29
++ --> $DIR/implicit_return.rs:16:15
+ |
+LL | if true { true } else { false }
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:23:17
++ --> $DIR/implicit_return.rs:16:29
+ |
+LL | if true { true } else { false }
+ | ^^^^^ help: add `return` as shown: `return false`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:24:20
++ --> $DIR/implicit_return.rs:22:17
+ |
+LL | true => false,
+ | ^^^^^ help: add `return` as shown: `return false`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:37:9
++ --> $DIR/implicit_return.rs:23:20
+ |
+LL | false => { true },
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:44:13
++ --> $DIR/implicit_return.rs:36:9
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:52:13
++ --> $DIR/implicit_return.rs:43:13
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:70:18
++ --> $DIR/implicit_return.rs:51:13
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:71:16
++ --> $DIR/implicit_return.rs:69:18
+ |
+LL | let _ = || { true };
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:79:5
++ --> $DIR/implicit_return.rs:70:16
+ |
+LL | let _ = || true;
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:88:5
++ --> $DIR/implicit_return.rs:78:5
+ |
+LL | format!("test {}", "test")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:94:13
++ --> $DIR/implicit_return.rs:87:5
+ |
+LL | m!(true, false)
+ | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:99:17
++ --> $DIR/implicit_return.rs:93:13
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:114:5
++ --> $DIR/implicit_return.rs:98:17
+ |
+LL | break 'outer false;
+ | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false`
+
+error: missing `return` statement
- --> $DIR/implicit_return.rs:128:5
++ --> $DIR/implicit_return.rs:113:5
+ |
+LL | / loop {
+LL | | m!(true);
+LL | | }
+ | |_____^
+ |
+help: add `return` as shown
+ |
+LL ~ return loop {
+LL + m!(true);
+LL + }
+ |
+
+error: missing `return` statement
++ --> $DIR/implicit_return.rs:127:5
+ |
+LL | true
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: aborting due to 16 previous errors
+
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::inconsistent_struct_constructor)]
+#![allow(clippy::redundant_field_names)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::no_effect)]
+#![allow(dead_code)]
+
+#[derive(Default)]
+struct Foo {
+ x: i32,
+ y: i32,
+ z: i32,
+}
+
+macro_rules! new_foo {
+ () => {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ };
+}
+
+mod without_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo { x, y, z };
+
+ // Should NOT lint.
+ // issue #7069.
+ new_foo!();
+
+ // Shoule NOT lint because the order is the same as in the definition.
+ Foo { x, y, z };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo { y, x, z: z };
+ }
+}
+
+mod with_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo { x, z, ..Default::default() };
+
+ // Should NOT lint because the order is consistent with the definition.
+ Foo {
+ x,
+ z,
+ ..Default::default()
+ };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo {
+ z: z,
+ x,
+ ..Default::default()
+ };
+ }
+}
+
+fn main() {}
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::inconsistent_struct_constructor)]
+#![allow(clippy::redundant_field_names)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::no_effect)]
+#![allow(dead_code)]
+
+#[derive(Default)]
+struct Foo {
+ x: i32,
+ y: i32,
+ z: i32,
+}
+
+macro_rules! new_foo {
+ () => {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ };
+}
+
+mod without_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo { y, x, z };
+
+ // Should NOT lint.
+ // issue #7069.
+ new_foo!();
+
+ // Shoule NOT lint because the order is the same as in the definition.
+ Foo { x, y, z };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo { y, x, z: z };
+ }
+}
+
+mod with_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo {
+ z,
+ x,
+ ..Default::default()
+ };
+
+ // Should NOT lint because the order is consistent with the definition.
+ Foo {
+ x,
+ z,
+ ..Default::default()
+ };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo {
+ z: z,
+ x,
+ ..Default::default()
+ };
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/inconsistent_struct_constructor.rs:34:9
+error: struct constructor field order is inconsistent with struct definition field order
- --> $DIR/inconsistent_struct_constructor.rs:56:9
++ --> $DIR/inconsistent_struct_constructor.rs:33:9
+ |
+LL | Foo { y, x, z };
+ | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
+ |
+ = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
+
+error: struct constructor field order is inconsistent with struct definition field order
++ --> $DIR/inconsistent_struct_constructor.rs:55:9
+ |
+LL | / Foo {
+LL | | z,
+LL | | x,
+LL | | ..Default::default()
+LL | | };
+ | |_________^ help: try: `Foo { x, z, ..Default::default() }`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
--- /dev/null
++error: sub-expression diverges
++ --> $DIR/issue-7447.rs:23:15
++ |
++LL | byte_view(panic!());
++ | ^^^^^^^^
++ |
++ = note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: sub-expression diverges
++ --> $DIR/issue-7447.rs:24:19
++ |
++LL | group_entries(panic!());
++ | ^^^^^^^^
++ |
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 2 previous errors
++
--- /dev/null
- // edition:2018
+#![allow(dead_code)]
+
+async fn sink1<'a>(_: &'a str) {} // lint
+async fn sink1_elided(_: &str) {} // ok
+
+// lint
+async fn one_to_one<'a>(s: &'a str) -> &'a str {
+ s
+}
+
+// ok
+async fn one_to_one_elided(s: &str) -> &str {
+ s
+}
+
+// ok
+async fn all_to_one<'a>(a: &'a str, _b: &'a str) -> &'a str {
+ a
+}
+
+// async fn unrelated(_: &str, _: &str) {} // Not allowed in async fn
+
+// #3988
+struct Foo;
+impl Foo {
+ // ok
+ pub async fn new(&mut self) -> Self {
+ Foo {}
+ }
+}
+
+// rust-lang/rust#61115
+// ok
+async fn print(s: &str) {
+ println!("{}", s);
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/issue_4266.rs:4:1
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/issue_4266.rs:8:1
++ --> $DIR/issue_4266.rs:3:1
+ |
+LL | async fn sink1<'a>(_: &'a str) {} // lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/issue_4266.rs:7:1
+ |
+LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
- // edition:2018
-
+#![warn(clippy::len_without_is_empty)]
+#![allow(dead_code, unused)]
+
+pub struct PubOne;
+
+impl PubOne {
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+impl PubOne {
+ // A second impl for this struct -- the error span shouldn't mention this.
+ pub fn irrelevant(&self) -> bool {
+ false
+ }
+}
+
+// Identical to `PubOne`, but with an `allow` attribute on the impl complaining `len`.
+pub struct PubAllowed;
+
+#[allow(clippy::len_without_is_empty)]
+impl PubAllowed {
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
+// impl containing `len`.
+impl PubAllowed {
+ pub fn irrelevant(&self) -> bool {
+ false
+ }
+}
+
+pub struct PubAllowedFn;
+
+impl PubAllowedFn {
+ #[allow(clippy::len_without_is_empty)]
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+#[allow(clippy::len_without_is_empty)]
+pub struct PubAllowedStruct;
+
+impl PubAllowedStruct {
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+pub trait PubTraitsToo {
+ fn len(&self) -> isize;
+}
+
+impl PubTraitsToo for One {
+ fn len(&self) -> isize {
+ 0
+ }
+}
+
+pub struct HasIsEmpty;
+
+impl HasIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+pub struct HasWrongIsEmpty;
+
+impl HasWrongIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ pub fn is_empty(&self, x: u32) -> bool {
+ false
+ }
+}
+
+pub struct MismatchedSelf;
+
+impl MismatchedSelf {
+ pub fn len(self) -> isize {
+ 1
+ }
+
+ pub fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+struct NotPubOne;
+
+impl NotPubOne {
+ pub fn len(&self) -> isize {
+ // No error; `len` is pub but `NotPubOne` is not exported anyway.
+ 1
+ }
+}
+
+struct One;
+
+impl One {
+ fn len(&self) -> isize {
+ // No error; `len` is private; see issue #1085.
+ 1
+ }
+}
+
+trait TraitsToo {
+ fn len(&self) -> isize;
+ // No error; `len` is private; see issue #1085.
+}
+
+impl TraitsToo for One {
+ fn len(&self) -> isize {
+ 0
+ }
+}
+
+struct HasPrivateIsEmpty;
+
+impl HasPrivateIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+struct Wither;
+
+pub trait WithIsEmpty {
+ fn len(&self) -> isize;
+ fn is_empty(&self) -> bool;
+}
+
+impl WithIsEmpty for Wither {
+ fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+pub trait Empty {
+ fn is_empty(&self) -> bool;
+}
+
+pub trait InheritingEmpty: Empty {
+ // Must not trigger `LEN_WITHOUT_IS_EMPTY`.
+ fn len(&self) -> isize;
+}
+
+// This used to ICE.
+pub trait Foo: Sized {}
+
+pub trait DependsOnFoo: Foo {
+ fn len(&mut self) -> usize;
+}
+
+// issue #1562
+pub struct MultipleImpls;
+
+impl MultipleImpls {
+ pub fn len(&self) -> usize {
+ 1
+ }
+}
+
+impl MultipleImpls {
+ pub fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+// issue #6958
+pub struct OptionalLen;
+
+impl OptionalLen {
+ pub fn len(&self) -> Option<usize> {
+ Some(0)
+ }
+
+ pub fn is_empty(&self) -> Option<bool> {
+ Some(true)
+ }
+}
+
+pub struct OptionalLen2;
+impl OptionalLen2 {
+ pub fn len(&self) -> Option<usize> {
+ Some(0)
+ }
+
+ pub fn is_empty(&self) -> bool {
+ true
+ }
+}
+
+pub struct OptionalLen3;
+impl OptionalLen3 {
+ pub fn len(&self) -> usize {
+ 0
+ }
+
+ // should lint, len is not an option
+ pub fn is_empty(&self) -> Option<bool> {
+ None
+ }
+}
+
+pub struct ResultLen;
+impl ResultLen {
+ pub fn len(&self) -> Result<usize, ()> {
+ Ok(0)
+ }
+
+ // Differing result types
+ pub fn is_empty(&self) -> Option<bool> {
+ Some(true)
+ }
+}
+
+pub struct ResultLen2;
+impl ResultLen2 {
+ pub fn len(&self) -> Result<usize, ()> {
+ Ok(0)
+ }
+
+ pub fn is_empty(&self) -> Result<bool, ()> {
+ Ok(true)
+ }
+}
+
+pub struct ResultLen3;
+impl ResultLen3 {
+ pub fn len(&self) -> Result<usize, ()> {
+ Ok(0)
+ }
+
+ // Non-fallible result is ok.
+ pub fn is_empty(&self) -> bool {
+ true
+ }
+}
+
+pub struct OddLenSig;
+impl OddLenSig {
+ // don't lint
+ pub fn len(&self) -> bool {
+ true
+ }
+}
+
+// issue #6958
+pub struct AsyncLen;
+impl AsyncLen {
+ async fn async_task(&self) -> bool {
+ true
+ }
+
+ pub async fn len(&self) -> usize {
+ if self.async_task().await { 0 } else { 1 }
+ }
+
+ pub async fn is_empty(&self) -> bool {
+ self.len().await == 0
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/len_without_is_empty.rs:9:5
+error: struct `PubOne` has a public `len` method, but no `is_empty` method
- --> $DIR/len_without_is_empty.rs:57:1
++ --> $DIR/len_without_is_empty.rs:7:5
+ |
+LL | pub fn len(&self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::len-without-is-empty` implied by `-D warnings`
+
+error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method
- --> $DIR/len_without_is_empty.rs:70:5
++ --> $DIR/len_without_is_empty.rs:55:1
+ |
+LL | / pub trait PubTraitsToo {
+LL | | fn len(&self) -> isize;
+LL | | }
+ | |_^
+
+error: struct `HasIsEmpty` has a public `len` method, but a private `is_empty` method
- --> $DIR/len_without_is_empty.rs:74:5
++ --> $DIR/len_without_is_empty.rs:68:5
+ |
+LL | pub fn len(&self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
- --> $DIR/len_without_is_empty.rs:82:5
++ --> $DIR/len_without_is_empty.rs:72:5
+ |
+LL | fn is_empty(&self) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `HasWrongIsEmpty` has a public `len` method, but the `is_empty` method has an unexpected signature
- --> $DIR/len_without_is_empty.rs:86:5
++ --> $DIR/len_without_is_empty.rs:80:5
+ |
+LL | pub fn len(&self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
- --> $DIR/len_without_is_empty.rs:94:5
++ --> $DIR/len_without_is_empty.rs:84:5
+ |
+LL | pub fn is_empty(&self, x: u32) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(&self) -> bool`
+
+error: struct `MismatchedSelf` has a public `len` method, but the `is_empty` method has an unexpected signature
- --> $DIR/len_without_is_empty.rs:98:5
++ --> $DIR/len_without_is_empty.rs:92:5
+ |
+LL | pub fn len(self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
- --> $DIR/len_without_is_empty.rs:173:1
++ --> $DIR/len_without_is_empty.rs:96:5
+ |
+LL | pub fn is_empty(&self) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(self) -> bool`
+
+error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method
- --> $DIR/len_without_is_empty.rs:218:5
++ --> $DIR/len_without_is_empty.rs:171:1
+ |
+LL | / pub trait DependsOnFoo: Foo {
+LL | | fn len(&mut self) -> usize;
+LL | | }
+ | |_^
+
+error: struct `OptionalLen3` has a public `len` method, but the `is_empty` method has an unexpected signature
- --> $DIR/len_without_is_empty.rs:223:5
++ --> $DIR/len_without_is_empty.rs:216:5
+ |
+LL | pub fn len(&self) -> usize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
- --> $DIR/len_without_is_empty.rs:230:5
++ --> $DIR/len_without_is_empty.rs:221:5
+ |
+LL | pub fn is_empty(&self) -> Option<bool> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(&self) -> bool`
+
+error: struct `ResultLen` has a public `len` method, but the `is_empty` method has an unexpected signature
- --> $DIR/len_without_is_empty.rs:235:5
++ --> $DIR/len_without_is_empty.rs:228:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
- --> $DIR/len_without_is_empty.rs:230:5
++ --> $DIR/len_without_is_empty.rs:233:5
+ |
+LL | pub fn is_empty(&self) -> Option<bool> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(&self) -> bool` or `(&self) -> Result<bool>
+
+error: this returns a `Result<_, ()>`
- --> $DIR/len_without_is_empty.rs:242:5
++ --> $DIR/len_without_is_empty.rs:228:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::result-unit-err` implied by `-D warnings`
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
- --> $DIR/len_without_is_empty.rs:246:5
++ --> $DIR/len_without_is_empty.rs:240:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
- --> $DIR/len_without_is_empty.rs:253:5
++ --> $DIR/len_without_is_empty.rs:244:5
+ |
+LL | pub fn is_empty(&self) -> Result<bool, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
++ --> $DIR/len_without_is_empty.rs:251:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: aborting due to 12 previous errors
+
--- /dev/null
- #![allow(clippy::unseparated_literal_suffix)]
+// does not test any rustfixable lints
+
+#![warn(clippy::mixed_case_hex_literals)]
+#![warn(clippy::zero_prefixed_literal)]
++#![warn(clippy::unseparated_literal_suffix)]
++#![warn(clippy::separated_literal_suffix)]
+#![allow(dead_code)]
+
+fn main() {
+ let ok1 = 0xABCD;
+ let ok3 = 0xab_cd;
+ let ok4 = 0xab_cd_i32;
+ let ok5 = 0xAB_CD_u32;
+ let ok5 = 0xAB_CD_isize;
+ let fail1 = 0xabCD;
+ let fail2 = 0xabCD_u32;
+ let fail2 = 0xabCD_isize;
+ let fail_multi_zero = 000_123usize;
+
+ let ok9 = 0;
+ let ok10 = 0_i64;
+ let fail8 = 0123;
+
+ let ok11 = 0o123;
+ let ok12 = 0b10_1010;
+
+ let ok13 = 0xab_abcd;
+ let ok14 = 0xBAFE_BAFE;
+ let ok15 = 0xab_cabc_abca_bcab_cabc;
+ let ok16 = 0xFE_BAFE_ABAB_ABCD;
+ let ok17 = 0x123_4567_8901_usize;
+ let ok18 = 0xF;
+
+ let fail19 = 12_3456_21;
+ let fail22 = 3__4___23;
+ let fail23 = 3__16___23;
+
+ let fail24 = 0xAB_ABC_AB;
+ let fail25 = 0b01_100_101;
+ let ok26 = 0x6_A0_BF;
+ let ok27 = 0b1_0010_0101;
+}
--- /dev/null
- --> $DIR/literals.rs:14:17
++error: integer type suffix should not be separated by an underscore
++ --> $DIR/literals.rs:12:15
++ |
++LL | let ok4 = 0xab_cd_i32;
++ | ^^^^^^^^^^^ help: remove the underscore: `0xab_cdi32`
++ |
++ = note: `-D clippy::separated-literal-suffix` implied by `-D warnings`
++
++error: integer type suffix should not be separated by an underscore
++ --> $DIR/literals.rs:13:15
++ |
++LL | let ok5 = 0xAB_CD_u32;
++ | ^^^^^^^^^^^ help: remove the underscore: `0xAB_CDu32`
++
++error: integer type suffix should not be separated by an underscore
++ --> $DIR/literals.rs:14:15
++ |
++LL | let ok5 = 0xAB_CD_isize;
++ | ^^^^^^^^^^^^^ help: remove the underscore: `0xAB_CDisize`
++
+error: inconsistent casing in hexadecimal literal
- --> $DIR/literals.rs:15:17
++ --> $DIR/literals.rs:15:17
+ |
+LL | let fail1 = 0xabCD;
+ | ^^^^^^
+ |
+ = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings`
+
++error: integer type suffix should not be separated by an underscore
++ --> $DIR/literals.rs:16:17
++ |
++LL | let fail2 = 0xabCD_u32;
++ | ^^^^^^^^^^ help: remove the underscore: `0xabCDu32`
++
+error: inconsistent casing in hexadecimal literal
- --> $DIR/literals.rs:16:17
++ --> $DIR/literals.rs:16:17
+ |
+LL | let fail2 = 0xabCD_u32;
+ | ^^^^^^^^^^
+
++error: integer type suffix should not be separated by an underscore
++ --> $DIR/literals.rs:17:17
++ |
++LL | let fail2 = 0xabCD_isize;
++ | ^^^^^^^^^^^^ help: remove the underscore: `0xabCDisize`
++
+error: inconsistent casing in hexadecimal literal
- --> $DIR/literals.rs:17:27
++ --> $DIR/literals.rs:17:17
+ |
+LL | let fail2 = 0xabCD_isize;
+ | ^^^^^^^^^^^^
+
++error: integer type suffix should be separated by an underscore
++ --> $DIR/literals.rs:18:27
++ |
++LL | let fail_multi_zero = 000_123usize;
++ | ^^^^^^^^^^^^ help: add an underscore: `000_123_usize`
++ |
++ = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings`
++
+error: this is a decimal constant
- --> $DIR/literals.rs:21:17
++ --> $DIR/literals.rs:18:27
+ |
+LL | let fail_multi_zero = 000_123usize;
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings`
+help: if you mean to use a decimal constant, remove the `0` to avoid confusion
+ |
+LL | let fail_multi_zero = 123usize;
+ | ~~~~~~~~
+help: if you mean to use an octal constant, use `0o`
+ |
+LL | let fail_multi_zero = 0o123usize;
+ | ~~~~~~~~~~
+
++error: integer type suffix should not be separated by an underscore
++ --> $DIR/literals.rs:21:16
++ |
++LL | let ok10 = 0_i64;
++ | ^^^^^ help: remove the underscore: `0i64`
++
+error: this is a decimal constant
- --> $DIR/literals.rs:33:18
++ --> $DIR/literals.rs:22:17
+ |
+LL | let fail8 = 0123;
+ | ^^^^
+ |
+help: if you mean to use a decimal constant, remove the `0` to avoid confusion
+ |
+LL | let fail8 = 123;
+ | ~~~
+help: if you mean to use an octal constant, use `0o`
+ |
+LL | let fail8 = 0o123;
+ | ~~~~~
+
++error: integer type suffix should not be separated by an underscore
++ --> $DIR/literals.rs:31:16
++ |
++LL | let ok17 = 0x123_4567_8901_usize;
++ | ^^^^^^^^^^^^^^^^^^^^^ help: remove the underscore: `0x123_4567_8901usize`
++
+error: digits grouped inconsistently by underscores
- --> $DIR/literals.rs:34:18
++ --> $DIR/literals.rs:34:18
+ |
+LL | let fail19 = 12_3456_21;
+ | ^^^^^^^^^^ help: consider: `12_345_621`
+ |
+ = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
+
+error: digits grouped inconsistently by underscores
- --> $DIR/literals.rs:35:18
++ --> $DIR/literals.rs:35:18
+ |
+LL | let fail22 = 3__4___23;
+ | ^^^^^^^^^ help: consider: `3_423`
+
+error: digits grouped inconsistently by underscores
- --> $DIR/literals.rs:37:18
++ --> $DIR/literals.rs:36:18
+ |
+LL | let fail23 = 3__16___23;
+ | ^^^^^^^^^^ help: consider: `31_623`
+
+error: digits of hex or binary literal not grouped by four
- --> $DIR/literals.rs:38:18
++ --> $DIR/literals.rs:38:18
+ |
+LL | let fail24 = 0xAB_ABC_AB;
+ | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: digits of hex or binary literal not grouped by four
- error: aborting due to 10 previous errors
++ --> $DIR/literals.rs:39:18
+ |
+LL | let fail25 = 0b01_100_101;
+ | ^^^^^^^^^^^^ help: consider: `0b0110_0101`
+
++error: aborting due to 18 previous errors
+
--- /dev/null
- // compile-flags: --edition 2018
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// aux-build:proc_macro_derive.rs
+// run-rustfix
+// ignore-32bit
+
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate proc_macro_derive as mini_mac;
+
+mod a {
+ use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};
+ use mac;
+ use mini_mac::ClippyMiniMacroTest;
+ use mini_mac;
+ use mac::{inner::foofoo, inner::try_err};
+ use mac::inner;
+ use mac::inner::nested::string_add;
+ use mac::inner::nested;
+
+ #[derive(ClippyMiniMacroTest)]
+ struct Test;
+
+ fn test() {
+ pub_macro!();
+ inner_mod_macro!();
+ pub_in_private_macro!(_var);
+ function_macro!();
+ let v: ty_macro!() = Vec::default();
+
+ inner::try_err!();
+ inner::foofoo!();
+ nested::string_add!();
+ }
+}
+
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
+fn main() {}
--- /dev/null
- // compile-flags: --edition 2018
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// aux-build:proc_macro_derive.rs
+// run-rustfix
+// ignore-32bit
+
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate proc_macro_derive as mini_mac;
+
+mod a {
+ #[macro_use]
+ use mac;
+ #[macro_use]
+ use mini_mac;
+ #[macro_use]
+ use mac::inner;
+ #[macro_use]
+ use mac::inner::nested;
+
+ #[derive(ClippyMiniMacroTest)]
+ struct Test;
+
+ fn test() {
+ pub_macro!();
+ inner_mod_macro!();
+ pub_in_private_macro!(_var);
+ function_macro!();
+ let v: ty_macro!() = Vec::default();
+
+ inner::try_err!();
+ inner::foofoo!();
+ nested::string_add!();
+ }
+}
+
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
+fn main() {}
--- /dev/null
- --> $DIR/macro_use_imports.rs:19:5
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:25:5
++ --> $DIR/macro_use_imports.rs:18:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};`
+ |
+ = note: `-D clippy::macro-use-imports` implied by `-D warnings`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
++ --> $DIR/macro_use_imports.rs:20:5
+ |
+LL | #[macro_use]
- --> $DIR/macro_use_imports.rs:21:5
++ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
++ --> $DIR/macro_use_imports.rs:22:5
+ |
+LL | #[macro_use]
- --> $DIR/macro_use_imports.rs:23:5
++ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
++ --> $DIR/macro_use_imports.rs:24:5
+ |
+LL | #[macro_use]
++ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+
+error: aborting due to 4 previous errors
+
--- /dev/null
--- /dev/null
++// revisions: edition2018 edition2021
++// [edition2018] edition:2018
++// [edition2021] edition:2021
++// run-rustfix
++#![warn(clippy::manual_assert)]
++
++fn main() {
++ let a = vec![1, 2, 3];
++ let c = Some(2);
++ if !a.is_empty()
++ && a.len() == 3
++ && c != None
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ {
++ panic!("qaqaq{:?}", a);
++ }
++ assert!(a.is_empty(), "qaqaq{:?}", a);
++ assert!(a.is_empty(), "qwqwq");
++ if a.len() == 3 {
++ println!("qwq");
++ println!("qwq");
++ println!("qwq");
++ }
++ if let Some(b) = c {
++ panic!("orz {}", b);
++ }
++ if a.len() == 3 {
++ panic!("qaqaq");
++ } else {
++ println!("qwq");
++ }
++ let b = vec![1, 2, 3];
++ assert!(!b.is_empty(), "panic1");
++ assert!(!(b.is_empty() && a.is_empty()), "panic2");
++ assert!(!(a.is_empty() && !b.is_empty()), "panic3");
++ assert!(!(b.is_empty() || a.is_empty()), "panic4");
++ assert!(!(a.is_empty() || !b.is_empty()), "panic5");
++}
--- /dev/null
--- /dev/null
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:22:5
++ |
++LL | / if !a.is_empty() {
++LL | | panic!("qaqaq{:?}", a);
++LL | | }
++ | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
++ |
++ = note: `-D clippy::manual-assert` implied by `-D warnings`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:25:5
++ |
++LL | / if !a.is_empty() {
++LL | | panic!("qwqwq");
++LL | | }
++ | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:42:5
++ |
++LL | / if b.is_empty() {
++LL | | panic!("panic1");
++LL | | }
++ | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:45:5
++ |
++LL | / if b.is_empty() && a.is_empty() {
++LL | | panic!("panic2");
++LL | | }
++ | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:48:5
++ |
++LL | / if a.is_empty() && !b.is_empty() {
++LL | | panic!("panic3");
++LL | | }
++ | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:51:5
++ |
++LL | / if b.is_empty() || a.is_empty() {
++LL | | panic!("panic4");
++LL | | }
++ | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:54:5
++ |
++LL | / if a.is_empty() || !b.is_empty() {
++LL | | panic!("panic5");
++LL | | }
++ | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// revisions: edition2018 edition2021
++// [edition2018] edition:2018
++// [edition2021] edition:2021
++// run-rustfix
++#![warn(clippy::manual_assert)]
++
++fn main() {
++ let a = vec![1, 2, 3];
++ let c = Some(2);
++ if !a.is_empty()
++ && a.len() == 3
++ && c != None
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ {
++ panic!("qaqaq{:?}", a);
++ }
++ assert!(a.is_empty(), "qaqaq{:?}", a);
++ assert!(a.is_empty(), "qwqwq");
++ if a.len() == 3 {
++ println!("qwq");
++ println!("qwq");
++ println!("qwq");
++ }
++ if let Some(b) = c {
++ panic!("orz {}", b);
++ }
++ if a.len() == 3 {
++ panic!("qaqaq");
++ } else {
++ println!("qwq");
++ }
++ let b = vec![1, 2, 3];
++ assert!(!b.is_empty(), "panic1");
++ assert!(!(b.is_empty() && a.is_empty()), "panic2");
++ assert!(!(a.is_empty() && !b.is_empty()), "panic3");
++ assert!(!(b.is_empty() || a.is_empty()), "panic4");
++ assert!(!(a.is_empty() || !b.is_empty()), "panic5");
++}
--- /dev/null
--- /dev/null
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:22:5
++ |
++LL | / if !a.is_empty() {
++LL | | panic!("qaqaq{:?}", a);
++LL | | }
++ | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
++ |
++ = note: `-D clippy::manual-assert` implied by `-D warnings`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:25:5
++ |
++LL | / if !a.is_empty() {
++LL | | panic!("qwqwq");
++LL | | }
++ | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:42:5
++ |
++LL | / if b.is_empty() {
++LL | | panic!("panic1");
++LL | | }
++ | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:45:5
++ |
++LL | / if b.is_empty() && a.is_empty() {
++LL | | panic!("panic2");
++LL | | }
++ | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:48:5
++ |
++LL | / if a.is_empty() && !b.is_empty() {
++LL | | panic!("panic3");
++LL | | }
++ | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:51:5
++ |
++LL | / if b.is_empty() || a.is_empty() {
++LL | | panic!("panic4");
++LL | | }
++ | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
++
++error: only a `panic!` in `if`-then statement
++ --> $DIR/manual_assert.rs:54:5
++ |
++LL | / if a.is_empty() || !b.is_empty() {
++LL | | panic!("panic5");
++LL | | }
++ | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// revisions: edition2018 edition2021
++// [edition2018] edition:2018
++// [edition2021] edition:2021
++// run-rustfix
++#![warn(clippy::manual_assert)]
++
++fn main() {
++ let a = vec![1, 2, 3];
++ let c = Some(2);
++ if !a.is_empty()
++ && a.len() == 3
++ && c != None
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ {
++ panic!("qaqaq{:?}", a);
++ }
++ assert!(a.is_empty(), "qaqaq{:?}", a);
++ assert!(a.is_empty(), "qwqwq");
++ if a.len() == 3 {
++ println!("qwq");
++ println!("qwq");
++ println!("qwq");
++ }
++ if let Some(b) = c {
++ panic!("orz {}", b);
++ }
++ if a.len() == 3 {
++ panic!("qaqaq");
++ } else {
++ println!("qwq");
++ }
++ let b = vec![1, 2, 3];
++ assert!(!b.is_empty(), "panic1");
++ assert!(!(b.is_empty() && a.is_empty()), "panic2");
++ assert!(!(a.is_empty() && !b.is_empty()), "panic3");
++ assert!(!(b.is_empty() || a.is_empty()), "panic4");
++ assert!(!(a.is_empty() || !b.is_empty()), "panic5");
++}
--- /dev/null
--- /dev/null
++// revisions: edition2018 edition2021
++// [edition2018] edition:2018
++// [edition2021] edition:2021
++// run-rustfix
++#![warn(clippy::manual_assert)]
++
++fn main() {
++ let a = vec![1, 2, 3];
++ let c = Some(2);
++ if !a.is_empty()
++ && a.len() == 3
++ && c != None
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ && !a.is_empty()
++ && a.len() == 3
++ {
++ panic!("qaqaq{:?}", a);
++ }
++ if !a.is_empty() {
++ panic!("qaqaq{:?}", a);
++ }
++ if !a.is_empty() {
++ panic!("qwqwq");
++ }
++ if a.len() == 3 {
++ println!("qwq");
++ println!("qwq");
++ println!("qwq");
++ }
++ if let Some(b) = c {
++ panic!("orz {}", b);
++ }
++ if a.len() == 3 {
++ panic!("qaqaq");
++ } else {
++ println!("qwq");
++ }
++ let b = vec![1, 2, 3];
++ if b.is_empty() {
++ panic!("panic1");
++ }
++ if b.is_empty() && a.is_empty() {
++ panic!("panic2");
++ }
++ if a.is_empty() && !b.is_empty() {
++ panic!("panic3");
++ }
++ if b.is_empty() || a.is_empty() {
++ panic!("panic4");
++ }
++ if a.is_empty() || !b.is_empty() {
++ panic!("panic5");
++ }
++}
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::manual_async_fn)]
+#![allow(unused)]
+
+use std::future::Future;
+
+async fn fut() -> i32 { 42 }
+
+#[rustfmt::skip]
+async fn fut2() -> i32 { 42 }
+
+#[rustfmt::skip]
+async fn fut3() -> i32 { 42 }
+
+async fn empty_fut() {}
+
+#[rustfmt::skip]
+async fn empty_fut2() {}
+
+#[rustfmt::skip]
+async fn empty_fut3() {}
+
+async fn core_fut() -> i32 { 42 }
+
+// should be ignored
+fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+}
+
+// should be ignored
+fn not_fut() -> i32 {
+ 42
+}
+
+// should be ignored
+async fn already_async() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+struct S {}
+impl S {
+ async fn inh_fut() -> i32 {
+ // NOTE: this code is here just to check that the indentation is correct in the suggested fix
+ let a = 42;
+ let b = 21;
+ if a < b {
+ let c = 21;
+ let d = 42;
+ if c < d {
+ let _ = 42;
+ }
+ }
+ 42
+ }
+
+ // should be ignored
+ fn not_fut(&self) -> i32 {
+ 42
+ }
+
+ // should be ignored
+ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+ }
+
+ // should be ignored
+ async fn already_async(&self) -> impl Future<Output = i32> {
+ async { 42 }
+ }
+}
+
+// Tests related to lifetime capture
+
+async fn elided(_: &i32) -> i32 { 42 }
+
+// should be ignored
+fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 }
+
+// should be ignored
+#[allow(clippy::needless_lifetimes)]
+fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+// should be ignored
+mod issue_5765 {
+ use std::future::Future;
+
+ struct A;
+ impl A {
+ fn f(&self) -> impl Future<Output = ()> {
+ async {}
+ }
+ }
+
+ fn test() {
+ let _future = {
+ let a = A;
+ a.f()
+ };
+ }
+}
+
+fn main() {}
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::manual_async_fn)]
+#![allow(unused)]
+
+use std::future::Future;
+
+fn fut() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+#[rustfmt::skip]
+fn fut2() ->impl Future<Output = i32> {
+ async { 42 }
+}
+
+#[rustfmt::skip]
+fn fut3()-> impl Future<Output = i32> {
+ async { 42 }
+}
+
+fn empty_fut() -> impl Future<Output = ()> {
+ async {}
+}
+
+#[rustfmt::skip]
+fn empty_fut2() ->impl Future<Output = ()> {
+ async {}
+}
+
+#[rustfmt::skip]
+fn empty_fut3()-> impl Future<Output = ()> {
+ async {}
+}
+
+fn core_fut() -> impl core::future::Future<Output = i32> {
+ async move { 42 }
+}
+
+// should be ignored
+fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+}
+
+// should be ignored
+fn not_fut() -> i32 {
+ 42
+}
+
+// should be ignored
+async fn already_async() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+struct S {}
+impl S {
+ fn inh_fut() -> impl Future<Output = i32> {
+ async {
+ // NOTE: this code is here just to check that the indentation is correct in the suggested fix
+ let a = 42;
+ let b = 21;
+ if a < b {
+ let c = 21;
+ let d = 42;
+ if c < d {
+ let _ = 42;
+ }
+ }
+ 42
+ }
+ }
+
+ // should be ignored
+ fn not_fut(&self) -> i32 {
+ 42
+ }
+
+ // should be ignored
+ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+ }
+
+ // should be ignored
+ async fn already_async(&self) -> impl Future<Output = i32> {
+ async { 42 }
+ }
+}
+
+// Tests related to lifetime capture
+
+fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
+ async { 42 }
+}
+
+// should be ignored
+fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
+ async { 42 }
+}
+
+// should be ignored
+#[allow(clippy::needless_lifetimes)]
+fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+// should be ignored
+mod issue_5765 {
+ use std::future::Future;
+
+ struct A;
+ impl A {
+ fn f(&self) -> impl Future<Output = ()> {
+ async {}
+ }
+ }
+
+ fn test() {
+ let _future = {
+ let a = A;
+ a.f()
+ };
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/manual_async_fn.rs:8:1
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:13:1
++ --> $DIR/manual_async_fn.rs:7:1
+ |
+LL | fn fut() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::manual-async-fn` implied by `-D warnings`
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn fut() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn fut() -> impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:18:1
++ --> $DIR/manual_async_fn.rs:12:1
+ |
+LL | fn fut2() ->impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn fut2() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn fut2() ->impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:22:1
++ --> $DIR/manual_async_fn.rs:17:1
+ |
+LL | fn fut3()-> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn fut3() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn fut3()-> impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:27:1
++ --> $DIR/manual_async_fn.rs:21:1
+ |
+LL | fn empty_fut() -> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut() {
+ | ~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut() -> impl Future<Output = ()> {}
+ | ~~
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:32:1
++ --> $DIR/manual_async_fn.rs:26:1
+ |
+LL | fn empty_fut2() ->impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut2() {
+ | ~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut2() ->impl Future<Output = ()> {}
+ | ~~
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:36:1
++ --> $DIR/manual_async_fn.rs:31:1
+ |
+LL | fn empty_fut3()-> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut3() {
+ | ~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut3()-> impl Future<Output = ()> {}
+ | ~~
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:58:5
++ --> $DIR/manual_async_fn.rs:35:1
+ |
+LL | fn core_fut() -> impl core::future::Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn core_fut() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn core_fut() -> impl core::future::Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:93:1
++ --> $DIR/manual_async_fn.rs:57:5
+ |
+LL | fn inh_fut() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn inh_fut() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL ~ fn inh_fut() -> impl Future<Output = i32> {
+LL + // NOTE: this code is here just to check that the indentation is correct in the suggested fix
+LL + let a = 42;
+LL + let b = 21;
+LL + if a < b {
+LL + let c = 21;
+ ...
+
+error: this function can be simplified using the `async fn` syntax
- --> $DIR/manual_async_fn.rs:102:1
++ --> $DIR/manual_async_fn.rs:92:1
+ |
+LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn elided(_: &i32) -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
++ --> $DIR/manual_async_fn.rs:101:1
+ |
+LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { 42 }
+ | ~~~~~~
+
+error: aborting due to 10 previous errors
+
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(
+ clippy::no_effect,
+ clippy::map_identity,
+ clippy::unit_arg,
+ clippy::match_ref_pats,
+ clippy::redundant_pattern_matching,
+ dead_code
+)]
+
+fn main() {
+ Some(0).map(|_| 2);
+
+ Some(0).map(|x| x + 1);
+
+ Some("").map(|x| x.is_empty());
+
+ Some(0).map(|x| !x);
+
+ #[rustfmt::skip]
+ Some(0).map(std::convert::identity);
+
+ Some(&String::new()).map(|x| str::len(x));
+
+ match Some(0) {
+ Some(x) if false => Some(x + 1),
+ _ => None,
+ };
+
+ Some([0, 1]).as_ref().map(|x| x[0]);
+
+ Some(0).map(|x| x * 2);
+
+ Some(String::new()).as_ref().map(|x| x.is_empty());
+
+ Some(String::new()).as_ref().map(|x| x.len());
+
+ Some(0).map(|x| x + x);
+
+ #[warn(clippy::option_map_unit_fn)]
+ match &mut Some(String::new()) {
+ Some(x) => Some(x.push_str("")),
+ None => None,
+ };
+
+ #[allow(clippy::option_map_unit_fn)]
+ {
+ Some(String::new()).as_mut().map(|x| x.push_str(""));
+ }
+
+ Some(String::new()).as_ref().map(|x| x.len());
+
+ Some(String::new()).as_ref().map(|x| x.is_empty());
+
+ Some((0, 1, 2)).map(|(x, y, z)| x + y + z);
+
+ Some([1, 2, 3]).map(|[first, ..]| first);
+
+ Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x));
+
+ match Some((String::new(), 0)) {
+ Some((ref x, y)) => Some((y, x)),
+ None => None,
+ };
+
+ match Some(Some(0)) {
+ Some(Some(_)) | Some(None) => Some(0),
+ None => None,
+ };
+
+ match Some(Some((0, 1))) {
+ Some(Some((x, 1))) => Some(x),
+ _ => None,
+ };
+
+ // #6795
+ fn f1() -> Result<(), ()> {
+ let _ = match Some(Ok(())) {
+ Some(x) => Some(x?),
+ None => None,
+ };
+ Ok(())
+ }
+
+ for &x in Some(Some(true)).iter() {
+ let _ = match x {
+ Some(x) => Some(if x { continue } else { x }),
+ None => None,
+ };
+ }
+
+ // #6797
+ let x1 = (Some(String::new()), 0);
+ let x2 = x1.0;
+ match x2 {
+ Some(x) => Some((x, x1.1)),
+ None => None,
+ };
+
+ struct S1 {
+ x: Option<String>,
+ y: u32,
+ }
+ impl S1 {
+ fn f(self) -> Option<(String, u32)> {
+ match self.x {
+ Some(x) => Some((x, self.y)),
+ None => None,
+ }
+ }
+ }
+
+ // #6811
+ Some(0).map(|x| vec![x]);
+
+ option_env!("").map(String::from);
+
+ // #6819
+ async fn f2(x: u32) -> u32 {
+ x
+ }
+
+ async fn f3() {
+ match Some(0) {
+ Some(x) => Some(f2(x).await),
+ None => None,
+ };
+ }
+
+ // #6847
+ if let Some(_) = Some(0) {
+ Some(0)
+ } else { Some(0).map(|x| x + 1) };
+
+ if true {
+ Some(0)
+ } else { Some(0).map(|x| x + 1) };
+
+ // #6967
+ const fn f4() {
+ match Some(0) {
+ Some(x) => Some(x + 1),
+ None => None,
+ };
+ }
+
+ // #7077
+ let s = &String::new();
+ let _: Option<&str> = match Some(s) {
+ Some(s) => Some(s),
+ None => None,
+ };
+}
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(
+ clippy::no_effect,
+ clippy::map_identity,
+ clippy::unit_arg,
+ clippy::match_ref_pats,
+ clippy::redundant_pattern_matching,
+ dead_code
+)]
+
+fn main() {
+ match Some(0) {
+ Some(_) => Some(2),
+ None::<u32> => None,
+ };
+
+ match Some(0) {
+ Some(x) => Some(x + 1),
+ _ => None,
+ };
+
+ match Some("") {
+ Some(x) => Some(x.is_empty()),
+ None => None,
+ };
+
+ if let Some(x) = Some(0) {
+ Some(!x)
+ } else {
+ None
+ };
+
+ #[rustfmt::skip]
+ match Some(0) {
+ Some(x) => { Some(std::convert::identity(x)) }
+ None => { None }
+ };
+
+ match Some(&String::new()) {
+ Some(x) => Some(str::len(x)),
+ None => None,
+ };
+
+ match Some(0) {
+ Some(x) if false => Some(x + 1),
+ _ => None,
+ };
+
+ match &Some([0, 1]) {
+ Some(x) => Some(x[0]),
+ &None => None,
+ };
+
+ match &Some(0) {
+ &Some(x) => Some(x * 2),
+ None => None,
+ };
+
+ match Some(String::new()) {
+ Some(ref x) => Some(x.is_empty()),
+ _ => None,
+ };
+
+ match &&Some(String::new()) {
+ Some(x) => Some(x.len()),
+ _ => None,
+ };
+
+ match &&Some(0) {
+ &&Some(x) => Some(x + x),
+ &&_ => None,
+ };
+
+ #[warn(clippy::option_map_unit_fn)]
+ match &mut Some(String::new()) {
+ Some(x) => Some(x.push_str("")),
+ None => None,
+ };
+
+ #[allow(clippy::option_map_unit_fn)]
+ {
+ match &mut Some(String::new()) {
+ Some(x) => Some(x.push_str("")),
+ None => None,
+ };
+ }
+
+ match &mut Some(String::new()) {
+ Some(ref x) => Some(x.len()),
+ None => None,
+ };
+
+ match &mut &Some(String::new()) {
+ Some(x) => Some(x.is_empty()),
+ &mut _ => None,
+ };
+
+ match Some((0, 1, 2)) {
+ Some((x, y, z)) => Some(x + y + z),
+ None => None,
+ };
+
+ match Some([1, 2, 3]) {
+ Some([first, ..]) => Some(first),
+ None => None,
+ };
+
+ match &Some((String::new(), "test")) {
+ Some((x, y)) => Some((y, x)),
+ None => None,
+ };
+
+ match Some((String::new(), 0)) {
+ Some((ref x, y)) => Some((y, x)),
+ None => None,
+ };
+
+ match Some(Some(0)) {
+ Some(Some(_)) | Some(None) => Some(0),
+ None => None,
+ };
+
+ match Some(Some((0, 1))) {
+ Some(Some((x, 1))) => Some(x),
+ _ => None,
+ };
+
+ // #6795
+ fn f1() -> Result<(), ()> {
+ let _ = match Some(Ok(())) {
+ Some(x) => Some(x?),
+ None => None,
+ };
+ Ok(())
+ }
+
+ for &x in Some(Some(true)).iter() {
+ let _ = match x {
+ Some(x) => Some(if x { continue } else { x }),
+ None => None,
+ };
+ }
+
+ // #6797
+ let x1 = (Some(String::new()), 0);
+ let x2 = x1.0;
+ match x2 {
+ Some(x) => Some((x, x1.1)),
+ None => None,
+ };
+
+ struct S1 {
+ x: Option<String>,
+ y: u32,
+ }
+ impl S1 {
+ fn f(self) -> Option<(String, u32)> {
+ match self.x {
+ Some(x) => Some((x, self.y)),
+ None => None,
+ }
+ }
+ }
+
+ // #6811
+ match Some(0) {
+ Some(x) => Some(vec![x]),
+ None => None,
+ };
+
+ match option_env!("") {
+ Some(x) => Some(String::from(x)),
+ None => None,
+ };
+
+ // #6819
+ async fn f2(x: u32) -> u32 {
+ x
+ }
+
+ async fn f3() {
+ match Some(0) {
+ Some(x) => Some(f2(x).await),
+ None => None,
+ };
+ }
+
+ // #6847
+ if let Some(_) = Some(0) {
+ Some(0)
+ } else if let Some(x) = Some(0) {
+ Some(x + 1)
+ } else {
+ None
+ };
+
+ if true {
+ Some(0)
+ } else if let Some(x) = Some(0) {
+ Some(x + 1)
+ } else {
+ None
+ };
+
+ // #6967
+ const fn f4() {
+ match Some(0) {
+ Some(x) => Some(x + 1),
+ None => None,
+ };
+ }
+
+ // #7077
+ let s = &String::new();
+ let _: Option<&str> = match Some(s) {
+ Some(s) => Some(s),
+ None => None,
+ };
+}
--- /dev/null
- --> $DIR/manual_map_option.rs:15:5
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:20:5
++ --> $DIR/manual_map_option.rs:14:5
+ |
+LL | / match Some(0) {
+LL | | Some(_) => Some(2),
+LL | | None::<u32> => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|_| 2)`
+ |
+ = note: `-D clippy::manual-map` implied by `-D warnings`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:25:5
++ --> $DIR/manual_map_option.rs:19:5
+ |
+LL | / match Some(0) {
+LL | | Some(x) => Some(x + 1),
+LL | | _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| x + 1)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:30:5
++ --> $DIR/manual_map_option.rs:24:5
+ |
+LL | / match Some("") {
+LL | | Some(x) => Some(x.is_empty()),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some("").map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:37:5
++ --> $DIR/manual_map_option.rs:29:5
+ |
+LL | / if let Some(x) = Some(0) {
+LL | | Some(!x)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| !x)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:42:5
++ --> $DIR/manual_map_option.rs:36:5
+ |
+LL | / match Some(0) {
+LL | | Some(x) => { Some(std::convert::identity(x)) }
+LL | | None => { None }
+LL | | };
+ | |_____^ help: try this: `Some(0).map(std::convert::identity)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:52:5
++ --> $DIR/manual_map_option.rs:41:5
+ |
+LL | / match Some(&String::new()) {
+LL | | Some(x) => Some(str::len(x)),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:57:5
++ --> $DIR/manual_map_option.rs:51:5
+ |
+LL | / match &Some([0, 1]) {
+LL | | Some(x) => Some(x[0]),
+LL | | &None => None,
+LL | | };
+ | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:62:5
++ --> $DIR/manual_map_option.rs:56:5
+ |
+LL | / match &Some(0) {
+LL | | &Some(x) => Some(x * 2),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| x * 2)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:67:5
++ --> $DIR/manual_map_option.rs:61:5
+ |
+LL | / match Some(String::new()) {
+LL | | Some(ref x) => Some(x.is_empty()),
+LL | | _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:72:5
++ --> $DIR/manual_map_option.rs:66:5
+ |
+LL | / match &&Some(String::new()) {
+LL | | Some(x) => Some(x.len()),
+LL | | _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:85:9
++ --> $DIR/manual_map_option.rs:71:5
+ |
+LL | / match &&Some(0) {
+LL | | &&Some(x) => Some(x + x),
+LL | | &&_ => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| x + x)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:91:5
++ --> $DIR/manual_map_option.rs:84:9
+ |
+LL | / match &mut Some(String::new()) {
+LL | | Some(x) => Some(x.push_str("")),
+LL | | None => None,
+LL | | };
+ | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:96:5
++ --> $DIR/manual_map_option.rs:90:5
+ |
+LL | / match &mut Some(String::new()) {
+LL | | Some(ref x) => Some(x.len()),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:101:5
++ --> $DIR/manual_map_option.rs:95:5
+ |
+LL | / match &mut &Some(String::new()) {
+LL | | Some(x) => Some(x.is_empty()),
+LL | | &mut _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:106:5
++ --> $DIR/manual_map_option.rs:100:5
+ |
+LL | / match Some((0, 1, 2)) {
+LL | | Some((x, y, z)) => Some(x + y + z),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:111:5
++ --> $DIR/manual_map_option.rs:105:5
+ |
+LL | / match Some([1, 2, 3]) {
+LL | | Some([first, ..]) => Some(first),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:169:5
++ --> $DIR/manual_map_option.rs:110:5
+ |
+LL | / match &Some((String::new(), "test")) {
+LL | | Some((x, y)) => Some((y, x)),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:174:5
++ --> $DIR/manual_map_option.rs:168:5
+ |
+LL | / match Some(0) {
+LL | | Some(x) => Some(vec![x]),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| vec![x])`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:194:12
++ --> $DIR/manual_map_option.rs:173:5
+ |
+LL | / match option_env!("") {
+LL | | Some(x) => Some(String::from(x)),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `option_env!("").map(String::from)`
+
+error: manual implementation of `Option::map`
- --> $DIR/manual_map_option.rs:202:12
++ --> $DIR/manual_map_option.rs:193:12
+ |
+LL | } else if let Some(x) = Some(0) {
+ | ____________^
+LL | | Some(x + 1)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }`
+
+error: manual implementation of `Option::map`
++ --> $DIR/manual_map_option.rs:201:12
+ |
+LL | } else if let Some(x) = Some(0) {
+ | ____________^
+LL | | Some(x + 1)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }`
+
+error: aborting due to 21 previous errors
+
--- /dev/null
+#![feature(exclusive_range_pattern)]
+#![feature(half_open_range_patterns)]
+#![warn(clippy::match_overlapping_arm)]
+#![allow(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+
+/// Tests for match_overlapping_arm
+
+fn overlapping() {
+ const FOO: u64 = 2;
+
+ match 42 {
+ 0..=10 => println!("0..=10"),
+ 0..=11 => println!("0..=11"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..=5 => println!("0..=5"),
+ 6..=7 => println!("6..=7"),
+ FOO..=11 => println!("FOO..=11"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..=5 => println!("0..=5"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..=2 => println!("0..=2"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..=10 => println!("0..=10"),
+ 11..=50 => println!("11..=50"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..2 => println!("0..2"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..10 => println!("0..10"),
+ 10..50 => println!("10..50"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..11 => println!("0..11"),
+ 0..=11 => println!("0..=11"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..7 => println!("5..7"),
+ 0..10 => println!("0..10"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..10 => println!("5..10"),
+ 0..=10 => println!("0..=10"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..14 => println!("0..14"),
+ 5..10 => println!("5..10"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..14 => println!("5..14"),
+ 0..=10 => println!("0..=10"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..7 => println!("0..7"),
+ 0..=10 => println!("0..=10"),
+ _ => (),
+ }
+
+ match 42 {
+ 3.. => println!("3.."),
+ 0.. => println!("0.."),
+ _ => (),
+ }
+
+ match 42 {
+ ..=23 => println!("..=23"),
+ ..26 => println!("..26"),
+ _ => (),
+ }
+
++ // Issue #7829
++ match 0 {
++ -1..=1 => (),
++ -2..=2 => (),
++ _ => (),
++ }
++
+ if let None = Some(42) {
+ // nothing
+ } else if let None = Some(42) {
+ // another nothing :-)
+ }
+}
+
+fn main() {}
--- /dev/null
- #![allow(clippy::equatable_if_let)]
+#![warn(clippy::match_ref_pats)]
++#![allow(clippy::equatable_if_let, clippy::enum_variant_names)]
+
+fn ref_pats() {
+ {
+ let v = &Some(0);
+ match v {
+ &Some(v) => println!("{:?}", v),
+ &None => println!("none"),
+ }
+ match v {
+ // This doesn't trigger; we have a different pattern.
+ &Some(v) => println!("some"),
+ other => println!("other"),
+ }
+ }
+ let tup = &(1, 2);
+ match tup {
+ &(v, 1) => println!("{}", v),
+ _ => println!("none"),
+ }
+ // Special case: using `&` both in expr and pats.
+ let w = Some(0);
+ match &w {
+ &Some(v) => println!("{:?}", v),
+ &None => println!("none"),
+ }
+ // False positive: only wildcard pattern.
+ let w = Some(0);
+ #[allow(clippy::match_single_binding)]
+ match w {
+ _ => println!("none"),
+ }
+
+ let a = &Some(0);
+ if let &None = a {
+ println!("none");
+ }
+
+ let b = Some(0);
+ if let &None = &b {
+ println!("none");
+ }
+}
+
+mod ice_3719 {
+ macro_rules! foo_variant(
+ ($idx:expr) => (Foo::get($idx).unwrap())
+ );
+
+ enum Foo {
+ A,
+ B,
+ }
+
+ impl Foo {
+ fn get(idx: u8) -> Option<&'static Self> {
+ match idx {
+ 0 => Some(&Foo::A),
+ 1 => Some(&Foo::B),
+ _ => None,
+ }
+ }
+ }
+
+ fn ice_3719() {
+ // ICE #3719
+ match foo_variant!(0) {
+ &Foo::A => println!("A"),
+ _ => println!("Wild"),
+ }
+ }
+}
+
+mod issue_7740 {
+ macro_rules! foobar_variant(
+ ($idx:expr) => (FooBar::get($idx).unwrap())
+ );
+
+ enum FooBar {
+ Foo,
+ Bar,
+ FooBar,
+ BarFoo,
+ }
+
+ impl FooBar {
+ fn get(idx: u8) -> Option<&'static Self> {
+ match idx {
+ 0 => Some(&FooBar::Foo),
+ 1 => Some(&FooBar::Bar),
+ 2 => Some(&FooBar::FooBar),
+ 3 => Some(&FooBar::BarFoo),
+ _ => None,
+ }
+ }
+ }
+
+ fn issue_7740() {
+ // Issue #7740
+ match foobar_variant!(0) {
+ &FooBar::Foo => println!("Foo"),
+ &FooBar::Bar => println!("Bar"),
+ &FooBar::FooBar => println!("FooBar"),
+ _ => println!("Wild"),
+ }
+
+ // This shouldn't trigger
+ if let &FooBar::BarFoo = foobar_variant!(3) {
+ println!("BarFoo");
+ } else {
+ println!("Wild");
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
+#![warn(clippy::match_str_case_mismatch)]
+
+// Valid
+
+fn as_str_match() {
+ let var = "BAR";
+
+ match var.to_ascii_lowercase().as_str() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
++fn non_alphabetic() {
++ let var = "~!@#$%^&*()-_=+FOO";
++
++ match var.to_ascii_lowercase().as_str() {
++ "1234567890" => {},
++ "~!@#$%^&*()-_=+foo" => {},
++ "\n\r\t\x7F" => {},
++ _ => {},
++ }
++}
++
++fn unicode_cased() {
++ let var = "ВОДЫ";
++
++ match var.to_lowercase().as_str() {
++ "水" => {},
++ "νερό" => {},
++ "воды" => {},
++ "물" => {},
++ _ => {},
++ }
++}
++
++fn titlecase() {
++ let var = "BarDz";
++
++ match var.to_lowercase().as_str() {
++ "foolj" => {},
++ "bardz" => {},
++ _ => {},
++ }
++}
++
++fn no_case_equivalent() {
++ let var = "barʁ";
++
++ match var.to_uppercase().as_str() {
++ "FOOɕ" => {},
++ "BARʁ" => {},
++ _ => {},
++ }
++}
++
+fn addrof_unary_match() {
+ let var = "BAR";
+
+ match &*var.to_ascii_lowercase() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
+fn alternating_chain() {
+ let var = "BAR";
+
+ match &*var
+ .to_ascii_lowercase()
+ .to_uppercase()
+ .to_lowercase()
+ .to_ascii_uppercase()
+ {
+ "FOO" => {},
+ "BAR" => {},
+ _ => {},
+ }
+}
+
+fn unrelated_method() {
+ struct Item {
+ a: String,
+ }
+
+ impl Item {
+ #[allow(clippy::wrong_self_convention)]
+ fn to_lowercase(self) -> String {
+ self.a
+ }
+ }
+
+ let item = Item { a: String::from("BAR") };
+
+ match &*item.to_lowercase() {
+ "FOO" => {},
+ "BAR" => {},
+ _ => {},
+ }
+}
+
+// Invalid
+
+fn as_str_match_mismatch() {
+ let var = "BAR";
+
+ match var.to_ascii_lowercase().as_str() {
+ "foo" => {},
+ "Bar" => {},
+ _ => {},
+ }
+}
+
++fn non_alphabetic_mismatch() {
++ let var = "~!@#$%^&*()-_=+FOO";
++
++ match var.to_ascii_lowercase().as_str() {
++ "1234567890" => {},
++ "~!@#$%^&*()-_=+Foo" => {},
++ "\n\r\t\x7F" => {},
++ _ => {},
++ }
++}
++
++fn unicode_cased_mismatch() {
++ let var = "ВОДЫ";
++
++ match var.to_lowercase().as_str() {
++ "水" => {},
++ "νερό" => {},
++ "Воды" => {},
++ "물" => {},
++ _ => {},
++ }
++}
++
++fn titlecase_mismatch() {
++ let var = "BarDz";
++
++ match var.to_lowercase().as_str() {
++ "foolj" => {},
++ "barDz" => {},
++ _ => {},
++ }
++}
++
++fn no_case_equivalent_mismatch() {
++ let var = "barʁ";
++
++ match var.to_uppercase().as_str() {
++ "FOOɕ" => {},
++ "bARʁ" => {},
++ _ => {},
++ }
++}
++
+fn addrof_unary_match_mismatch() {
+ let var = "BAR";
+
+ match &*var.to_ascii_lowercase() {
+ "foo" => {},
+ "Bar" => {},
+ _ => {},
+ }
+}
+
+fn alternating_chain_mismatch() {
+ let var = "BAR";
+
+ match &*var
+ .to_ascii_lowercase()
+ .to_uppercase()
+ .to_lowercase()
+ .to_ascii_uppercase()
+ {
+ "FOO" => {},
+ "bAR" => {},
+ _ => {},
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/match_str_case_mismatch.rs:68:9
+error: this `match` arm has a differing case than its expression
- --> $DIR/match_str_case_mismatch.rs:78:9
++ --> $DIR/match_str_case_mismatch.rs:111:9
+ |
+LL | "Bar" => {},
+ | ^^^^^
+ |
+ = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings`
+help: consider changing the case of this arm to respect `to_ascii_lowercase`
+ |
+LL | "bar" => {},
+ | ~~~~~
+
+error: this `match` arm has a differing case than its expression
- --> $DIR/match_str_case_mismatch.rs:93:9
++ --> $DIR/match_str_case_mismatch.rs:121:9
++ |
++LL | "~!@#$%^&*()-_=+Foo" => {},
++ | ^^^^^^^^^^^^^^^^^^^^
++ |
++help: consider changing the case of this arm to respect `to_ascii_lowercase`
++ |
++LL | "~!@#$%^&*()-_=+foo" => {},
++ | ~~~~~~~~~~~~~~~~~~~~
++
++error: this `match` arm has a differing case than its expression
++ --> $DIR/match_str_case_mismatch.rs:133:9
++ |
++LL | "Воды" => {},
++ | ^^^^^^
++ |
++help: consider changing the case of this arm to respect `to_lowercase`
++ |
++LL | "воды" => {},
++ | ~~~~~~
++
++error: this `match` arm has a differing case than its expression
++ --> $DIR/match_str_case_mismatch.rs:144:9
++ |
++LL | "barDz" => {},
++ | ^^^^^^
++ |
++help: consider changing the case of this arm to respect `to_lowercase`
++ |
++LL | "bardz" => {},
++ | ~~~~~~
++
++error: this `match` arm has a differing case than its expression
++ --> $DIR/match_str_case_mismatch.rs:154:9
++ |
++LL | "bARʁ" => {},
++ | ^^^^^^
++ |
++help: consider changing the case of this arm to respect `to_uppercase`
++ |
++LL | "BARʁ" => {},
++ | ~~~~~~
++
++error: this `match` arm has a differing case than its expression
++ --> $DIR/match_str_case_mismatch.rs:164:9
+ |
+LL | "Bar" => {},
+ | ^^^^^
+ |
+help: consider changing the case of this arm to respect `to_ascii_lowercase`
+ |
+LL | "bar" => {},
+ | ~~~~~
+
+error: this `match` arm has a differing case than its expression
- error: aborting due to 3 previous errors
++ --> $DIR/match_str_case_mismatch.rs:179:9
+ |
+LL | "bAR" => {},
+ | ^^^^^
+ |
+help: consider changing the case of this arm to respect `to_ascii_uppercase`
+ |
+LL | "BAR" => {},
+ | ~~~~~
+
++error: aborting due to 7 previous errors
+
--- /dev/null
--- /dev/null
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:14:9
++ |
++LL | Err(_) => panic!("err"),
++ | ^^^^^^
++ |
++ = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:20:9
++ |
++LL | Err(_) => panic!(),
++ | ^^^^^^
++ |
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:26:9
++ |
++LL | Err(_) => {
++ | ^^^^^^
++ |
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: `Err(_e)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:34:9
++ |
++LL | Err(_e) => panic!(),
++ | ^^^^^^^
++ |
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:14:9
++ |
++LL | Err(_) => panic!("err"),
++ | ^^^^^^
++ |
++ = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:20:9
++ |
++LL | Err(_) => panic!(),
++ | ^^^^^^
++ |
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:26:9
++ |
++LL | Err(_) => {
++ | ^^^^^^
++ |
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: `Err(_e)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:34:9
++ |
++LL | Err(_e) => panic!(),
++ | ^^^^^^^
++ |
++ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++
++error: aborting due to 4 previous errors
++
--- /dev/null
++// revisions: edition2018 edition2021
++// [edition2018] edition:2018
++// [edition2021] edition:2021
+#![feature(exclusive_range_pattern)]
+#![allow(clippy::match_same_arms)]
+#![warn(clippy::match_wild_err_arm)]
+
+fn match_wild_err_arm() {
+ let x: Result<i32, &str> = Ok(3);
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => panic!("err"),
+ }
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => panic!(),
+ }
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => {
+ panic!();
+ },
+ }
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_e) => panic!(),
+ }
+
+ // Allowed when used in `panic!`.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_e) => panic!("{}", _e),
+ }
+
+ // Allowed when not with `panic!` block.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => println!("err"),
+ }
+
+ // Allowed when used with `unreachable!`.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => unreachable!(),
+ }
+
+ // Allowed when used with `unreachable!`.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => {
+ unreachable!();
+ },
+ }
+}
+
+fn main() {}
--- /dev/null
- // edition:2018
+// aux-build:option_helpers.rs
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::blacklisted_name,
+ clippy::default_trait_access,
+ clippy::missing_docs_in_private_items,
+ clippy::missing_safety_doc,
+ clippy::non_ascii_literal,
+ clippy::new_without_default,
+ clippy::needless_pass_by_value,
+ clippy::needless_lifetimes,
+ clippy::print_stdout,
+ clippy::must_use_candidate,
+ clippy::use_self,
+ clippy::useless_format,
+ clippy::wrong_self_convention,
+ clippy::unused_self,
+ unused
+)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::VecDeque;
+use std::iter::FromIterator;
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives};
+
+struct Lt<'a> {
+ foo: &'a u32,
+}
+
+impl<'a> Lt<'a> {
+ // The lifetime is different, but that’s irrelevant; see issue #734.
+ #[allow(clippy::needless_lifetimes)]
+ pub fn new<'b>(s: &'b str) -> Lt<'b> {
+ unimplemented!()
+ }
+}
+
+struct Lt2<'a> {
+ foo: &'a u32,
+}
+
+impl<'a> Lt2<'a> {
+ // The lifetime is different, but that’s irrelevant; see issue #734.
+ pub fn new(s: &str) -> Lt2 {
+ unimplemented!()
+ }
+}
+
+struct Lt3<'a> {
+ foo: &'a u32,
+}
+
+impl<'a> Lt3<'a> {
+ // The lifetime is different, but that’s irrelevant; see issue #734.
+ pub fn new() -> Lt3<'static> {
+ unimplemented!()
+ }
+}
+
+#[derive(Clone, Copy)]
+struct U;
+
+impl U {
+ fn new() -> Self {
+ U
+ }
+ // Ok because `U` is `Copy`.
+ fn to_something(self) -> u32 {
+ 0
+ }
+}
+
+struct V<T> {
+ _dummy: T,
+}
+
+impl<T> V<T> {
+ fn new() -> Option<V<T>> {
+ None
+ }
+}
+
+struct AsyncNew;
+
+impl AsyncNew {
+ async fn new() -> Option<Self> {
+ None
+ }
+}
+
+struct BadNew;
+
+impl BadNew {
+ fn new() -> i32 {
+ 0
+ }
+}
+
+struct T;
+
+impl Mul<T> for T {
+ type Output = T;
+ // No error, obviously.
+ fn mul(self, other: T) -> T {
+ self
+ }
+}
+
+/// Checks implementation of `FILTER_NEXT` lint.
+#[rustfmt::skip]
+fn filter_next() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+
+ // Multi-line case.
+ let _ = v.iter().filter(|&x| {
+ *x < 0
+ }
+ ).next();
+
+ // Check that we don't lint if the caller is not an `Iterator`.
+ let foo = IteratorFalsePositives { foo: 0 };
+ let _ = foo.filter().next();
+
+ let foo = IteratorMethodFalsePositives {};
+ let _ = foo.filter(42).next();
+}
+
+fn main() {
+ filter_next();
+}
--- /dev/null
- --> $DIR/methods.rs:105:5
+error: methods called `new` usually return `Self`
- --> $DIR/methods.rs:126:13
++ --> $DIR/methods.rs:104:5
+ |
+LL | / fn new() -> i32 {
+LL | | 0
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
+
+error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
++ --> $DIR/methods.rs:125:13
+ |
+LL | let _ = v.iter().filter(|&x| {
+ | _____________^
+LL | | *x < 0
+LL | | }
+LL | | ).next();
+ | |___________________________^
+ |
+ = note: `-D clippy::filter-next` implied by `-D warnings`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
-
+#![warn(clippy::missing_docs_in_private_items)]
+// When denying at the crate level, be sure to not get random warnings from the
+// injected intrinsics by the compiler.
+#![allow(dead_code)]
+#![feature(global_asm)]
- pub use internal_impl::documented as foo;
- pub use internal_impl::globbed::*;
- pub use internal_impl::undocumented1 as bar;
- pub use internal_impl::{documented, undocumented2};
+//! Some garbage docs for the crate here
+#![doc = "More garbage"]
+
+type Typedef = String;
+pub type PubTypedef = String;
+
+mod module_no_dox {}
+pub mod pub_module_no_dox {}
+
+/// dox
+pub fn foo() {}
+pub fn foo2() {}
+fn foo3() {}
+#[allow(clippy::missing_docs_in_private_items)]
+pub fn foo4() {}
+
+// It sure is nice if doc(hidden) implies allow(missing_docs), and that it
+// applies recursively
+#[doc(hidden)]
+mod a {
+ pub fn baz() {}
+ pub mod b {
+ pub fn baz() {}
+ }
+}
+
+enum Baz {
+ BazA { a: isize, b: isize },
+ BarB,
+}
+
+pub enum PubBaz {
+ PubBazA { a: isize },
+}
+
+/// dox
+pub enum PubBaz2 {
+ /// dox
+ PubBaz2A {
+ /// dox
+ a: isize,
+ },
+}
+
+#[allow(clippy::missing_docs_in_private_items)]
+pub enum PubBaz3 {
+ PubBaz3A { b: isize },
+}
+
+#[doc(hidden)]
+pub fn baz() {}
+
+const FOO: u32 = 0;
+/// dox
+pub const FOO1: u32 = 0;
+#[allow(clippy::missing_docs_in_private_items)]
+pub const FOO2: u32 = 0;
+#[doc(hidden)]
+pub const FOO3: u32 = 0;
+pub const FOO4: u32 = 0;
+
+static BAR: u32 = 0;
+/// dox
+pub static BAR1: u32 = 0;
+#[allow(clippy::missing_docs_in_private_items)]
+pub static BAR2: u32 = 0;
+#[doc(hidden)]
+pub static BAR3: u32 = 0;
+pub static BAR4: u32 = 0;
+
+mod internal_impl {
+ /// dox
+ pub fn documented() {}
+ pub fn undocumented1() {}
+ pub fn undocumented2() {}
+ fn undocumented3() {}
+ /// dox
+ pub mod globbed {
+ /// dox
+ pub fn also_documented() {}
+ pub fn also_undocumented1() {}
+ fn also_undocumented2() {}
+ }
+}
+/// dox
+pub mod public_interface {
++ pub use crate::internal_impl::documented as foo;
++ pub use crate::internal_impl::globbed::*;
++ pub use crate::internal_impl::undocumented1 as bar;
++ pub use crate::internal_impl::{documented, undocumented2};
+}
+
+fn main() {}
+
+// Ensure global asm doesn't require documentation.
+global_asm! { "" }
--- /dev/null
- --> $DIR/missing-doc.rs:10:1
+error: missing documentation for a type alias
- --> $DIR/missing-doc.rs:11:1
++ --> $DIR/missing-doc.rs:9:1
+ |
+LL | type Typedef = String;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
+
+error: missing documentation for a type alias
- --> $DIR/missing-doc.rs:13:1
++ --> $DIR/missing-doc.rs:10:1
+ |
+LL | pub type PubTypedef = String;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a module
- --> $DIR/missing-doc.rs:14:1
++ --> $DIR/missing-doc.rs:12:1
+ |
+LL | mod module_no_dox {}
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a module
- --> $DIR/missing-doc.rs:18:1
++ --> $DIR/missing-doc.rs:13:1
+ |
+LL | pub mod pub_module_no_dox {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
- --> $DIR/missing-doc.rs:19:1
++ --> $DIR/missing-doc.rs:17:1
+ |
+LL | pub fn foo2() {}
+ | ^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
- --> $DIR/missing-doc.rs:33:1
++ --> $DIR/missing-doc.rs:18:1
+ |
+LL | fn foo3() {}
+ | ^^^^^^^^^^^^
+
+error: missing documentation for an enum
- --> $DIR/missing-doc.rs:34:5
++ --> $DIR/missing-doc.rs:32:1
+ |
+LL | / enum Baz {
+LL | | BazA { a: isize, b: isize },
+LL | | BarB,
+LL | | }
+ | |_^
+
+error: missing documentation for a variant
- --> $DIR/missing-doc.rs:34:12
++ --> $DIR/missing-doc.rs:33:5
+ |
+LL | BazA { a: isize, b: isize },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct field
- --> $DIR/missing-doc.rs:34:22
++ --> $DIR/missing-doc.rs:33:12
+ |
+LL | BazA { a: isize, b: isize },
+ | ^^^^^^^^
+
+error: missing documentation for a struct field
- --> $DIR/missing-doc.rs:35:5
++ --> $DIR/missing-doc.rs:33:22
+ |
+LL | BazA { a: isize, b: isize },
+ | ^^^^^^^^
+
+error: missing documentation for a variant
- --> $DIR/missing-doc.rs:38:1
++ --> $DIR/missing-doc.rs:34:5
+ |
+LL | BarB,
+ | ^^^^
+
+error: missing documentation for an enum
- --> $DIR/missing-doc.rs:39:5
++ --> $DIR/missing-doc.rs:37:1
+ |
+LL | / pub enum PubBaz {
+LL | | PubBazA { a: isize },
+LL | | }
+ | |_^
+
+error: missing documentation for a variant
- --> $DIR/missing-doc.rs:39:15
++ --> $DIR/missing-doc.rs:38:5
+ |
+LL | PubBazA { a: isize },
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct field
- --> $DIR/missing-doc.rs:59:1
++ --> $DIR/missing-doc.rs:38:15
+ |
+LL | PubBazA { a: isize },
+ | ^^^^^^^^
+
+error: missing documentation for a constant
- --> $DIR/missing-doc.rs:66:1
++ --> $DIR/missing-doc.rs:58:1
+ |
+LL | const FOO: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a constant
- --> $DIR/missing-doc.rs:68:1
++ --> $DIR/missing-doc.rs:65:1
+ |
+LL | pub const FOO4: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a static
- --> $DIR/missing-doc.rs:75:1
++ --> $DIR/missing-doc.rs:67:1
+ |
+LL | static BAR: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a static
- --> $DIR/missing-doc.rs:77:1
++ --> $DIR/missing-doc.rs:74:1
+ |
+LL | pub static BAR4: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a module
- --> $DIR/missing-doc.rs:80:5
++ --> $DIR/missing-doc.rs:76:1
+ |
+LL | / mod internal_impl {
+LL | | /// dox
+LL | | pub fn documented() {}
+LL | | pub fn undocumented1() {}
+... |
+LL | | }
+LL | | }
+ | |_^
+
+error: missing documentation for a function
- --> $DIR/missing-doc.rs:81:5
++ --> $DIR/missing-doc.rs:79:5
+ |
+LL | pub fn undocumented1() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
- --> $DIR/missing-doc.rs:82:5
++ --> $DIR/missing-doc.rs:80:5
+ |
+LL | pub fn undocumented2() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
- --> $DIR/missing-doc.rs:87:9
++ --> $DIR/missing-doc.rs:81:5
+ |
+LL | fn undocumented3() {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
- --> $DIR/missing-doc.rs:88:9
++ --> $DIR/missing-doc.rs:86:9
+ |
+LL | pub fn also_undocumented1() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
++ --> $DIR/missing-doc.rs:87:9
+ |
+LL | fn also_undocumented2() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 24 previous errors
+
--- /dev/null
-
+#![warn(clippy::missing_panics_doc)]
+#![allow(clippy::option_map_unit_fn)]
+fn main() {}
+
+/// This needs to be documented
+pub fn unwrap() {
+ let result = Err("Hi");
+ result.unwrap()
+}
+
+/// This needs to be documented
+pub fn panic() {
+ panic!("This function panics")
+}
+
+/// This needs to be documented
+pub fn todo() {
+ todo!()
+}
+
+/// This needs to be documented
+pub fn inner_body(opt: Option<u32>) {
+ opt.map(|x| {
+ if x == 10 {
+ panic!()
+ }
+ });
+}
+
+/// This needs to be documented
+pub fn unreachable_and_panic() {
+ if true { unreachable!() } else { panic!() }
+}
+
+/// This needs to be documented
+pub fn assert_eq() {
+ let x = 0;
+ assert_eq!(x, 0);
+}
+
+/// This needs to be documented
+pub fn assert_ne() {
+ let x = 0;
+ assert_ne!(x, 0);
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `result` if an error
+pub fn unwrap_documented() {
+ let result = Err("Hi");
+ result.unwrap()
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics just because
+pub fn panic_documented() {
+ panic!("This function panics")
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `opt` is Just(10)
+pub fn inner_body_documented(opt: Option<u32>) {
+ opt.map(|x| {
+ if x == 10 {
+ panic!()
+ }
+ });
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// We still need to do this part
+pub fn todo_documented() {
+ todo!()
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// We still need to do this part
+pub fn unreachable_amd_panic_documented() {
+ if true { unreachable!() } else { panic!() }
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `x` is not 0.
+pub fn assert_eq_documented() {
+ let x = 0;
+ assert_eq!(x, 0);
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `x` is 0.
+pub fn assert_ne_documented() {
+ let x = 0;
+ assert_ne!(x, 0);
+}
+
+/// This is okay because it is private
+fn unwrap_private() {
+ let result = Err("Hi");
+ result.unwrap()
+}
+
+/// This is okay because it is private
+fn panic_private() {
+ panic!("This function panics")
+}
+
+/// This is okay because it is private
+fn todo_private() {
+ todo!()
+}
+
+/// This is okay because it is private
+fn inner_body_private(opt: Option<u32>) {
+ opt.map(|x| {
+ if x == 10 {
+ panic!()
+ }
+ });
+}
+
+/// This is okay because unreachable
+pub fn unreachable() {
+ unreachable!("This function panics")
+}
+
+/// #6970.
+/// This is okay because it is expansion of `debug_assert` family.
+pub fn debug_assertions() {
+ debug_assert!(false);
+ debug_assert_eq!(1, 2);
+ debug_assert_ne!(1, 2);
+}
--- /dev/null
- --> $DIR/missing_panics_doc.rs:7:1
+error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:9:5
++ --> $DIR/missing_panics_doc.rs:6:1
+ |
+LL | / pub fn unwrap() {
+LL | | let result = Err("Hi");
+LL | | result.unwrap()
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::missing-panics-doc` implied by `-D warnings`
+note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:13:1
++ --> $DIR/missing_panics_doc.rs:8:5
+ |
+LL | result.unwrap()
+ | ^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:14:5
++ --> $DIR/missing_panics_doc.rs:12:1
+ |
+LL | / pub fn panic() {
+LL | | panic!("This function panics")
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/missing_panics_doc.rs:13:5
+ |
+LL | panic!("This function panics")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- --> $DIR/missing_panics_doc.rs:18:1
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:19:5
++ --> $DIR/missing_panics_doc.rs:17:1
+ |
+LL | / pub fn todo() {
+LL | | todo!()
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:23:1
++ --> $DIR/missing_panics_doc.rs:18:5
+ |
+LL | todo!()
+ | ^^^^^^^
+ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:26:13
++ --> $DIR/missing_panics_doc.rs:22:1
+ |
+LL | / pub fn inner_body(opt: Option<u32>) {
+LL | | opt.map(|x| {
+LL | | if x == 10 {
+LL | | panic!()
+LL | | }
+LL | | });
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/missing_panics_doc.rs:25:13
+ |
+LL | panic!()
+ | ^^^^^^^^
- --> $DIR/missing_panics_doc.rs:32:1
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:33:39
++ --> $DIR/missing_panics_doc.rs:31:1
+ |
+LL | / pub fn unreachable_and_panic() {
+LL | | if true { unreachable!() } else { panic!() }
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/missing_panics_doc.rs:32:39
+ |
+LL | if true { unreachable!() } else { panic!() }
+ | ^^^^^^^^
- --> $DIR/missing_panics_doc.rs:37:1
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:39:5
++ --> $DIR/missing_panics_doc.rs:36:1
+ |
+LL | / pub fn assert_eq() {
+LL | | let x = 0;
+LL | | assert_eq!(x, 0);
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
- --> $DIR/missing_panics_doc.rs:43:1
++ --> $DIR/missing_panics_doc.rs:38:5
+ |
+LL | assert_eq!(x, 0);
+ | ^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: docs for function which may panic missing `# Panics` section
- --> $DIR/missing_panics_doc.rs:45:5
++ --> $DIR/missing_panics_doc.rs:42:1
+ |
+LL | / pub fn assert_ne() {
+LL | | let x = 0;
+LL | | assert_ne!(x, 0);
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
++ --> $DIR/missing_panics_doc.rs:44:5
+ |
+LL | assert_ne!(x, 0);
+ | ^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 7 previous errors
+
--- /dev/null
- // edition:2018
+// FIXME: run-rustfix waiting on multi-span suggestions
+
+#![warn(clippy::needless_borrow)]
+#![allow(clippy::needless_borrowed_reference)]
+
+fn f1(_: &str) {}
+macro_rules! m1 {
+ ($e:expr) => {
+ f1($e)
+ };
+}
+macro_rules! m3 {
+ ($i:ident) => {
+ Some(ref $i)
+ };
+}
+macro_rules! if_chain {
+ (if $e:expr; $($rest:tt)*) => {
+ if $e {
+ if_chain!($($rest)*)
+ }
+ };
+
+ (if let $p:pat = $e:expr; $($rest:tt)*) => {
+ if let $p = $e {
+ if_chain!($($rest)*)
+ }
+ };
+
+ (then $b:block) => {
+ $b
+ };
+}
+
+#[allow(dead_code)]
+fn main() {
+ let x = String::new();
+
+ // Ok, reference to a String.
+ let _: &String = match Some(x.clone()) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Ok, reference to a &mut String
+ let _: &&mut String = match Some(&mut x.clone()) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Ok, the pattern is from a macro
+ let _: &String = match Some(&x) {
+ m3!(x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &String = match Some(&x) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String.
+ let _: &String = match Some(&x) {
+ Some(ref x) => *x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &String = match Some(&x) {
+ Some(ref x) => {
+ f1(x);
+ f1(*x);
+ x
+ },
+ None => return,
+ };
+
+ // Err, reference to a &String
+ match Some(&x) {
+ Some(ref x) => m1!(x),
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _ = |&ref x: &&String| {
+ let _: &String = x;
+ };
+
+ // Err, reference to a &String
+ let (ref y,) = (&x,);
+ let _: &String = *y;
+
+ let y = &&x;
+ // Ok, different y
+ let _: &String = *y;
+
+ let x = (0, 0);
+ // Err, reference to a &u32. Don't suggest adding a reference to the field access.
+ let _: u32 = match Some(&x) {
+ Some(ref x) => x.0,
+ None => return,
+ };
+
+ enum E {
+ A(&'static u32),
+ B(&'static u32),
+ }
+ // Err, reference to &u32.
+ let _: &u32 = match E::A(&0) {
+ E::A(ref x) | E::B(ref x) => *x,
+ };
+
+ // Err, reference to &String.
+ if_chain! {
+ if true;
+ if let Some(ref x) = Some(&String::new());
+ then {
+ f1(x);
+ }
+ }
+}
+
+// Err, reference to a &String
+fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ let _: &String = x;
+ *x
+}
+
+trait T1 {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &String = x;
+ }
+}
+
+struct S;
+impl T1 for S {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &String = *x;
+ }
+}
+
+// Ok - used to error due to rustc bug
+#[allow(dead_code)]
+#[derive(Debug)]
+enum Foo<'a> {
+ Str(&'a str),
+}
--- /dev/null
- --> $DIR/needless_borrow_pat.rs:60:14
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:66:14
++ --> $DIR/needless_borrow_pat.rs:59:14
+ |
+LL | Some(ref x) => x,
+ | ^^^^^ help: try this: `x`
+ |
+ = note: `-D clippy::needless-borrow` implied by `-D warnings`
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:72:14
++ --> $DIR/needless_borrow_pat.rs:65:14
+ |
+LL | Some(ref x) => *x,
+ | ^^^^^
+ |
+help: try this
+ |
+LL | Some(x) => x,
+ | ~ ~
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:82:14
++ --> $DIR/needless_borrow_pat.rs:71:14
+ |
+LL | Some(ref x) => {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ Some(x) => {
+LL | f1(x);
+LL ~ f1(x);
+ |
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:87:15
++ --> $DIR/needless_borrow_pat.rs:81:14
+ |
+LL | Some(ref x) => m1!(x),
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:92:10
++ --> $DIR/needless_borrow_pat.rs:86:15
+ |
+LL | let _ = |&ref x: &&String| {
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:102:14
++ --> $DIR/needless_borrow_pat.rs:91:10
+ |
+LL | let (ref y,) = (&x,);
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ let (y,) = (&x,);
+LL ~ let _: &String = y;
+ |
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:112:14
++ --> $DIR/needless_borrow_pat.rs:101:14
+ |
+LL | Some(ref x) => x.0,
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:118:21
++ --> $DIR/needless_borrow_pat.rs:111:14
+ |
+LL | E::A(ref x) | E::B(ref x) => *x,
+ | ^^^^^ ^^^^^
+ |
+help: try this
+ |
+LL | E::A(x) | E::B(x) => x,
+ | ~ ~ ~
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:126:12
++ --> $DIR/needless_borrow_pat.rs:117:21
+ |
+LL | if let Some(ref x) = Some(&String::new());
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:133:11
++ --> $DIR/needless_borrow_pat.rs:125:12
+ |
+LL | fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f2<'a>(&x: &&'a String) -> &'a String {
+LL | let _: &String = x;
+LL ~ x
+ |
+
+error: this pattern creates a reference to a reference
- --> $DIR/needless_borrow_pat.rs:141:11
++ --> $DIR/needless_borrow_pat.rs:132:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
++ --> $DIR/needless_borrow_pat.rs:140:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f(&x: &&String) {
+LL ~ let _: &String = x;
+ |
+
+error: aborting due to 12 previous errors
+
--- /dev/null
+#![warn(clippy::needless_lifetimes)]
+#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop)]
+
+fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
+
+fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
+
+// No error; same lifetime on two params.
+fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {}
+
+// No error; static involved.
+fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {}
+
+fn mut_and_static_input(_x: &mut u8, _y: &'static str) {}
+
+fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
+ x
+}
+
+// No error; multiple input refs.
+fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 {
+ x
+}
+
+// No error; multiple input refs.
+fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
+ x
+}
+
++// No error; multiple input refs
++async fn func<'a>(args: &[&'a str]) -> Option<&'a str> {
++ args.get(0).cloned()
++}
++
+// No error; static involved.
+fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 {
+ x
+}
+
+// No error.
+fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
+ Ok(x)
+}
+
+// No error; two input refs.
+fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 {
+ x.unwrap()
+}
+
+fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
+ Ok(x)
+}
+
+// Where-clause, but without lifetimes.
+fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
+where
+ T: Copy,
+{
+ Ok(x)
+}
+
+type Ref<'r> = &'r u8;
+
+// No error; same lifetime on two params.
+fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {}
+
+fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
+
+// No error; bounded lifetime.
+fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {}
+
+// No error; bounded lifetime.
+fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8)
+where
+ 'b: 'a,
+{
+}
+
+struct Lt<'a, I: 'static> {
+ x: &'a I,
+}
+
+// No error; fn bound references `'a`.
+fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+where
+ F: Fn(Lt<'a, I>) -> Lt<'a, I>,
+{
+ unreachable!()
+}
+
+fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+where
+ for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>,
+{
+ unreachable!()
+}
+
+// No error; see below.
+fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) {
+ f(x);
+}
+
+fn fn_bound_3_cannot_elide() {
+ let x = 42;
+ let p = &x;
+ let mut q = &x;
+ // This will fail if we elide lifetimes of `fn_bound_3`.
+ fn_bound_3(p, |y| q = y);
+}
+
+// No error; multiple input refs.
+fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () {
+ if cond { x } else { f() }
+}
+
+struct X {
+ x: u8,
+}
+
+impl X {
+ fn self_and_out<'s>(&'s self) -> &'s u8 {
+ &self.x
+ }
+
+ // No error; multiple input refs.
+ fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
+ &self.x
+ }
+
+ fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
+
+ // No error; same lifetimes on two params.
+ fn self_and_same_in<'s>(&'s self, _x: &'s u8) {}
+}
+
+struct Foo<'a>(&'a u8);
+
+impl<'a> Foo<'a> {
+ // No error; lifetime `'a` not defined in method.
+ fn self_shared_lifetime(&self, _: &'a u8) {}
+ // No error; bounds exist.
+ fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {}
+}
+
+fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
+ unimplemented!()
+}
+
+fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (named on the reference, anonymous on `Foo`).
+fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (anonymous on the reference, named on `Foo`).
+fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes.
+fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
+ unimplemented!()
+}
+
+trait WithLifetime<'a> {}
+
+type WithLifetimeAlias<'a> = dyn WithLifetime<'a>;
+
+// Should not warn because it won't build without the lifetime.
+fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str {
+ unimplemented!()
+}
+
+// Should warn because there is no lifetime on `Drop`, so this would be
+// unambiguous if we elided the lifetime.
+fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
+ unimplemented!()
+}
+
+type FooAlias<'a> = Foo<'a>;
+
+fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`).
+fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`).
+fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes.
+fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
+ unimplemented!()
+}
+
+fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
+ unimplemented!()
+}
+
+fn elided_input_named_output<'a>(_arg: &str) -> &'a str {
+ unimplemented!()
+}
+
+fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
+ unimplemented!()
+}
+fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) {
+ unimplemented!()
+}
+
+// Don't warn on these; see issue #292.
+fn trait_bound_bug<'a, T: WithLifetime<'a>>() {
+ unimplemented!()
+}
+
+// See issue #740.
+struct Test {
+ vec: Vec<usize>,
+}
+
+impl Test {
+ fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
+ unimplemented!()
+ }
+}
+
+trait LintContext<'a> {}
+
+fn f<'a, T: LintContext<'a>>(_: &T) {}
+
+fn test<'a>(x: &'a [u8]) -> u8 {
+ let y: &'a u8 = &x[5];
+ *y
+}
+
+// Issue #3284: give hint regarding lifetime in return type.
+struct Cow<'a> {
+ x: &'a str,
+}
+fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
+ unimplemented!()
+}
+
+// Make sure we still warn on implementations
+mod issue4291 {
+ trait BadTrait {
+ fn needless_lt<'a>(x: &'a u8) {}
+ }
+
+ impl BadTrait for () {
+ fn needless_lt<'a>(_x: &'a u8) {}
+ }
+}
+
+mod issue2944 {
+ trait Foo {}
+ struct Bar {}
+ struct Baz<'a> {
+ bar: &'a Bar,
+ }
+
+ impl<'a> Foo for Baz<'a> {}
+ impl Bar {
+ fn baz<'a>(&'a self) -> impl Foo + 'a {
+ Baz { bar: self }
+ }
+ }
+}
+
+mod nested_elision_sites {
+ // issue #issue2944
+
+ // closure trait bounds subject to nested elision
+ // don't lint because they refer to outer lifetimes
+ fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
+ move || i
+ }
+ fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
+ move || i
+ }
+ fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
+ move || i
+ }
+
+ // don't lint
+ fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
+ f()
+ }
+ fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
+ move || i
+ }
+ // lint
+ fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
+ f(i)
+ }
+ fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
+ f(i)
+ }
+
+ // don't lint
+ fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
+ f()
+ }
+ // lint
+ fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
+ f(i)
+ }
+
+ // don't lint
+ fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
+ where
+ T: Fn() -> &'a i32,
+ {
+ f()
+ }
+ // lint
+ fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
+ where
+ T: Fn(&i32) -> &i32,
+ {
+ f(i)
+ }
+
+ // don't lint
+ fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
+ f(i)
+ }
+ fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
+ |i| i
+ }
+ // lint
+ fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
+ f(i)
+ }
+
+ // don't lint
+ fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
+ |f| 42
+ }
+ fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
+ |f| ()
+ }
+
+ // lint
+ fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
+ |f| 42
+ }
+ fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
+ |f| ()
+ }
+}
+
+mod issue6159 {
+ use std::ops::Deref;
+ pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R
+ where
+ T: Deref,
+ F: FnOnce(&'a T::Target) -> R,
+ {
+ f(x.deref())
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/needless_lifetimes.rs:45:1
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:4:1
+ |
+LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:6:1
+ |
+LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:16:1
+ |
+LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:50:1
++ --> $DIR/needless_lifetimes.rs:50:1
+ |
+LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:62:1
++ --> $DIR/needless_lifetimes.rs:55:1
+ |
+LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:86:1
++ --> $DIR/needless_lifetimes.rs:67:1
+ |
+LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:116:5
++ --> $DIR/needless_lifetimes.rs:91:1
+ |
+LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:125:5
++ --> $DIR/needless_lifetimes.rs:121:5
+ |
+LL | fn self_and_out<'s>(&'s self) -> &'s u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:144:1
++ --> $DIR/needless_lifetimes.rs:130:5
+ |
+LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:174:1
++ --> $DIR/needless_lifetimes.rs:149:1
+ |
+LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:180:1
++ --> $DIR/needless_lifetimes.rs:179:1
+ |
+LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:199:1
++ --> $DIR/needless_lifetimes.rs:185:1
+ |
+LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:207:1
++ --> $DIR/needless_lifetimes.rs:204:1
+ |
+LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:243:1
++ --> $DIR/needless_lifetimes.rs:212:1
+ |
+LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:250:9
++ --> $DIR/needless_lifetimes.rs:248:1
+ |
+LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:254:9
++ --> $DIR/needless_lifetimes.rs:255:9
+ |
+LL | fn needless_lt<'a>(x: &'a u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:267:9
++ --> $DIR/needless_lifetimes.rs:259:9
+ |
+LL | fn needless_lt<'a>(_x: &'a u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:296:5
++ --> $DIR/needless_lifetimes.rs:272:9
+ |
+LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:299:5
++ --> $DIR/needless_lifetimes.rs:301:5
+ |
+LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:308:5
++ --> $DIR/needless_lifetimes.rs:304:5
+ |
+LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:320:5
++ --> $DIR/needless_lifetimes.rs:313:5
+ |
+LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:335:5
++ --> $DIR/needless_lifetimes.rs:325:5
+ |
+LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:348:5
++ --> $DIR/needless_lifetimes.rs:340:5
+ |
+LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:351:5
++ --> $DIR/needless_lifetimes.rs:353:5
+ |
+LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:356:5
+ |
+LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 25 previous errors
+
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![feature(let_else)]
+#![allow(unused)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::single_match,
+ clippy::needless_bool,
+ clippy::equatable_if_let
+)]
+#![warn(clippy::needless_return)]
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+fn test_no_semicolon() -> bool {
+ true
+}
+
+fn test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+fn test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+fn test_void_fun() {
+
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+
+ } else {
+
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => {},
+ }
+}
+
+fn read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| {})
+ }
+
+ fn test_closure() {
+ let _ = || {
+
+ };
+ let _ = || {};
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+async fn async_test_no_semicolon() -> bool {
+ true
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+
+ } else {
+
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => {},
+ }
+}
+
+async fn async_read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn main() {}
--- /dev/null
- // edition:2018
+// run-rustfix
+
+#![feature(let_else)]
+#![allow(unused)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::single_match,
+ clippy::needless_bool,
+ clippy::equatable_if_let
+)]
+#![warn(clippy::needless_return)]
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+fn test_no_semicolon() -> bool {
+ return true;
+}
+
+fn test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+fn test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+fn test_void_fun() {
+ return;
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+fn read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| return)
+ }
+
+ fn test_closure() {
+ let _ = || {
+ return;
+ };
+ let _ = || return;
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| return Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+async fn async_test_no_semicolon() -> bool {
+ return true;
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+ return;
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+async fn async_read_line() -> String {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ use std::io::BufRead;
+ let stdin = ::std::io::stdin();
+ let _a = stdin.lock().lines().next().unwrap().unwrap();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/needless_return.rs:25:5
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:29:5
++ --> $DIR/needless_return.rs:24: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:34:9
++ --> $DIR/needless_return.rs:28:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:36:9
++ --> $DIR/needless_return.rs:33:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:42:17
++ --> $DIR/needless_return.rs:35:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:44:13
++ --> $DIR/needless_return.rs:41:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:51:9
++ --> $DIR/needless_return.rs:43:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:53:16
++ --> $DIR/needless_return.rs:50:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:61:5
++ --> $DIR/needless_return.rs:52:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:66:9
++ --> $DIR/needless_return.rs:60:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:68:9
++ --> $DIR/needless_return.rs:65:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:75:14
++ --> $DIR/needless_return.rs:67:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:90:9
++ --> $DIR/needless_return.rs:74:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:92:9
++ --> $DIR/needless_return.rs:89:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:113:32
++ --> $DIR/needless_return.rs:91:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:118:13
++ --> $DIR/needless_return.rs:112:32
+ |
+LL | bar.unwrap_or_else(|_| return)
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:120:20
++ --> $DIR/needless_return.rs:117:13
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:126:32
++ --> $DIR/needless_return.rs:119:20
+ |
+LL | let _ = || return;
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:135:5
++ --> $DIR/needless_return.rs:125:32
+ |
+LL | res.unwrap_or_else(|_| return Foo)
+ | ^^^^^^^^^^ help: remove `return`: `Foo`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:139:5
++ --> $DIR/needless_return.rs:134:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:144:9
++ --> $DIR/needless_return.rs:138:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:146:9
++ --> $DIR/needless_return.rs:143:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:152:17
++ --> $DIR/needless_return.rs:145:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:154:13
++ --> $DIR/needless_return.rs:151:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:161:9
++ --> $DIR/needless_return.rs:153:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:163:16
++ --> $DIR/needless_return.rs:160:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:171:5
++ --> $DIR/needless_return.rs:162:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:176:9
++ --> $DIR/needless_return.rs:170:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:178:9
++ --> $DIR/needless_return.rs:175:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:185:14
++ --> $DIR/needless_return.rs:177:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:200:9
++ --> $DIR/needless_return.rs:184:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:202:9
++ --> $DIR/needless_return.rs:199:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
++ --> $DIR/needless_return.rs:201:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: aborting due to 32 previous errors
+
--- /dev/null
- MaybeInst::Split1(goto1) => panic!(1),
- MaybeInst::Split2(goto2) => panic!(2),
+#![warn(clippy::all)]
+#![allow(unused, clippy::println_empty_string)]
+
+#[derive(Clone, Debug)]
+enum MaybeInst {
+ Split,
+ Split1(usize),
+ Split2(usize),
+}
+
+struct InstSplit {
+ uiae: usize,
+}
+
+impl MaybeInst {
+ fn fill(&mut self) {
+ let filled = match *self {
++ MaybeInst::Split1(goto1) => panic!("1"),
++ MaybeInst::Split2(goto2) => panic!("2"),
+ _ => unimplemented!(),
+ };
+ unimplemented!()
+ }
+}
+
+fn underscores_and_numbers() {
+ let _1 = 1; //~ERROR Consider a more descriptive name
+ let ____1 = 1; //~ERROR Consider a more descriptive name
+ let __1___2 = 12; //~ERROR Consider a more descriptive name
+ let _1_ok = 1;
+}
+
+fn issue2927() {
+ let args = 1;
+ format!("{:?}", 2);
+}
+
+fn issue3078() {
+ match "a" {
+ stringify!(a) => {},
+ _ => {},
+ }
+}
+
+struct Bar;
+
+impl Bar {
+ fn bar() {
+ let _1 = 1;
+ let ____1 = 1;
+ let __1___2 = 12;
+ let _1_ok = 1;
+ }
+}
+
+fn main() {}
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+ string.map_or((false, "hello"), |x| (true, x))
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+ if string.is_none() {
+ None
+ } else if let Some(x) = string {
+ Some((true, x))
+ } else {
+ Some((false, ""))
+ }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+ let _ = string.map_or(0, |s| s.len());
+ let _ = num.as_ref().map_or(&0, |s| s);
+ let _ = num.as_mut().map_or(&mut 0, |s| {
+ *s += 1;
+ s
+ });
+ let _ = num.as_ref().map_or(&0, |s| s);
+ let _ = num.map_or(0, |mut s| {
+ s += 1;
+ s
+ });
+ let _ = num.as_mut().map_or(&mut 0, |s| {
+ *s += 1;
+ s
+ });
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+ arg.map_or(13, |x| {
+ let y = x * x;
+ y * y
+ })
+}
+
+fn impure_else(arg: Option<i32>) {
+ let side_effect = || {
+ println!("return 1");
+ 1
+ };
+ let _ = arg.map_or_else(|| side_effect(), |x| x);
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+ let _ = arg.map_or_else(|| {
+ let mut y = 1;
+ y = (y + 2 / y) / 2;
+ y = (y + 2 / y) / 2;
+ y
+ }, |x| x * x * x * x);
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+ let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+ for _ in 0..10 {
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ continue;
+ };
+ }
+ let _ = if let Some(x) = arg {
+ return x;
+ } else {
+ 5
+ };
+ 7
+}
+
+fn main() {
+ let optional = Some(5);
+ let _ = optional.map_or(5, |x| x + 2);
+ let _ = bad1(None);
+ let _ = else_if_option(None);
+ unop_bad(&None, None);
+ let _ = longer_body(None);
+ test_map_or_else(None);
+ let _ = negative_tests(None);
+ let _ = impure_else(None);
+
+ let _ = Some(0).map_or(0, |x| loop {
+ if x == 0 {
+ break x;
+ }
+ });
+
+ // #7576
+ const fn _f(x: Option<u32>) -> u32 {
+ // Don't lint, `map_or` isn't const
+ if let Some(x) = x { x } else { 10 }
+ }
+
+ // #5822
+ let s = String::new();
+ // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ s.len()
+ };
+
+ let s = String::new();
+ // Lint, both branches immutably borrow `s`.
+ let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
+
+ let s = String::new();
+ // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+ let _ = Some(0).map_or(1, |x| {
+ let s = s;
+ s.len() + x
+ });
+
+ let s = Some(String::new());
+ // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+ let _ = if let Some(x) = &s {
+ x.len()
+ } else {
+ let _s = s;
+ 10
+ };
+
+ let mut s = Some(String::new());
+ // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s`
+ let _ = if let Some(x) = &mut s {
+ x.push_str("test");
+ x.len()
+ } else {
+ let _s = &s;
+ 10
+ };
+
+ async fn _f1(x: u32) -> u32 {
+ x
+ }
+
+ async fn _f2() {
+ // Don't lint. `await` can't be moved into a closure.
+ let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+ }
+}
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+ if let Some(x) = string {
+ (true, x)
+ } else {
+ (false, "hello")
+ }
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+ if string.is_none() {
+ None
+ } else if let Some(x) = string {
+ Some((true, x))
+ } else {
+ Some((false, ""))
+ }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+ let _ = if let Some(s) = *string { s.len() } else { 0 };
+ let _ = if let Some(s) = &num { s } else { &0 };
+ let _ = if let Some(s) = &mut num {
+ *s += 1;
+ s
+ } else {
+ &mut 0
+ };
+ let _ = if let Some(ref s) = num { s } else { &0 };
+ let _ = if let Some(mut s) = num {
+ s += 1;
+ s
+ } else {
+ 0
+ };
+ let _ = if let Some(ref mut s) = num {
+ *s += 1;
+ s
+ } else {
+ &mut 0
+ };
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+ if let Some(x) = arg {
+ let y = x * x;
+ y * y
+ } else {
+ 13
+ }
+}
+
+fn impure_else(arg: Option<i32>) {
+ let side_effect = || {
+ println!("return 1");
+ 1
+ };
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ // map_or_else must be suggested
+ side_effect()
+ };
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+ let _ = if let Some(x) = arg {
+ x * x * x * x
+ } else {
+ let mut y = 1;
+ y = (y + 2 / y) / 2;
+ y = (y + 2 / y) / 2;
+ y
+ };
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+ let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+ for _ in 0..10 {
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ continue;
+ };
+ }
+ let _ = if let Some(x) = arg {
+ return x;
+ } else {
+ 5
+ };
+ 7
+}
+
+fn main() {
+ let optional = Some(5);
+ let _ = if let Some(x) = optional { x + 2 } else { 5 };
+ let _ = bad1(None);
+ let _ = else_if_option(None);
+ unop_bad(&None, None);
+ let _ = longer_body(None);
+ test_map_or_else(None);
+ let _ = negative_tests(None);
+ let _ = impure_else(None);
+
+ let _ = if let Some(x) = Some(0) {
+ loop {
+ if x == 0 {
+ break x;
+ }
+ }
+ } else {
+ 0
+ };
+
+ // #7576
+ const fn _f(x: Option<u32>) -> u32 {
+ // Don't lint, `map_or` isn't const
+ if let Some(x) = x { x } else { 10 }
+ }
+
+ // #5822
+ let s = String::new();
+ // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ s.len()
+ };
+
+ let s = String::new();
+ // Lint, both branches immutably borrow `s`.
+ let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
+
+ let s = String::new();
+ // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ 1
+ };
+
+ let s = Some(String::new());
+ // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+ let _ = if let Some(x) = &s {
+ x.len()
+ } else {
+ let _s = s;
+ 10
+ };
+
+ let mut s = Some(String::new());
+ // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s`
+ let _ = if let Some(x) = &mut s {
+ x.push_str("test");
+ x.len()
+ } else {
+ let _s = &s;
+ 10
+ };
+
+ async fn _f1(x: u32) -> u32 {
+ x
+ }
+
+ async fn _f2() {
+ // Don't lint. `await` can't be moved into a closure.
+ let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+ }
+}
--- /dev/null
- --> $DIR/option_if_let_else.rs:7:5
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:25:13
++ --> $DIR/option_if_let_else.rs:6:5
+ |
+LL | / if let Some(x) = string {
+LL | | (true, x)
+LL | | } else {
+LL | | (false, "hello")
+LL | | }
+ | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))`
+ |
+ = note: `-D clippy::option-if-let-else` implied by `-D warnings`
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:26:13
++ --> $DIR/option_if_let_else.rs:24:13
+ |
+LL | let _ = if let Some(s) = *string { s.len() } else { 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:27:13
++ --> $DIR/option_if_let_else.rs:25:13
+ |
+LL | let _ = if let Some(s) = &num { s } else { &0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:33:13
++ --> $DIR/option_if_let_else.rs:26:13
+ |
+LL | let _ = if let Some(s) = &mut num {
+ | _____________^
+LL | | *s += 1;
+LL | | s
+LL | | } else {
+LL | | &mut 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = num.as_mut().map_or(&mut 0, |s| {
+LL + *s += 1;
+LL + s
+LL ~ });
+ |
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:34:13
++ --> $DIR/option_if_let_else.rs:32:13
+ |
+LL | let _ = if let Some(ref s) = num { s } else { &0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:40:13
++ --> $DIR/option_if_let_else.rs:33:13
+ |
+LL | let _ = if let Some(mut s) = num {
+ | _____________^
+LL | | s += 1;
+LL | | s
+LL | | } else {
+LL | | 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = num.map_or(0, |mut s| {
+LL + s += 1;
+LL + s
+LL ~ });
+ |
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:49:5
++ --> $DIR/option_if_let_else.rs:39:13
+ |
+LL | let _ = if let Some(ref mut s) = num {
+ | _____________^
+LL | | *s += 1;
+LL | | s
+LL | | } else {
+LL | | &mut 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = num.as_mut().map_or(&mut 0, |s| {
+LL + *s += 1;
+LL + s
+LL ~ });
+ |
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:62:13
++ --> $DIR/option_if_let_else.rs:48:5
+ |
+LL | / if let Some(x) = arg {
+LL | | let y = x * x;
+LL | | y * y
+LL | | } else {
+LL | | 13
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ arg.map_or(13, |x| {
+LL + let y = x * x;
+LL + y * y
+LL + })
+ |
+
+error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:71:13
++ --> $DIR/option_if_let_else.rs:61:13
+ |
+LL | let _ = if let Some(x) = arg {
+ | _____________^
+LL | | x
+LL | | } else {
+LL | | // map_or_else must be suggested
+LL | | side_effect()
+LL | | };
+ | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
+
+error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:100:13
++ --> $DIR/option_if_let_else.rs:70:13
+ |
+LL | let _ = if let Some(x) = arg {
+ | _____________^
+LL | | x * x * x * x
+LL | | } else {
+LL | | let mut y = 1;
+... |
+LL | | y
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = arg.map_or_else(|| {
+LL + let mut y = 1;
+LL + y = (y + 2 / y) / 2;
+LL + y = (y + 2 / y) / 2;
+LL + y
+LL ~ }, |x| x * x * x * x);
+ |
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:109:13
++ --> $DIR/option_if_let_else.rs:99:13
+ |
+LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
+
+error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:137:13
++ --> $DIR/option_if_let_else.rs:108:13
+ |
+LL | let _ = if let Some(x) = Some(0) {
+ | _____________^
+LL | | loop {
+LL | | if x == 0 {
+LL | | break x;
+... |
+LL | | 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = Some(0).map_or(0, |x| loop {
+LL + if x == 0 {
+LL + break x;
+LL + }
+LL ~ });
+ |
+
+error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:141:13
++ --> $DIR/option_if_let_else.rs:136:13
+ |
+LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)`
+
+error: use Option::map_or instead of an if let/else
++ --> $DIR/option_if_let_else.rs:140:13
+ |
+LL | let _ = if let Some(x) = Some(0) {
+ | _____________^
+LL | | let s = s;
+LL | | s.len() + x
+LL | | } else {
+LL | | 1
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = Some(0).map_or(1, |x| {
+LL + let s = s;
+LL + s.len() + x
+LL ~ });
+ |
+
+error: aborting due to 14 previous errors
+
--- /dev/null
-
+#![warn(clippy::panic_in_result_fn)]
+#![allow(clippy::unnecessary_wraps)]
+struct A;
+
+impl A {
+ fn result_with_panic() -> Result<bool, String> // should emit lint
+ {
+ panic!("error");
+ }
+
+ fn result_with_unimplemented() -> Result<bool, String> // should emit lint
+ {
+ unimplemented!();
+ }
+
+ fn result_with_unreachable() -> Result<bool, String> // should emit lint
+ {
+ unreachable!();
+ }
+
+ fn result_with_todo() -> Result<bool, String> // should emit lint
+ {
+ todo!("Finish this");
+ }
+
+ fn other_with_panic() // should not emit lint
+ {
+ panic!("");
+ }
+
+ fn other_with_unreachable() // should not emit lint
+ {
+ unreachable!();
+ }
+
+ fn other_with_unimplemented() // should not emit lint
+ {
+ unimplemented!();
+ }
+
+ fn other_with_todo() // should not emit lint
+ {
+ todo!("finish this")
+ }
+
+ fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
+ {
+ Ok(true)
+ }
+}
+
+fn function_result_with_panic() -> Result<bool, String> // should emit lint
+{
+ panic!("error");
+}
+
+fn todo() {
+ println!("something");
+}
+
+fn function_result_with_custom_todo() -> Result<bool, String> // should not emit lint
+{
+ todo();
+ Ok(true)
+}
+
+fn main() -> Result<(), String> {
+ todo!("finish main method");
+ Ok(())
+}
--- /dev/null
- --> $DIR/panic_in_result_fn.rs:7:5
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn.rs:9:9
++ --> $DIR/panic_in_result_fn.rs:6:5
+ |
+LL | / fn result_with_panic() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | panic!("error");
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
+ = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/panic_in_result_fn.rs:8:9
+ |
+LL | panic!("error");
+ | ^^^^^^^^^^^^^^^
- --> $DIR/panic_in_result_fn.rs:12:5
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn.rs:14:9
++ --> $DIR/panic_in_result_fn.rs:11:5
+ |
+LL | / fn result_with_unimplemented() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+ |
+ = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
- --> $DIR/panic_in_result_fn.rs:17:5
++ --> $DIR/panic_in_result_fn.rs:13:9
+ |
+LL | unimplemented!();
+ | ^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn.rs:19:9
++ --> $DIR/panic_in_result_fn.rs:16:5
+ |
+LL | / fn result_with_unreachable() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | unreachable!();
+LL | | }
+ | |_____^
+ |
+ = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
- --> $DIR/panic_in_result_fn.rs:22:5
++ --> $DIR/panic_in_result_fn.rs:18:9
+ |
+LL | unreachable!();
+ | ^^^^^^^^^^^^^^
+ = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn.rs:24:9
++ --> $DIR/panic_in_result_fn.rs:21:5
+ |
+LL | / fn result_with_todo() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | todo!("Finish this");
+LL | | }
+ | |_____^
+ |
+ = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
- --> $DIR/panic_in_result_fn.rs:53:1
++ --> $DIR/panic_in_result_fn.rs:23:9
+ |
+LL | todo!("Finish this");
+ | ^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn.rs:55:5
++ --> $DIR/panic_in_result_fn.rs:52:1
+ |
+LL | / fn function_result_with_panic() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | panic!("error");
+LL | | }
+ | |_^
+ |
+ = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
- = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
++ --> $DIR/panic_in_result_fn.rs:54:5
+ |
+LL | panic!("error");
+ | ^^^^^^^^^^^^^^^
- --> $DIR/panic_in_result_fn.rs:68:1
++ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
- --> $DIR/panic_in_result_fn.rs:69:5
++ --> $DIR/panic_in_result_fn.rs:67:1
+ |
+LL | / fn main() -> Result<(), String> {
+LL | | todo!("finish main method");
+LL | | Ok(())
+LL | | }
+ | |_^
+ |
+ = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
++ --> $DIR/panic_in_result_fn.rs:68:5
+ |
+LL | todo!("finish main method");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
+
--- /dev/null
- #![allow(
- unused,
- clippy::many_single_char_names,
- clippy::redundant_clone,
- clippy::if_then_panic
- )]
++#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#![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();
+ }
+}
++
++// No error for types behind an alias (#7699)
++type A = Vec<u8>;
++fn aliased(a: &A) {}
--- /dev/null
- --> $DIR/ptr_arg.rs:12:14
+error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:21:14
++ --> $DIR/ptr_arg.rs:7: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:30:15
++ --> $DIR/ptr_arg.rs:16: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:43:18
++ --> $DIR/ptr_arg.rs:25: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:56:14
++ --> $DIR/ptr_arg.rs:38: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:65:18
++ --> $DIR/ptr_arg.rs:51: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:73:19
++ --> $DIR/ptr_arg.rs:60: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:81:44
++ --> $DIR/ptr_arg.rs:68: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:95:25
++ --> $DIR/ptr_arg.rs:76: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:148:21
++ --> $DIR/ptr_arg.rs:90: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:153:23
++ --> $DIR/ptr_arg.rs:143: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:158:21
++ --> $DIR/ptr_arg.rs:148: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:153: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
- fn result_func(x: Result<i32, &str>) -> Result<i32, &str> {
+// run-rustfix
+#![allow(unreachable_code)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn some_func(a: Option<u32>) -> Option<u32> {
+ a?;
+
+ a
+}
+
+fn some_other_func(a: Option<u32>) -> Option<u32> {
+ if a.is_none() {
+ return None;
+ } else {
+ return Some(0);
+ }
+ unreachable!()
+}
+
+pub enum SeemsOption<T> {
+ Some(T),
+ None,
+}
+
+impl<T> SeemsOption<T> {
+ pub fn is_none(&self) -> bool {
+ match *self {
+ SeemsOption::None => true,
+ SeemsOption::Some(_) => false,
+ }
+ }
+}
+
+fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
+ if a.is_none() {
+ return SeemsOption::None;
+ }
+
+ a
+}
+
+pub struct CopyStruct {
+ pub opt: Option<u32>,
+}
+
+impl CopyStruct {
+ #[rustfmt::skip]
+ pub fn func(&self) -> Option<u32> {
+ (self.opt)?;
+
+ self.opt?;
+
+ let _ = Some(self.opt?);
+
+ let _ = self.opt?;
+
+ self.opt
+ }
+}
+
+#[derive(Clone)]
+pub struct MoveStruct {
+ pub opt: Option<Vec<u32>>,
+}
+
+impl MoveStruct {
+ pub fn ref_func(&self) -> Option<Vec<u32>> {
+ self.opt.as_ref()?;
+
+ self.opt.clone()
+ }
+
+ pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
+ self.opt.as_ref()?;
+
+ self.opt
+ }
+
+ pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
+ self.opt.as_ref()?;
+ Some(Vec::new())
+ }
+
+ pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
+ let v: &Vec<_> = self.opt.as_ref()?;
+
+ Some(v.clone())
+ }
+
+ pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
+ let v = self.opt?;
+
+ Some(v)
+ }
+}
+
+fn func() -> Option<i32> {
+ fn f() -> Option<String> {
+ Some(String::new())
+ }
+
+ f()?;
+
+ Some(0)
+}
+
- return Err("some error");
++fn func_returning_result() -> Result<i32, i32> {
++ Ok(1)
++}
++
++fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
+ let _ = x?;
+
+ x?;
+
+ // No warning
+ let y = if let Ok(x) = x {
+ x
+ } else {
++ return Err(0);
++ };
++
++ // issue #7859
++ // no warning
++ let _ = if let Ok(x) = func_returning_result() {
++ x
++ } else {
++ return Err(0);
+ };
+
++ // no warning
++ if func_returning_result().is_err() {
++ return func_returning_result();
++ }
++
+ Ok(y)
+}
+
+fn main() {
+ some_func(Some(42));
+ some_func(None);
+ some_other_func(Some(42));
+
+ let copy_struct = CopyStruct { opt: Some(54) };
+ copy_struct.func();
+
+ let move_struct = MoveStruct {
+ opt: Some(vec![42, 1337]),
+ };
+ move_struct.ref_func();
+ move_struct.clone().mov_func_reuse();
+ move_struct.mov_func_no_use();
+
+ let so = SeemsOption::Some(45);
+ returns_something_similar_to_option(so);
+
+ func();
+
+ let _ = result_func(Ok(42));
+}
--- /dev/null
- fn result_func(x: Result<i32, &str>) -> Result<i32, &str> {
+// run-rustfix
+#![allow(unreachable_code)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn some_func(a: Option<u32>) -> Option<u32> {
+ if a.is_none() {
+ return None;
+ }
+
+ a
+}
+
+fn some_other_func(a: Option<u32>) -> Option<u32> {
+ if a.is_none() {
+ return None;
+ } else {
+ return Some(0);
+ }
+ unreachable!()
+}
+
+pub enum SeemsOption<T> {
+ Some(T),
+ None,
+}
+
+impl<T> SeemsOption<T> {
+ pub fn is_none(&self) -> bool {
+ match *self {
+ SeemsOption::None => true,
+ SeemsOption::Some(_) => false,
+ }
+ }
+}
+
+fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
+ if a.is_none() {
+ return SeemsOption::None;
+ }
+
+ a
+}
+
+pub struct CopyStruct {
+ pub opt: Option<u32>,
+}
+
+impl CopyStruct {
+ #[rustfmt::skip]
+ pub fn func(&self) -> Option<u32> {
+ if (self.opt).is_none() {
+ return None;
+ }
+
+ if self.opt.is_none() {
+ return None
+ }
+
+ let _ = if self.opt.is_none() {
+ return None;
+ } else {
+ self.opt
+ };
+
+ let _ = if let Some(x) = self.opt {
+ x
+ } else {
+ return None;
+ };
+
+ self.opt
+ }
+}
+
+#[derive(Clone)]
+pub struct MoveStruct {
+ pub opt: Option<Vec<u32>>,
+}
+
+impl MoveStruct {
+ pub fn ref_func(&self) -> Option<Vec<u32>> {
+ if self.opt.is_none() {
+ return None;
+ }
+
+ self.opt.clone()
+ }
+
+ pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
+ if self.opt.is_none() {
+ return None;
+ }
+
+ self.opt
+ }
+
+ pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
+ if self.opt.is_none() {
+ return None;
+ }
+ Some(Vec::new())
+ }
+
+ pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
+ let v: &Vec<_> = if let Some(ref v) = self.opt {
+ v
+ } else {
+ return None;
+ };
+
+ Some(v.clone())
+ }
+
+ pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
+ let v = if let Some(v) = self.opt {
+ v
+ } else {
+ return None;
+ };
+
+ Some(v)
+ }
+}
+
+fn func() -> Option<i32> {
+ fn f() -> Option<String> {
+ Some(String::new())
+ }
+
+ if f().is_none() {
+ return None;
+ }
+
+ Some(0)
+}
+
- return Err("some error");
++fn func_returning_result() -> Result<i32, i32> {
++ Ok(1)
++}
++
++fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
+ let _ = if let Ok(x) = x { x } else { return x };
+
+ if x.is_err() {
+ return x;
+ }
+
+ // No warning
+ let y = if let Ok(x) = x {
+ x
+ } else {
++ return Err(0);
++ };
++
++ // issue #7859
++ // no warning
++ let _ = if let Ok(x) = func_returning_result() {
++ x
++ } else {
++ return Err(0);
+ };
+
++ // no warning
++ if func_returning_result().is_err() {
++ return func_returning_result();
++ }
++
+ Ok(y)
+}
+
+fn main() {
+ some_func(Some(42));
+ some_func(None);
+ some_other_func(Some(42));
+
+ let copy_struct = CopyStruct { opt: Some(54) };
+ copy_struct.func();
+
+ let move_struct = MoveStruct {
+ opt: Some(vec![42, 1337]),
+ };
+ move_struct.ref_func();
+ move_struct.clone().mov_func_reuse();
+ move_struct.mov_func_no_use();
+
+ let so = SeemsOption::Some(45);
+ returns_something_similar_to_option(so);
+
+ func();
+
+ let _ = result_func(Ok(42));
+}
--- /dev/null
- --> $DIR/question_mark.rs:138:13
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:6:5
+ |
+LL | / if a.is_none() {
+LL | | return None;
+LL | | }
+ | |_____^ help: replace it with: `a?;`
+ |
+ = note: `-D clippy::question-mark` implied by `-D warnings`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:51:9
+ |
+LL | / if (self.opt).is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `(self.opt)?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:55:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None
+LL | | }
+ | |_________^ help: replace it with: `self.opt?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:59:17
+ |
+LL | let _ = if self.opt.is_none() {
+ | _________________^
+LL | | return None;
+LL | | } else {
+LL | | self.opt
+LL | | };
+ | |_________^ help: replace it with: `Some(self.opt?)`
+
+error: this if-let-else may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:65:17
+ |
+LL | let _ = if let Some(x) = self.opt {
+ | _________________^
+LL | | x
+LL | | } else {
+LL | | return None;
+LL | | };
+ | |_________^ help: replace it with: `self.opt?`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:82:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `self.opt.as_ref()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:90:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `self.opt.as_ref()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:98:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `self.opt.as_ref()?;`
+
+error: this if-let-else may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:105:26
+ |
+LL | let v: &Vec<_> = if let Some(ref v) = self.opt {
+ | __________________________^
+LL | | v
+LL | | } else {
+LL | | return None;
+LL | | };
+ | |_________^ help: replace it with: `self.opt.as_ref()?`
+
+error: this if-let-else may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:115:17
+ |
+LL | let v = if let Some(v) = self.opt {
+ | _________________^
+LL | | v
+LL | | } else {
+LL | | return None;
+LL | | };
+ | |_________^ help: replace it with: `self.opt?`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:130:5
+ |
+LL | / if f().is_none() {
+LL | | return None;
+LL | | }
+ | |_____^ help: replace it with: `f()?;`
+
+error: this if-let-else may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:140:5
++ --> $DIR/question_mark.rs:142:13
+ |
+LL | let _ = if let Ok(x) = x { x } else { return x };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
+
+error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:144:5
+ |
+LL | / if x.is_err() {
+LL | | return x;
+LL | | }
+ | |_____^ help: replace it with: `x?;`
+
+error: aborting due to 13 previous errors
+
--- /dev/null
- foo(&x.clone(), move || {
+// run-rustfix
+// rustfix-only-machine-applicable
+
+#![allow(clippy::implicit_clone)]
+use std::ffi::OsString;
+use std::path::Path;
+
+fn main() {
+ let _s = ["lorem", "ipsum"].join(" ");
+
+ let s = String::from("foo");
+ let _s = s;
+
+ let s = String::from("foo");
+ let _s = s;
+
+ let s = String::from("foo");
+ let _s = s;
+
+ let _s = Path::new("/a/b/").join("c");
+
+ let _s = Path::new("/a/b/").join("c");
+
+ let _s = OsString::new();
+
+ let _s = OsString::new();
+
+ // Check that lint level works
+ #[allow(clippy::redundant_clone)]
+ let _s = String::new().to_string();
+
+ let tup = (String::from("foo"),);
+ let _t = tup.0;
+
+ let tup_ref = &(String::from("foo"),);
+ let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
+
+ {
+ let x = String::new();
+ let y = &x;
+
+ let _x = x.clone(); // ok; `x` is borrowed by `y`
+
+ let _ = y.len();
+ }
+
+ let x = (String::new(),);
+ let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x`
+
+ with_branch(Alpha, true);
+ cannot_double_move(Alpha);
+ cannot_move_from_type_with_drop();
+ borrower_propagation();
+ not_consumed();
+ issue_5405();
+ manually_drop();
+ clone_then_move_cloned();
+ hashmap_neg();
+ false_negative_5707();
+}
+
+#[derive(Clone)]
+struct Alpha;
+fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
+ if b { (a.clone(), a) } else { (Alpha, a) }
+}
+
+fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) {
+ (a.clone(), a)
+}
+
+struct TypeWithDrop {
+ x: String,
+}
+
+impl Drop for TypeWithDrop {
+ fn drop(&mut self) {}
+}
+
+fn cannot_move_from_type_with_drop() -> String {
+ let s = TypeWithDrop { x: String::new() };
+ s.x.clone() // removing this `clone()` summons E0509
+}
+
+fn borrower_propagation() {
+ let s = String::new();
+ let t = String::new();
+
+ {
+ fn b() -> bool {
+ unimplemented!()
+ }
+ let _u = if b() { &s } else { &t };
+
+ // ok; `s` and `t` are possibly borrowed
+ let _s = s.clone();
+ let _t = t.clone();
+ }
+
+ {
+ let _u = || s.len();
+ let _v = [&t; 32];
+ let _s = s.clone(); // ok
+ let _t = t.clone(); // ok
+ }
+
+ {
+ let _u = {
+ let u = Some(&s);
+ let _ = s.clone(); // ok
+ u
+ };
+ let _s = s.clone(); // ok
+ }
+
+ {
+ use std::convert::identity as id;
+ let _u = id(id(&s));
+ let _s = s.clone(); // ok, `u` borrows `s`
+ }
+
+ let _s = s;
+ let _t = t;
+
+ #[derive(Clone)]
+ struct Foo {
+ x: usize,
+ }
+
+ {
+ let f = Foo { x: 123 };
+ let _x = Some(f.x);
+ let _f = f;
+ }
+
+ {
+ let f = Foo { x: 123 };
+ let _x = &f.x;
+ let _f = f.clone(); // ok
+ }
+}
+
+fn not_consumed() {
+ let x = std::path::PathBuf::from("home");
+ let y = x.join("matthias");
+ // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is
+ // redundant. (It also does not consume the PathBuf)
+
+ println!("x: {:?}, y: {:?}", x, y);
+
+ let mut s = String::new();
+ s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
+ s.push_str("bar");
+ assert_eq!(s, "bar");
+
+ let t = Some(s);
+ // OK
+ if let Some(x) = t.clone() {
+ println!("{}", x);
+ }
+ if let Some(x) = t {
+ println!("{}", x);
+ }
+}
+
+#[allow(clippy::clone_on_copy)]
+fn issue_5405() {
+ let a: [String; 1] = [String::from("foo")];
+ let _b: String = a[0].clone();
+
+ let c: [usize; 2] = [2, 3];
+ let _d: usize = c[1].clone();
+}
+
+fn manually_drop() {
+ use std::mem::ManuallyDrop;
+ use std::sync::Arc;
+
+ let a = ManuallyDrop::new(Arc::new("Hello!".to_owned()));
+ let _ = a.clone(); // OK
+
+ let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a));
+ unsafe {
+ Arc::from_raw(p);
+ Arc::from_raw(p);
+ }
+}
+
+fn clone_then_move_cloned() {
+ // issue #5973
+ let x = Some(String::new());
+ // ok, x is moved while the clone is in use.
+ assert_eq!(x.clone(), None, "not equal {}", x.unwrap());
+
+ // issue #5595
+ fn foo<F: Fn()>(_: &Alpha, _: F) {}
+ let x = Alpha;
+ // ok, data is moved while the clone is in use.
++ foo(&x, move || {
+ let _ = x;
+ });
+
+ // issue #6998
+ struct S(String);
+ impl S {
+ fn m(&mut self) {}
+ }
+ let mut x = S(String::new());
+ x.0.clone().chars().for_each(|_| x.m());
+}
+
+fn hashmap_neg() {
+ // issue 5707
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ let p = PathBuf::from("/");
+
+ let mut h: HashMap<&str, &str> = HashMap::new();
+ h.insert("orig-p", p.to_str().unwrap());
+
+ let mut q = p.clone();
+ q.push("foo");
+
+ println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+ fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+ let x = Alpha;
+ let mut y = Alpha;
+ foo(&x, &mut y);
+ let _z = x.clone(); // pr 7346 can't lint on `x`
+ drop(y);
+}
--- /dev/null
- error: aborting due to 14 previous errors
+error: redundant clone
+ --> $DIR/redundant_clone.rs:9:42
+ |
+LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
+ | ^^^^^^^^^^^^ help: remove this
+ |
+ = note: `-D clippy::redundant-clone` implied by `-D warnings`
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:9:14
+ |
+LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:12:15
+ |
+LL | let _s = s.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:12:14
+ |
+LL | let _s = s.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:15:15
+ |
+LL | let _s = s.to_string();
+ | ^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:15:14
+ |
+LL | let _s = s.to_string();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:18:15
+ |
+LL | let _s = s.to_owned();
+ | ^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:18:14
+ |
+LL | let _s = s.to_owned();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:20:42
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_owned();
+ | ^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:20:14
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_owned();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:22:42
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
+ | ^^^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:22:14
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:24:29
+ |
+LL | let _s = OsString::new().to_owned();
+ | ^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:24:14
+ |
+LL | let _s = OsString::new().to_owned();
+ | ^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:26:29
+ |
+LL | let _s = OsString::new().to_os_string();
+ | ^^^^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:26:14
+ |
+LL | let _s = OsString::new().to_os_string();
+ | ^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:33:19
+ |
+LL | let _t = tup.0.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:33:14
+ |
+LL | let _t = tup.0.clone();
+ | ^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:65:25
+ |
+LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:65:24
+ |
+LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:122:15
+ |
+LL | let _s = s.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:122:14
+ |
+LL | let _s = s.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:123:15
+ |
+LL | let _t = t.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:123:14
+ |
+LL | let _t = t.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:133:19
+ |
+LL | let _f = f.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:133:18
+ |
+LL | let _f = f.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:145:14
+ |
+LL | let y = x.clone().join("matthias");
+ | ^^^^^^^^ help: remove this
+ |
+note: cloned value is neither consumed nor mutated
+ --> $DIR/redundant_clone.rs:145:13
+ |
+LL | let y = x.clone().join("matthias");
+ | ^^^^^^^^^
+
++error: redundant clone
++ --> $DIR/redundant_clone.rs:199:11
++ |
++LL | foo(&x.clone(), move || {
++ | ^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:199:10
++ |
++LL | foo(&x.clone(), move || {
++ | ^
++
++error: aborting due to 15 previous errors
+
--- /dev/null
- // edition:2018
+// FIXME: run-rustfix waiting on multi-span suggestions
+
+#![warn(clippy::ref_binding_to_reference)]
+#![allow(clippy::needless_borrowed_reference)]
+
+fn f1(_: &str) {}
+macro_rules! m2 {
+ ($e:expr) => {
+ f1(*$e)
+ };
+}
+macro_rules! m3 {
+ ($i:ident) => {
+ Some(ref $i)
+ };
+}
+
+#[allow(dead_code)]
+fn main() {
+ let x = String::new();
+
+ // Ok, the pattern is from a macro
+ let _: &&String = match Some(&x) {
+ m3!(x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &&String = match Some(&x) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &&String = match Some(&x) {
+ Some(ref x) => {
+ f1(x);
+ f1(*x);
+ x
+ },
+ None => return,
+ };
+
+ // Err, reference to a &String
+ match Some(&x) {
+ Some(ref x) => m2!(x),
+ None => return,
+ }
+
+ // Err, reference to a &String
+ let _ = |&ref x: &&String| {
+ let _: &&String = x;
+ };
+}
+
+// Err, reference to a &String
+fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ let _: &&String = x;
+ *x
+}
+
+trait T1 {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &&String = x;
+ }
+}
+
+struct S;
+impl T1 for S {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &&String = x;
+ }
+}
--- /dev/null
- --> $DIR/ref_binding_to_reference.rs:31:14
+error: this pattern creates a reference to a reference
- --> $DIR/ref_binding_to_reference.rs:37:14
++ --> $DIR/ref_binding_to_reference.rs:30:14
+ |
+LL | Some(ref x) => x,
+ | ^^^^^
+ |
+ = note: `-D clippy::ref-binding-to-reference` implied by `-D warnings`
+help: try this
+ |
+LL | Some(x) => &x,
+ | ~ ~~
+
+error: this pattern creates a reference to a reference
- --> $DIR/ref_binding_to_reference.rs:47:14
++ --> $DIR/ref_binding_to_reference.rs:36:14
+ |
+LL | Some(ref x) => {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ Some(x) => {
+LL | f1(x);
+LL ~ f1(x);
+LL ~ &x
+ |
+
+error: this pattern creates a reference to a reference
- --> $DIR/ref_binding_to_reference.rs:52:15
++ --> $DIR/ref_binding_to_reference.rs:46:14
+ |
+LL | Some(ref x) => m2!(x),
+ | ^^^^^
+ |
+help: try this
+ |
+LL | Some(x) => m2!(&x),
+ | ~ ~~
+
+error: this pattern creates a reference to a reference
- --> $DIR/ref_binding_to_reference.rs:58:12
++ --> $DIR/ref_binding_to_reference.rs:51:15
+ |
+LL | let _ = |&ref x: &&String| {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ let _ = |&x: &&String| {
+LL ~ let _: &&String = &x;
+ |
+
+error: this pattern creates a reference to a reference
- --> $DIR/ref_binding_to_reference.rs:65:11
++ --> $DIR/ref_binding_to_reference.rs:57:12
+ |
+LL | fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f2<'a>(&x: &&'a String) -> &'a String {
+LL ~ let _: &&String = &x;
+LL ~ x
+ |
+
+error: this pattern creates a reference to a reference
- --> $DIR/ref_binding_to_reference.rs:73:11
++ --> $DIR/ref_binding_to_reference.rs:64:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f(&x: &&String) {
+LL ~ let _: &&String = &x;
+ |
+
+error: this pattern creates a reference to a reference
++ --> $DIR/ref_binding_to_reference.rs:72:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f(&x: &&String) {
+LL ~ let _: &&String = &x;
+ |
+
+error: aborting due to 7 previous errors
+
--- /dev/null
- #[warn(clippy::module_name_repetitions)]
+//! Test for Clippy lint renames.
+// run-rustfix
+
+#![allow(dead_code)]
+// allow the new lint name here, to test if the new name works
+#![allow(clippy::module_name_repetitions)]
+#![allow(clippy::new_without_default)]
+#![allow(clippy::redundant_static_lifetimes)]
++#![allow(clippy::bind_instead_of_map)]
++#![allow(clippy::box_collection)]
++#![allow(clippy::blocks_in_if_conditions)]
++#![allow(clippy::map_unwrap_or)]
++#![allow(clippy::unwrap_used)]
++#![allow(clippy::expect_used)]
++#![allow(clippy::for_loops_over_fallibles)]
++#![allow(clippy::useless_conversion)]
++#![allow(clippy::invisible_characters)]
++#![allow(clippy::single_char_add_str)]
++#![allow(clippy::match_result_ok)]
++// uplifted lints
++#![allow(invalid_value)]
++#![allow(array_into_iter)]
++#![allow(unused_labels)]
++#![allow(drop_bounds)]
++#![allow(temporary_cstring_as_ptr)]
++#![allow(non_fmt_panics)]
++#![allow(unknown_lints)]
++#![allow(invalid_atomic_ordering)]
++#![allow(enum_intrinsics_non_enums)]
+// warn for the old lint name here, to test if the renaming worked
++#![warn(clippy::module_name_repetitions)]
++#![warn(clippy::new_without_default)]
++#![warn(clippy::redundant_static_lifetimes)]
+#![warn(clippy::cognitive_complexity)]
++#![warn(clippy::bind_instead_of_map)]
++#![warn(clippy::box_collection)]
++#![warn(clippy::blocks_in_if_conditions)]
++#![warn(clippy::blocks_in_if_conditions)]
++#![warn(clippy::map_unwrap_or)]
++#![warn(clippy::map_unwrap_or)]
++#![warn(clippy::map_unwrap_or)]
++#![warn(clippy::unwrap_used)]
++#![warn(clippy::unwrap_used)]
++#![warn(clippy::expect_used)]
++#![warn(clippy::expect_used)]
++#![warn(clippy::for_loops_over_fallibles)]
++#![warn(clippy::for_loops_over_fallibles)]
++#![warn(clippy::useless_conversion)]
++#![warn(clippy::invisible_characters)]
++#![warn(clippy::single_char_add_str)]
++#![warn(clippy::match_result_ok)]
++// uplifted lints
++#![warn(invalid_value)]
++#![warn(array_into_iter)]
++#![warn(unused_labels)]
++#![warn(drop_bounds)]
++#![warn(temporary_cstring_as_ptr)]
++#![warn(non_fmt_panics)]
++#![warn(unknown_lints)]
++#![warn(invalid_atomic_ordering)]
+#![warn(enum_intrinsics_non_enums)]
+
-
- #[warn(clippy::new_without_default)]
- struct Foo;
-
- #[warn(clippy::redundant_static_lifetimes)]
- fn foo() {}
+fn main() {}
--- /dev/null
- #[warn(clippy::stutter)]
+//! Test for Clippy lint renames.
+// run-rustfix
+
+#![allow(dead_code)]
+// allow the new lint name here, to test if the new name works
+#![allow(clippy::module_name_repetitions)]
+#![allow(clippy::new_without_default)]
+#![allow(clippy::redundant_static_lifetimes)]
++#![allow(clippy::bind_instead_of_map)]
++#![allow(clippy::box_collection)]
++#![allow(clippy::blocks_in_if_conditions)]
++#![allow(clippy::map_unwrap_or)]
++#![allow(clippy::unwrap_used)]
++#![allow(clippy::expect_used)]
++#![allow(clippy::for_loops_over_fallibles)]
++#![allow(clippy::useless_conversion)]
++#![allow(clippy::invisible_characters)]
++#![allow(clippy::single_char_add_str)]
++#![allow(clippy::match_result_ok)]
++// uplifted lints
++#![allow(invalid_value)]
++#![allow(array_into_iter)]
++#![allow(unused_labels)]
++#![allow(drop_bounds)]
++#![allow(temporary_cstring_as_ptr)]
++#![allow(non_fmt_panics)]
++#![allow(unknown_lints)]
++#![allow(invalid_atomic_ordering)]
++#![allow(enum_intrinsics_non_enums)]
+// warn for the old lint name here, to test if the renaming worked
++#![warn(clippy::stutter)]
++#![warn(clippy::new_without_default_derive)]
++#![warn(clippy::const_static_lifetime)]
+#![warn(clippy::cyclomatic_complexity)]
++#![warn(clippy::option_and_then_some)]
++#![warn(clippy::box_vec)]
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![warn(clippy::option_map_unwrap_or)]
++#![warn(clippy::option_map_unwrap_or_else)]
++#![warn(clippy::result_map_unwrap_or_else)]
++#![warn(clippy::option_unwrap_used)]
++#![warn(clippy::result_unwrap_used)]
++#![warn(clippy::option_expect_used)]
++#![warn(clippy::result_expect_used)]
++#![warn(clippy::for_loop_over_option)]
++#![warn(clippy::for_loop_over_result)]
++#![warn(clippy::identity_conversion)]
++#![warn(clippy::zero_width_space)]
++#![warn(clippy::single_char_push_str)]
++#![warn(clippy::if_let_some_result)]
++// uplifted lints
++#![warn(clippy::invalid_ref)]
++#![warn(clippy::into_iter_on_array)]
++#![warn(clippy::unused_label)]
++#![warn(clippy::drop_bounds)]
++#![warn(clippy::temporary_cstring_as_ptr)]
++#![warn(clippy::panic_params)]
++#![warn(clippy::unknown_clippy_lints)]
++#![warn(clippy::invalid_atomic_ordering)]
+#![warn(clippy::mem_discriminant_non_enum)]
+
-
- #[warn(clippy::new_without_default_derive)]
- struct Foo;
-
- #[warn(clippy::const_static_lifetime)]
- fn foo() {}
+fn main() {}
--- /dev/null
- --> $DIR/rename.rs:10:9
++error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
++ --> $DIR/rename.rs:31:9
++ |
++LL | #![warn(clippy::stutter)]
++ | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
++ |
++ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++
++error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
++ --> $DIR/rename.rs:32:9
++ |
++LL | #![warn(clippy::new_without_default_derive)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
++
++error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
++ --> $DIR/rename.rs:33:9
++ |
++LL | #![warn(clippy::const_static_lifetime)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
++
+error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
- = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++ --> $DIR/rename.rs:34:9
+ |
+LL | #![warn(clippy::cyclomatic_complexity)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
++
++error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
++ --> $DIR/rename.rs:35:9
+ |
- error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- --> $DIR/rename.rs:11:9
++LL | #![warn(clippy::option_and_then_some)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
+
- LL | #![warn(clippy::mem_discriminant_non_enum)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
++error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
++ --> $DIR/rename.rs:36:9
+ |
- error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
- --> $DIR/rename.rs:13:8
++LL | #![warn(clippy::box_vec)]
++ | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
+
- LL | #[warn(clippy::stutter)]
- | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
++error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
++ --> $DIR/rename.rs:37:9
+ |
- error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
- --> $DIR/rename.rs:16:8
++LL | #![warn(clippy::block_in_if_condition_expr)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+
- LL | #[warn(clippy::new_without_default_derive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
++error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
++ --> $DIR/rename.rs:38:9
+ |
- error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
- --> $DIR/rename.rs:19:8
++LL | #![warn(clippy::block_in_if_condition_stmt)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+
- LL | #[warn(clippy::const_static_lifetime)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
++error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
++ --> $DIR/rename.rs:39:9
++ |
++LL | #![warn(clippy::option_map_unwrap_or)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
++
++error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
++ --> $DIR/rename.rs:40:9
++ |
++LL | #![warn(clippy::option_map_unwrap_or_else)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
++
++error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
++ --> $DIR/rename.rs:41:9
++ |
++LL | #![warn(clippy::result_map_unwrap_or_else)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
++
++error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
++ --> $DIR/rename.rs:42:9
++ |
++LL | #![warn(clippy::option_unwrap_used)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
++
++error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
++ --> $DIR/rename.rs:43:9
++ |
++LL | #![warn(clippy::result_unwrap_used)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
++
++error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
++ --> $DIR/rename.rs:44:9
++ |
++LL | #![warn(clippy::option_expect_used)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
++
++error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
++ --> $DIR/rename.rs:45:9
++ |
++LL | #![warn(clippy::result_expect_used)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
++
++error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
++ --> $DIR/rename.rs:46:9
++ |
++LL | #![warn(clippy::for_loop_over_option)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
++
++error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
++ --> $DIR/rename.rs:47:9
++ |
++LL | #![warn(clippy::for_loop_over_result)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
++
++error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
++ --> $DIR/rename.rs:48:9
++ |
++LL | #![warn(clippy::identity_conversion)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
++
++error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
++ --> $DIR/rename.rs:49:9
++ |
++LL | #![warn(clippy::zero_width_space)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
++
++error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
++ --> $DIR/rename.rs:50:9
++ |
++LL | #![warn(clippy::single_char_push_str)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
++
++error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
++ --> $DIR/rename.rs:51:9
++ |
++LL | #![warn(clippy::if_let_some_result)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
++
++error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
++ --> $DIR/rename.rs:53:9
++ |
++LL | #![warn(clippy::invalid_ref)]
++ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
++
++error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
++ --> $DIR/rename.rs:54:9
++ |
++LL | #![warn(clippy::into_iter_on_array)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
++
++error: lint `clippy::unused_label` has been renamed to `unused_labels`
++ --> $DIR/rename.rs:55:9
++ |
++LL | #![warn(clippy::unused_label)]
++ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
++
++error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
++ --> $DIR/rename.rs:56:9
++ |
++LL | #![warn(clippy::drop_bounds)]
++ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
++
++error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
++ --> $DIR/rename.rs:57:9
++ |
++LL | #![warn(clippy::temporary_cstring_as_ptr)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
++
++error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
++ --> $DIR/rename.rs:58:9
++ |
++LL | #![warn(clippy::panic_params)]
++ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
++
++error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
++ --> $DIR/rename.rs:59:9
++ |
++LL | #![warn(clippy::unknown_clippy_lints)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
++
++error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
++ --> $DIR/rename.rs:60:9
++ |
++LL | #![warn(clippy::invalid_atomic_ordering)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
++
++error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
++ --> $DIR/rename.rs:61:9
+ |
- error: aborting due to 5 previous errors
++LL | #![warn(clippy::mem_discriminant_non_enum)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
+
++error: aborting due to 30 previous errors
+
--- /dev/null
- // edition:2018
-
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::missing_errors_doc,
+ clippy::needless_pass_by_value,
+ clippy::must_use_candidate,
+ clippy::unused_self,
+ clippy::needless_lifetimes,
+ clippy::missing_safety_doc,
+ clippy::wrong_self_convention,
+ clippy::missing_panics_doc
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+
+pub struct T1;
+impl T1 {
+ // corner cases: should not lint
+
+ // no error, not public interface
+ pub(crate) fn drop(&mut self) {}
+
+ // no error, private function
+ fn neg(self) -> Self {
+ self
+ }
+
+ // no error, private function
+ fn eq(&self, other: Self) -> bool {
+ true
+ }
+
+ // No error; self is a ref.
+ fn sub(&self, other: Self) -> &Self {
+ self
+ }
+
+ // No error; different number of arguments.
+ fn div(self) -> Self {
+ self
+ }
+
+ // No error; wrong return type.
+ fn rem(self, other: Self) {}
+
+ // Fine
+ fn into_u32(self) -> u32 {
+ 0
+ }
+
+ fn into_u16(&self) -> u16 {
+ 0
+ }
+
+ fn to_something(self) -> u32 {
+ 0
+ }
+
+ fn new(self) -> Self {
+ unimplemented!();
+ }
+
+ pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> {
+ unimplemented!();
+ }
+}
+
+pub struct T2;
+impl T2 {
+ // Shouldn't trigger lint as it is unsafe.
+ pub unsafe fn add(self, rhs: Self) -> Self {
+ self
+ }
+
+ // Should not trigger lint since this is an async function.
+ pub async fn next(&mut self) -> Option<Self> {
+ None
+ }
+}
--- /dev/null
- // edition:2018
-
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::missing_errors_doc,
+ clippy::needless_pass_by_value,
+ clippy::must_use_candidate,
+ clippy::unused_self,
+ clippy::needless_lifetimes,
+ clippy::missing_safety_doc,
+ clippy::wrong_self_convention,
+ clippy::missing_panics_doc
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+pub struct T;
+
+impl T {
+ // *****************************************
+ // trait method list part 1, should lint all
+ // *****************************************
+ pub fn add(self, other: T) -> T {
+ unimplemented!()
+ }
+
+ pub fn as_mut(&mut self) -> &mut T {
+ unimplemented!()
+ }
+
+ pub fn as_ref(&self) -> &T {
+ unimplemented!()
+ }
+
+ pub fn bitand(self, rhs: T) -> T {
+ unimplemented!()
+ }
+
+ pub fn bitor(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn bitxor(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn borrow(&self) -> &str {
+ unimplemented!()
+ }
+
+ pub fn borrow_mut(&mut self) -> &mut str {
+ unimplemented!()
+ }
+
+ pub fn clone(&self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn cmp(&self, other: &Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn default() -> Self {
+ unimplemented!()
+ }
+
+ pub fn deref(&self) -> &Self {
+ unimplemented!()
+ }
+
+ pub fn deref_mut(&mut self) -> &mut Self {
+ unimplemented!()
+ }
+
+ pub fn div(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn drop(&mut self) {
+ unimplemented!()
+ }
+ // **********
+ // part 1 end
+ // **********
+}
--- /dev/null
- --> $DIR/method_list_1.rs:26:5
+error: method `add` can be confused for the standard trait method `std::ops::Add::add`
- --> $DIR/method_list_1.rs:30:5
++ --> $DIR/method_list_1.rs:24:5
+ |
+LL | / pub fn add(self, other: T) -> T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::should-implement-trait` implied by `-D warnings`
+ = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name
+
+error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut`
- --> $DIR/method_list_1.rs:34:5
++ --> $DIR/method_list_1.rs:28:5
+ |
+LL | / pub fn as_mut(&mut self) -> &mut T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name
+
+error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref`
- --> $DIR/method_list_1.rs:38:5
++ --> $DIR/method_list_1.rs:32:5
+ |
+LL | / pub fn as_ref(&self) -> &T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name
+
+error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand`
- --> $DIR/method_list_1.rs:42:5
++ --> $DIR/method_list_1.rs:36:5
+ |
+LL | / pub fn bitand(self, rhs: T) -> T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name
+
+error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor`
- --> $DIR/method_list_1.rs:46:5
++ --> $DIR/method_list_1.rs:40:5
+ |
+LL | / pub fn bitor(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name
+
+error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor`
- --> $DIR/method_list_1.rs:50:5
++ --> $DIR/method_list_1.rs:44:5
+ |
+LL | / pub fn bitxor(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name
+
+error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow`
- --> $DIR/method_list_1.rs:54:5
++ --> $DIR/method_list_1.rs:48:5
+ |
+LL | / pub fn borrow(&self) -> &str {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name
+
+error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut`
- --> $DIR/method_list_1.rs:58:5
++ --> $DIR/method_list_1.rs:52:5
+ |
+LL | / pub fn borrow_mut(&mut self) -> &mut str {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name
+
+error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone`
- --> $DIR/method_list_1.rs:62:5
++ --> $DIR/method_list_1.rs:56:5
+ |
+LL | / pub fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name
+
+error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp`
- --> $DIR/method_list_1.rs:70:5
++ --> $DIR/method_list_1.rs:60:5
+ |
+LL | / pub fn cmp(&self, other: &Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name
+
+error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref`
- --> $DIR/method_list_1.rs:74:5
++ --> $DIR/method_list_1.rs:68:5
+ |
+LL | / pub fn deref(&self) -> &Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name
+
+error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut`
- --> $DIR/method_list_1.rs:78:5
++ --> $DIR/method_list_1.rs:72:5
+ |
+LL | / pub fn deref_mut(&mut self) -> &mut Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name
+
+error: method `div` can be confused for the standard trait method `std::ops::Div::div`
- --> $DIR/method_list_1.rs:82:5
++ --> $DIR/method_list_1.rs:76:5
+ |
+LL | / pub fn div(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name
+
+error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop`
++ --> $DIR/method_list_1.rs:80:5
+ |
+LL | / pub fn drop(&mut self) {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name
+
+error: aborting due to 14 previous errors
+
--- /dev/null
- // edition:2018
-
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::missing_errors_doc,
+ clippy::needless_pass_by_value,
+ clippy::must_use_candidate,
+ clippy::unused_self,
+ clippy::needless_lifetimes,
+ clippy::missing_safety_doc,
+ clippy::wrong_self_convention,
+ clippy::missing_panics_doc
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+pub struct T;
+
+impl T {
+ // *****************************************
+ // trait method list part 2, should lint all
+ // *****************************************
+
+ pub fn eq(&self, other: &Self) -> bool {
+ unimplemented!()
+ }
+
+ pub fn from_iter<T>(iter: T) -> Self {
+ unimplemented!()
+ }
+
+ pub fn from_str(s: &str) -> Result<Self, Self> {
+ unimplemented!()
+ }
+
+ pub fn hash(&self, state: &mut T) {
+ unimplemented!()
+ }
+
+ pub fn index(&self, index: usize) -> &Self {
+ unimplemented!()
+ }
+
+ pub fn index_mut(&mut self, index: usize) -> &mut Self {
+ unimplemented!()
+ }
+
+ pub fn into_iter(self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn mul(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn neg(self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn next(&mut self) -> Option<Self> {
+ unimplemented!()
+ }
+
+ pub fn not(self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn rem(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn shl(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn shr(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn sub(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+ // **********
+ // part 2 end
+ // **********
+}
--- /dev/null
- --> $DIR/method_list_2.rs:27:5
+error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq`
- --> $DIR/method_list_2.rs:31:5
++ --> $DIR/method_list_2.rs:25:5
+ |
+LL | / pub fn eq(&self, other: &Self) -> bool {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::should-implement-trait` implied by `-D warnings`
+ = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name
+
+error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter`
- --> $DIR/method_list_2.rs:35:5
++ --> $DIR/method_list_2.rs:29:5
+ |
+LL | / pub fn from_iter<T>(iter: T) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name
+
+error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str`
- --> $DIR/method_list_2.rs:39:5
++ --> $DIR/method_list_2.rs:33:5
+ |
+LL | / pub fn from_str(s: &str) -> Result<Self, Self> {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name
+
+error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash`
- --> $DIR/method_list_2.rs:43:5
++ --> $DIR/method_list_2.rs:37:5
+ |
+LL | / pub fn hash(&self, state: &mut T) {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name
+
+error: method `index` can be confused for the standard trait method `std::ops::Index::index`
- --> $DIR/method_list_2.rs:47:5
++ --> $DIR/method_list_2.rs:41:5
+ |
+LL | / pub fn index(&self, index: usize) -> &Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name
+
+error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut`
- --> $DIR/method_list_2.rs:51:5
++ --> $DIR/method_list_2.rs:45:5
+ |
+LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name
+
+error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter`
- --> $DIR/method_list_2.rs:55:5
++ --> $DIR/method_list_2.rs:49:5
+ |
+LL | / pub fn into_iter(self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name
+
+error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul`
- --> $DIR/method_list_2.rs:59:5
++ --> $DIR/method_list_2.rs:53:5
+ |
+LL | / pub fn mul(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name
+
+error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg`
- --> $DIR/method_list_2.rs:63:5
++ --> $DIR/method_list_2.rs:57:5
+ |
+LL | / pub fn neg(self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name
+
+error: method `next` can be confused for the standard trait method `std::iter::Iterator::next`
- --> $DIR/method_list_2.rs:67:5
++ --> $DIR/method_list_2.rs:61:5
+ |
+LL | / pub fn next(&mut self) -> Option<Self> {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name
+
+error: method `not` can be confused for the standard trait method `std::ops::Not::not`
- --> $DIR/method_list_2.rs:71:5
++ --> $DIR/method_list_2.rs:65:5
+ |
+LL | / pub fn not(self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name
+
+error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem`
- --> $DIR/method_list_2.rs:75:5
++ --> $DIR/method_list_2.rs:69:5
+ |
+LL | / pub fn rem(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name
+
+error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl`
- --> $DIR/method_list_2.rs:79:5
++ --> $DIR/method_list_2.rs:73:5
+ |
+LL | / pub fn shl(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name
+
+error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr`
- --> $DIR/method_list_2.rs:83:5
++ --> $DIR/method_list_2.rs:77:5
+ |
+LL | / pub fn shr(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name
+
+error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub`
++ --> $DIR/method_list_2.rs:81:5
+ |
+LL | / pub fn sub(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name
+
+error: aborting due to 15 previous errors
+
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+
+use serde as edres;
+pub use serde;
+
+macro_rules! m {
+ () => {
+ use regex;
+ };
+}
+
+fn main() {
+ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+
+ // False positive #5154, shouldn't trigger lint.
+ m!();
+}
+
+mod hello_mod {
+
+ #[allow(dead_code)]
+ fn hello_mod() {}
+}
+
+mod hi_mod {
+ use self::regex::{Regex, RegexSet};
+ use regex;
+ #[allow(dead_code)]
+ fn hi_mod() {}
+}
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+use serde as edres;
+pub use serde;
+
+macro_rules! m {
+ () => {
+ use regex;
+ };
+}
+
+fn main() {
+ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+
+ // False positive #5154, shouldn't trigger lint.
+ m!();
+}
+
+mod hello_mod {
+ use regex;
+ #[allow(dead_code)]
+ fn hello_mod() {}
+}
+
+mod hi_mod {
+ use self::regex::{Regex, RegexSet};
+ use regex;
+ #[allow(dead_code)]
+ fn hi_mod() {}
+}
--- /dev/null
- --> $DIR/single_component_path_imports.rs:24:5
+error: this import is redundant
- --> $DIR/single_component_path_imports.rs:6:1
++ --> $DIR/single_component_path_imports.rs:23:5
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
+error: this import is redundant
++ --> $DIR/single_component_path_imports.rs:5:1
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+
+error: aborting due to 2 previous errors
+
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+ () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+ () => {};
+}
+ // fail
+
+fn main() {
+ m1!();
+ m2!();
+}
--- /dev/null
- // edition:2018
+// run-rustfix
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+ () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+ () => {};
+}
+use m2; // fail
+
+fn main() {
+ m1!();
+ m2!();
+}
--- /dev/null
- --> $DIR/single_component_path_imports_macro.rs:16:1
+error: this import is redundant
++ --> $DIR/single_component_path_imports_macro.rs:15:1
+ |
+LL | use m2; // fail
+ | ^^^^^^^ help: remove it entirely
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
+error: aborting due to previous error
+
--- /dev/null
- // edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+use serde as edres;
+pub use serde;
+
+fn main() {
+ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+}
+
+mod root_nested_use_mod {
+ use {regex, serde};
+ #[allow(dead_code)]
+ fn root_nested_use_mod() {}
+}
--- /dev/null
- --> $DIR/single_component_path_imports_nested_first.rs:14:10
+error: this import is redundant
- --> $DIR/single_component_path_imports_nested_first.rs:14:17
++ --> $DIR/single_component_path_imports_nested_first.rs:13:10
+ |
+LL | use {regex, serde};
+ | ^^^^^
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+ = help: remove this import
+
+error: this import is redundant
- --> $DIR/single_component_path_imports_nested_first.rs:5:1
++ --> $DIR/single_component_path_imports_nested_first.rs:13:17
+ |
+LL | use {regex, serde};
+ | ^^^^^
+ |
+ = help: remove this import
+
+error: this import is redundant
++ --> $DIR/single_component_path_imports_nested_first.rs:4:1
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+
+error: aborting due to 3 previous errors
+
--- /dev/null
- // edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+ regex::{Regex, RegexSet},
+ some_mod::SomeType,
+};
+use regex;
+
+mod some_mod {
+ pub struct SomeType;
+}
+
+fn main() {}
--- /dev/null
- // edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+ regex::{Regex, RegexSet},
+ some_mod::SomeType,
+};
+
+mod some_mod {
+ pub struct SomeType;
+}
+
+fn main() {}
--- /dev/null
--- /dev/null
++#[warn(clippy::string_slice)]
++#[allow(clippy::no_effect)]
++
++fn main() {
++ &"Ölkanne"[1..];
++ let m = "Mötörhead";
++ &m[2..5];
++ let s = String::from(m);
++ &s[0..2];
++}
--- /dev/null
--- /dev/null
++error: indexing into a string may panic if the index is within a UTF-8 character
++ --> $DIR/string_slice.rs:5:6
++ |
++LL | &"Ölkanne"[1..];
++ | ^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::string-slice` implied by `-D warnings`
++
++error: indexing into a string may panic if the index is within a UTF-8 character
++ --> $DIR/string_slice.rs:7:6
++ |
++LL | &m[2..5];
++ | ^^^^^^^
++
++error: indexing into a string may panic if the index is within a UTF-8 character
++ --> $DIR/string_slice.rs:9:6
++ |
++LL | &s[0..2];
++ | ^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unit_hash)]
++
++use std::collections::hash_map::DefaultHasher;
++use std::hash::Hash;
++
++enum Foo {
++ Empty,
++ WithValue(u8),
++}
++
++fn do_nothing() {}
++
++fn main() {
++ let mut state = DefaultHasher::new();
++ let my_enum = Foo::Empty;
++
++ match my_enum {
++ Foo::Empty => ().hash(&mut state),
++ Foo::WithValue(x) => x.hash(&mut state),
++ }
++
++ let res = ();
++ res.hash(&mut state);
++
++ #[allow(clippy::unit_arg)]
++ do_nothing().hash(&mut state);
++}
--- /dev/null
--- /dev/null
++error: this call to `hash` on the unit type will do nothing
++ --> $DIR/unit_hash.rs:18:23
++ |
++LL | Foo::Empty => ().hash(&mut state),
++ | ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
++ |
++ = note: `-D clippy::unit-hash` implied by `-D warnings`
++ = note: the implementation of `Hash` for `()` is a no-op
++
++error: this call to `hash` on the unit type will do nothing
++ --> $DIR/unit_hash.rs:23:5
++ |
++LL | res.hash(&mut state);
++ | ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
++ |
++ = note: the implementation of `Hash` for `()` is a no-op
++
++error: this call to `hash` on the unit type will do nothing
++ --> $DIR/unit_hash.rs:26:5
++ |
++LL | do_nothing().hash(&mut state);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
++ |
++ = note: the implementation of `Hash` for `()` is a no-op
++
++error: aborting due to 3 previous errors
++
--- /dev/null
- // edition:2018
+#![warn(clippy::unused_async)]
+
+async fn foo() -> i32 {
+ 4
+}
+
+async fn bar() -> i32 {
+ foo().await
+}
+
+fn main() {
+ foo();
+ bar();
+}
--- /dev/null
- --> $DIR/unused_async.rs:4:1
+error: unused `async` for function with no await statements
++ --> $DIR/unused_async.rs:3:1
+ |
+LL | / async fn foo() -> i32 {
+LL | | 4
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::unused-async` implied by `-D warnings`
+ = help: consider removing the `async` from this function
+
+error: aborting due to previous error
+
--- /dev/null
- // edition:2018
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::use_self)]
+#![allow(dead_code)]
+#![allow(
+ clippy::should_implement_trait,
+ clippy::upper_case_acronyms,
+ clippy::from_over_into,
+ clippy::self_named_constructors
+)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+fn main() {}
+
+mod use_self {
+ struct Foo {}
+
+ impl Foo {
+ fn new() -> Self {
+ Self {}
+ }
+ fn test() -> Self {
+ Self::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod better {
+ struct Foo {}
+
+ impl Foo {
+ fn new() -> Self {
+ Self {}
+ }
+ fn test() -> Self {
+ Self::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod lifetimes {
+ struct Foo<'a> {
+ foo_str: &'a str,
+ }
+
+ impl<'a> Foo<'a> {
+ // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
+ // Foo<'b>`
+ fn foo(s: &str) -> Foo {
+ Foo { foo_str: s }
+ }
+ // cannot replace with `Self`, because that's `Foo<'a>`
+ fn bar() -> Foo<'static> {
+ Foo { foo_str: "foo" }
+ }
+
+ // FIXME: the lint does not handle lifetimed struct
+ // `Self` should be applicable here
+ fn clone(&self) -> Foo<'a> {
+ Foo { foo_str: self.foo_str }
+ }
+ }
+}
+
+mod issue2894 {
+ trait IntoBytes {
+ fn to_bytes(self) -> Vec<u8>;
+ }
+
+ // This should not be linted
+ impl IntoBytes for u8 {
+ fn to_bytes(self) -> Vec<u8> {
+ vec![self]
+ }
+ }
+}
+
+mod existential {
+ struct Foo;
+
+ impl Foo {
+ fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
+ foos.iter()
+ }
+
+ fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
+ foos.iter()
+ }
+ }
+}
+
+mod tuple_structs {
+ pub struct TS(i32);
+
+ impl TS {
+ pub fn ts() -> Self {
+ Self(0)
+ }
+ }
+}
+
+mod macros {
+ macro_rules! use_self_expand {
+ () => {
+ fn new() -> Foo {
+ Foo {}
+ }
+ };
+ }
+
+ struct Foo {}
+
+ impl Foo {
+ use_self_expand!(); // Should not lint in local macros
+ }
+
+ #[derive(StructAUseSelf)] // Should not lint in derives
+ struct A;
+}
+
+mod nesting {
+ struct Foo {}
+ impl Foo {
+ fn foo() {
+ #[allow(unused_imports)]
+ use self::Foo; // Can't use Self here
+ struct Bar {
+ foo: Foo, // Foo != Self
+ }
+
+ impl Bar {
+ fn bar() -> Self {
+ Self { foo: Foo {} }
+ }
+ }
+
+ // Can't use Self here
+ fn baz() -> Foo {
+ Foo {}
+ }
+ }
+
+ // Should lint here
+ fn baz() -> Self {
+ Self {}
+ }
+ }
+
+ enum Enum {
+ A,
+ B(u64),
+ C { field: bool },
+ }
+ impl Enum {
+ fn method() {
+ #[allow(unused_imports)]
+ use self::Enum::*; // Issue 3425
+ static STATIC: Enum = Enum::A; // Can't use Self as type
+ }
+
+ fn method2() {
+ let _ = Self::B(42);
+ let _ = Self::C { field: true };
+ let _ = Self::A;
+ }
+ }
+}
+
+mod issue3410 {
+
+ struct A;
+ struct B;
+
+ trait Trait<T> {
+ fn a(v: T) -> Self;
+ }
+
+ impl Trait<Vec<A>> for Vec<B> {
+ fn a(_: Vec<A>) -> Self {
+ unimplemented!()
+ }
+ }
+
+ impl<T> Trait<Vec<A>> for Vec<T>
+ where
+ T: Trait<B>,
+ {
+ fn a(v: Vec<A>) -> Self {
+ <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
+ }
+ }
+}
+
+#[allow(clippy::no_effect, path_statements)]
+mod rustfix {
+ mod nested {
+ pub struct A {}
+ }
+
+ impl nested::A {
+ const A: bool = true;
+
+ fn fun_1() {}
+
+ fn fun_2() {
+ Self::fun_1();
+ Self::A;
+
+ Self {};
+ }
+ }
+}
+
+mod issue3567 {
+ struct TestStruct {}
+ impl TestStruct {
+ fn from_something() -> Self {
+ Self {}
+ }
+ }
+
+ trait Test {
+ fn test() -> TestStruct;
+ }
+
+ impl Test for TestStruct {
+ fn test() -> TestStruct {
+ Self::from_something()
+ }
+ }
+}
+
+mod paths_created_by_lowering {
+ use std::ops::Range;
+
+ struct S {}
+
+ impl S {
+ const A: usize = 0;
+ const B: usize = 1;
+
+ async fn g() -> Self {
+ Self {}
+ }
+
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[Self::A..Self::B]
+ }
+ }
+
+ trait T {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
+ }
+
+ impl T for Range<u8> {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[0..1]
+ }
+ }
+}
+
+// reused from #1997
+mod generics {
+ struct Foo<T> {
+ value: T,
+ }
+
+ impl<T> Foo<T> {
+ // `Self` is applicable here
+ fn foo(value: T) -> Self {
+ Self { value }
+ }
+
+ // `Cannot` use `Self` as a return type as the generic types are different
+ fn bar(value: i32) -> Foo<i32> {
+ Foo { value }
+ }
+ }
+}
+
+mod issue4140 {
+ pub struct Error<From, To> {
+ _from: From,
+ _too: To,
+ }
+
+ pub trait From<T> {
+ type From;
+ type To;
+
+ fn from(value: T) -> Self;
+ }
+
+ pub trait TryFrom<T>
+ where
+ Self: Sized,
+ {
+ type From;
+ type To;
+
+ fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
+ }
+
+ // FIXME: Suggested fix results in infinite recursion.
+ // impl<F, T> TryFrom<F> for T
+ // where
+ // T: From<F>,
+ // {
+ // type From = Self::From;
+ // type To = Self::To;
+
+ // fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
+ // Ok(From::from(value))
+ // }
+ // }
+
+ impl From<bool> for i64 {
+ type From = bool;
+ type To = Self;
+
+ fn from(value: bool) -> Self {
+ if value { 100 } else { 0 }
+ }
+ }
+}
+
+mod issue2843 {
+ trait Foo {
+ type Bar;
+ }
+
+ impl Foo for usize {
+ type Bar = u8;
+ }
+
+ impl<T: Foo> Foo for Option<T> {
+ type Bar = Option<T::Bar>;
+ }
+}
+
+mod issue3859 {
+ pub struct Foo;
+ pub struct Bar([usize; 3]);
+
+ impl Foo {
+ pub const BAR: usize = 3;
+
+ pub fn foo() {
+ const _X: usize = Foo::BAR;
+ // const _Y: usize = Self::BAR;
+ }
+ }
+}
+
+mod issue4305 {
+ trait Foo: 'static {}
+
+ struct Bar;
+
+ impl Foo for Bar {}
+
+ impl<T: Foo> From<T> for Box<dyn Foo> {
+ fn from(t: T) -> Self {
+ Box::new(t)
+ }
+ }
+}
+
+mod lint_at_item_level {
+ struct Foo {}
+
+ #[allow(clippy::use_self)]
+ impl Foo {
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ #[allow(clippy::use_self)]
+ impl Default for Foo {
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod lint_at_impl_item_level {
+ struct Foo {}
+
+ impl Foo {
+ #[allow(clippy::use_self)]
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ impl Default for Foo {
+ #[allow(clippy::use_self)]
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod issue4734 {
+ #[repr(C, packed)]
+ pub struct X {
+ pub x: u32,
+ }
+
+ impl From<X> for u32 {
+ fn from(c: X) -> Self {
+ unsafe { core::mem::transmute(c) }
+ }
+ }
+}
+
+mod nested_paths {
+ use std::convert::Into;
+ mod submod {
+ pub struct B {}
+ pub struct C {}
+
+ impl Into<C> for B {
+ fn into(self) -> C {
+ C {}
+ }
+ }
+ }
+
+ struct A<T> {
+ t: T,
+ }
+
+ impl<T> A<T> {
+ fn new<V: Into<T>>(v: V) -> Self {
+ Self { t: Into::into(v) }
+ }
+ }
+
+ impl A<submod::C> {
+ fn test() -> Self {
+ Self::new::<submod::B>(submod::B {})
+ }
+ }
+}
+
+mod issue6818 {
+ #[derive(serde::Deserialize)]
+ struct A {
+ a: i32,
+ }
+}
+
+mod issue7206 {
+ struct MyStruct<const C: char>;
+ impl From<MyStruct<'a'>> for MyStruct<'b'> {
+ fn from(_s: MyStruct<'a'>) -> Self {
+ Self
+ }
+ }
+
+ // keep linting non-`Const` generic args
+ struct S<'a> {
+ inner: &'a str,
+ }
+
+ struct S2<T> {
+ inner: T,
+ }
+
+ impl<T> S2<T> {
+ fn new() -> Self {
+ unimplemented!();
+ }
+ }
+
+ impl<'a> S2<S<'a>> {
+ fn new_again() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod self_is_ty_param {
+ trait Trait {
+ type Type;
+ type Hi;
+
+ fn test();
+ }
+
+ impl<I> Trait for I
+ where
+ I: Iterator,
+ I::Item: Trait, // changing this to Self would require <Self as Iterator>
+ {
+ type Type = I;
+ type Hi = I::Item;
+
+ fn test() {
+ let _: I::Item;
+ let _: I; // this could lint, but is questionable
+ }
+ }
+}
--- /dev/null
- // edition:2018
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::use_self)]
+#![allow(dead_code)]
+#![allow(
+ clippy::should_implement_trait,
+ clippy::upper_case_acronyms,
+ clippy::from_over_into,
+ clippy::self_named_constructors
+)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+fn main() {}
+
+mod use_self {
+ struct Foo {}
+
+ impl Foo {
+ fn new() -> Foo {
+ Foo {}
+ }
+ fn test() -> Foo {
+ Foo::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod better {
+ struct Foo {}
+
+ impl Foo {
+ fn new() -> Self {
+ Self {}
+ }
+ fn test() -> Self {
+ Self::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod lifetimes {
+ struct Foo<'a> {
+ foo_str: &'a str,
+ }
+
+ impl<'a> Foo<'a> {
+ // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
+ // Foo<'b>`
+ fn foo(s: &str) -> Foo {
+ Foo { foo_str: s }
+ }
+ // cannot replace with `Self`, because that's `Foo<'a>`
+ fn bar() -> Foo<'static> {
+ Foo { foo_str: "foo" }
+ }
+
+ // FIXME: the lint does not handle lifetimed struct
+ // `Self` should be applicable here
+ fn clone(&self) -> Foo<'a> {
+ Foo { foo_str: self.foo_str }
+ }
+ }
+}
+
+mod issue2894 {
+ trait IntoBytes {
+ fn to_bytes(self) -> Vec<u8>;
+ }
+
+ // This should not be linted
+ impl IntoBytes for u8 {
+ fn to_bytes(self) -> Vec<u8> {
+ vec![self]
+ }
+ }
+}
+
+mod existential {
+ struct Foo;
+
+ impl Foo {
+ fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+ foos.iter()
+ }
+
+ fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
+ foos.iter()
+ }
+ }
+}
+
+mod tuple_structs {
+ pub struct TS(i32);
+
+ impl TS {
+ pub fn ts() -> Self {
+ TS(0)
+ }
+ }
+}
+
+mod macros {
+ macro_rules! use_self_expand {
+ () => {
+ fn new() -> Foo {
+ Foo {}
+ }
+ };
+ }
+
+ struct Foo {}
+
+ impl Foo {
+ use_self_expand!(); // Should not lint in local macros
+ }
+
+ #[derive(StructAUseSelf)] // Should not lint in derives
+ struct A;
+}
+
+mod nesting {
+ struct Foo {}
+ impl Foo {
+ fn foo() {
+ #[allow(unused_imports)]
+ use self::Foo; // Can't use Self here
+ struct Bar {
+ foo: Foo, // Foo != Self
+ }
+
+ impl Bar {
+ fn bar() -> Bar {
+ Bar { foo: Foo {} }
+ }
+ }
+
+ // Can't use Self here
+ fn baz() -> Foo {
+ Foo {}
+ }
+ }
+
+ // Should lint here
+ fn baz() -> Foo {
+ Foo {}
+ }
+ }
+
+ enum Enum {
+ A,
+ B(u64),
+ C { field: bool },
+ }
+ impl Enum {
+ fn method() {
+ #[allow(unused_imports)]
+ use self::Enum::*; // Issue 3425
+ static STATIC: Enum = Enum::A; // Can't use Self as type
+ }
+
+ fn method2() {
+ let _ = Enum::B(42);
+ let _ = Enum::C { field: true };
+ let _ = Enum::A;
+ }
+ }
+}
+
+mod issue3410 {
+
+ struct A;
+ struct B;
+
+ trait Trait<T> {
+ fn a(v: T) -> Self;
+ }
+
+ impl Trait<Vec<A>> for Vec<B> {
+ fn a(_: Vec<A>) -> Self {
+ unimplemented!()
+ }
+ }
+
+ impl<T> Trait<Vec<A>> for Vec<T>
+ where
+ T: Trait<B>,
+ {
+ fn a(v: Vec<A>) -> Self {
+ <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
+ }
+ }
+}
+
+#[allow(clippy::no_effect, path_statements)]
+mod rustfix {
+ mod nested {
+ pub struct A {}
+ }
+
+ impl nested::A {
+ const A: bool = true;
+
+ fn fun_1() {}
+
+ fn fun_2() {
+ nested::A::fun_1();
+ nested::A::A;
+
+ nested::A {};
+ }
+ }
+}
+
+mod issue3567 {
+ struct TestStruct {}
+ impl TestStruct {
+ fn from_something() -> Self {
+ Self {}
+ }
+ }
+
+ trait Test {
+ fn test() -> TestStruct;
+ }
+
+ impl Test for TestStruct {
+ fn test() -> TestStruct {
+ TestStruct::from_something()
+ }
+ }
+}
+
+mod paths_created_by_lowering {
+ use std::ops::Range;
+
+ struct S {}
+
+ impl S {
+ const A: usize = 0;
+ const B: usize = 1;
+
+ async fn g() -> S {
+ S {}
+ }
+
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[S::A..S::B]
+ }
+ }
+
+ trait T {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
+ }
+
+ impl T for Range<u8> {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[0..1]
+ }
+ }
+}
+
+// reused from #1997
+mod generics {
+ struct Foo<T> {
+ value: T,
+ }
+
+ impl<T> Foo<T> {
+ // `Self` is applicable here
+ fn foo(value: T) -> Foo<T> {
+ Foo::<T> { value }
+ }
+
+ // `Cannot` use `Self` as a return type as the generic types are different
+ fn bar(value: i32) -> Foo<i32> {
+ Foo { value }
+ }
+ }
+}
+
+mod issue4140 {
+ pub struct Error<From, To> {
+ _from: From,
+ _too: To,
+ }
+
+ pub trait From<T> {
+ type From;
+ type To;
+
+ fn from(value: T) -> Self;
+ }
+
+ pub trait TryFrom<T>
+ where
+ Self: Sized,
+ {
+ type From;
+ type To;
+
+ fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
+ }
+
+ // FIXME: Suggested fix results in infinite recursion.
+ // impl<F, T> TryFrom<F> for T
+ // where
+ // T: From<F>,
+ // {
+ // type From = Self::From;
+ // type To = Self::To;
+
+ // fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
+ // Ok(From::from(value))
+ // }
+ // }
+
+ impl From<bool> for i64 {
+ type From = bool;
+ type To = Self;
+
+ fn from(value: bool) -> Self {
+ if value { 100 } else { 0 }
+ }
+ }
+}
+
+mod issue2843 {
+ trait Foo {
+ type Bar;
+ }
+
+ impl Foo for usize {
+ type Bar = u8;
+ }
+
+ impl<T: Foo> Foo for Option<T> {
+ type Bar = Option<T::Bar>;
+ }
+}
+
+mod issue3859 {
+ pub struct Foo;
+ pub struct Bar([usize; 3]);
+
+ impl Foo {
+ pub const BAR: usize = 3;
+
+ pub fn foo() {
+ const _X: usize = Foo::BAR;
+ // const _Y: usize = Self::BAR;
+ }
+ }
+}
+
+mod issue4305 {
+ trait Foo: 'static {}
+
+ struct Bar;
+
+ impl Foo for Bar {}
+
+ impl<T: Foo> From<T> for Box<dyn Foo> {
+ fn from(t: T) -> Self {
+ Box::new(t)
+ }
+ }
+}
+
+mod lint_at_item_level {
+ struct Foo {}
+
+ #[allow(clippy::use_self)]
+ impl Foo {
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ #[allow(clippy::use_self)]
+ impl Default for Foo {
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod lint_at_impl_item_level {
+ struct Foo {}
+
+ impl Foo {
+ #[allow(clippy::use_self)]
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ impl Default for Foo {
+ #[allow(clippy::use_self)]
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod issue4734 {
+ #[repr(C, packed)]
+ pub struct X {
+ pub x: u32,
+ }
+
+ impl From<X> for u32 {
+ fn from(c: X) -> Self {
+ unsafe { core::mem::transmute(c) }
+ }
+ }
+}
+
+mod nested_paths {
+ use std::convert::Into;
+ mod submod {
+ pub struct B {}
+ pub struct C {}
+
+ impl Into<C> for B {
+ fn into(self) -> C {
+ C {}
+ }
+ }
+ }
+
+ struct A<T> {
+ t: T,
+ }
+
+ impl<T> A<T> {
+ fn new<V: Into<T>>(v: V) -> Self {
+ Self { t: Into::into(v) }
+ }
+ }
+
+ impl A<submod::C> {
+ fn test() -> Self {
+ A::new::<submod::B>(submod::B {})
+ }
+ }
+}
+
+mod issue6818 {
+ #[derive(serde::Deserialize)]
+ struct A {
+ a: i32,
+ }
+}
+
+mod issue7206 {
+ struct MyStruct<const C: char>;
+ impl From<MyStruct<'a'>> for MyStruct<'b'> {
+ fn from(_s: MyStruct<'a'>) -> Self {
+ Self
+ }
+ }
+
+ // keep linting non-`Const` generic args
+ struct S<'a> {
+ inner: &'a str,
+ }
+
+ struct S2<T> {
+ inner: T,
+ }
+
+ impl<T> S2<T> {
+ fn new() -> Self {
+ unimplemented!();
+ }
+ }
+
+ impl<'a> S2<S<'a>> {
+ fn new_again() -> Self {
+ S2::new()
+ }
+ }
+}
+
+mod self_is_ty_param {
+ trait Trait {
+ type Type;
+ type Hi;
+
+ fn test();
+ }
+
+ impl<I> Trait for I
+ where
+ I: Iterator,
+ I::Item: Trait, // changing this to Self would require <Self as Iterator>
+ {
+ type Type = I;
+ type Hi = I::Item;
+
+ fn test() {
+ let _: I::Item;
+ let _: I; // this could lint, but is questionable
+ }
+ }
+}
--- /dev/null
- --> $DIR/use_self.rs:23:21
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:24:13
++ --> $DIR/use_self.rs:22:21
+ |
+LL | fn new() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+ = note: `-D clippy::use-self` implied by `-D warnings`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:26:22
++ --> $DIR/use_self.rs:23:13
+ |
+LL | Foo {}
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:27:13
++ --> $DIR/use_self.rs:25:22
+ |
+LL | fn test() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:32:25
++ --> $DIR/use_self.rs:26:13
+ |
+LL | Foo::new()
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:33:13
++ --> $DIR/use_self.rs:31:25
+ |
+LL | fn default() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:98:24
++ --> $DIR/use_self.rs:32:13
+ |
+LL | Foo::new()
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:98:55
++ --> $DIR/use_self.rs:97:24
+ |
+LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:113:13
++ --> $DIR/use_self.rs:97:55
+ |
+LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:148:29
++ --> $DIR/use_self.rs:112:13
+ |
+LL | TS(0)
+ | ^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:149:21
++ --> $DIR/use_self.rs:147:29
+ |
+LL | fn bar() -> Bar {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:160:21
++ --> $DIR/use_self.rs:148:21
+ |
+LL | Bar { foo: Foo {} }
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:161:13
++ --> $DIR/use_self.rs:159:21
+ |
+LL | fn baz() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:178:21
++ --> $DIR/use_self.rs:160:13
+ |
+LL | Foo {}
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:179:21
++ --> $DIR/use_self.rs:177:21
+ |
+LL | let _ = Enum::B(42);
+ | ^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:180:21
++ --> $DIR/use_self.rs:178:21
+ |
+LL | let _ = Enum::C { field: true };
+ | ^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:222:13
++ --> $DIR/use_self.rs:179:21
+ |
+LL | let _ = Enum::A;
+ | ^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:223:13
++ --> $DIR/use_self.rs:221:13
+ |
+LL | nested::A::fun_1();
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:225:13
++ --> $DIR/use_self.rs:222:13
+ |
+LL | nested::A::A;
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:244:13
++ --> $DIR/use_self.rs:224:13
+ |
+LL | nested::A {};
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:258:25
++ --> $DIR/use_self.rs:243:13
+ |
+LL | TestStruct::from_something()
+ | ^^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:259:13
++ --> $DIR/use_self.rs:257:25
+ |
+LL | async fn g() -> S {
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:263:16
++ --> $DIR/use_self.rs:258:13
+ |
+LL | S {}
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:263:22
++ --> $DIR/use_self.rs:262:16
+ |
+LL | &p[S::A..S::B]
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:286:29
++ --> $DIR/use_self.rs:262:22
+ |
+LL | &p[S::A..S::B]
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:287:13
++ --> $DIR/use_self.rs:285:29
+ |
+LL | fn foo(value: T) -> Foo<T> {
+ | ^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:459:13
++ --> $DIR/use_self.rs:286:13
+ |
+LL | Foo::<T> { value }
+ | ^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
- --> $DIR/use_self.rs:496:13
++ --> $DIR/use_self.rs:458:13
+ |
+LL | A::new::<submod::B>(submod::B {})
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:495:13
+ |
+LL | S2::new()
+ | ^^ help: use the applicable keyword: `Self`
+
+error: aborting due to 28 previous errors
+
--- /dev/null
- // edition:2018
+// aux-build:proc_macro_derive.rs
+
+#![feature(rustc_private)]
+#![warn(clippy::all)]
+#![allow(clippy::blacklisted_name, clippy::eq_op)]
+#![warn(clippy::used_underscore_binding)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+// This should not trigger the lint. There's underscore binding inside the external derive that
+// would trigger the `used_underscore_binding` lint.
+#[derive(DeriveSomething)]
+struct Baz;
+
+macro_rules! test_macro {
+ () => {{
+ let _foo = 42;
+ _foo + 1
+ }};
+}
+
+/// Tests that we lint if we use a binding with a single leading underscore
+fn prefix_underscore(_foo: u32) -> u32 {
+ _foo + 1
+}
+
+/// Tests that we lint if we use a `_`-variable defined outside within a macro expansion
+fn in_macro_or_desugar(_foo: u32) {
+ println!("{}", _foo);
+ assert_eq!(_foo, _foo);
+
+ test_macro!() + 1;
+}
+
+// Struct for testing use of fields prefixed with an underscore
+struct StructFieldTest {
+ _underscore_field: u32,
+}
+
+/// Tests that we lint the use of a struct field which is prefixed with an underscore
+fn in_struct_field() {
+ let mut s = StructFieldTest { _underscore_field: 0 };
+ s._underscore_field += 1;
+}
+
+/// Tests that we do not lint if the underscore is not a prefix
+fn non_prefix_underscore(some_foo: u32) -> u32 {
+ some_foo + 1
+}
+
+/// Tests that we do not lint if we do not use the binding (simple case)
+fn unused_underscore_simple(_foo: u32) -> u32 {
+ 1
+}
+
+/// Tests that we do not lint if we do not use the binding (complex case). This checks for
+/// compatibility with the built-in `unused_variables` lint.
+fn unused_underscore_complex(mut _foo: u32) -> u32 {
+ _foo += 1;
+ _foo = 2;
+ 1
+}
+
+/// Test that we do not lint for multiple underscores
+fn multiple_underscores(__foo: u32) -> u32 {
+ __foo + 1
+}
+
+// Non-variable bindings with preceding underscore
+fn _fn_test() {}
+struct _StructTest;
+enum _EnumTest {
+ _Empty,
+ _Value(_StructTest),
+}
+
+/// Tests that we do not lint for non-variable bindings
+fn non_variables() {
+ _fn_test();
+ let _s = _StructTest;
+ let _e = match _EnumTest::_Value(_StructTest) {
+ _EnumTest::_Empty => 0,
+ _EnumTest::_Value(_st) => 1,
+ };
+ let f = _fn_test;
+ f();
+}
+
+// Tests that we do not lint if the binding comes from await desugaring,
+// but we do lint the awaited expression. See issue 5360.
+async fn await_desugaring() {
+ async fn foo() {}
+ fn uses_i(_i: i32) {}
+
+ foo().await;
+ ({
+ let _i = 5;
+ uses_i(_i);
+ foo()
+ })
+ .await
+}
+
+fn main() {
+ let foo = 0u32;
+ // tests of unused_underscore lint
+ let _ = prefix_underscore(foo);
+ in_macro_or_desugar(foo);
+ in_struct_field();
+ // possible false positives
+ let _ = non_prefix_underscore(foo);
+ let _ = unused_underscore_simple(foo);
+ let _ = unused_underscore_complex(foo);
+ let _ = multiple_underscores(foo);
+ non_variables();
+ await_desugaring();
+}
--- /dev/null
- --> $DIR/used_underscore_binding.rs:26:5
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
- --> $DIR/used_underscore_binding.rs:31:20
++ --> $DIR/used_underscore_binding.rs:25:5
+ |
+LL | _foo + 1
+ | ^^^^
+ |
+ = note: `-D clippy::used-underscore-binding` implied by `-D warnings`
+
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
- --> $DIR/used_underscore_binding.rs:32:16
++ --> $DIR/used_underscore_binding.rs:30:20
+ |
+LL | println!("{}", _foo);
+ | ^^^^
+
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
- --> $DIR/used_underscore_binding.rs:32:22
++ --> $DIR/used_underscore_binding.rs:31:16
+ |
+LL | assert_eq!(_foo, _foo);
+ | ^^^^
+
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
- --> $DIR/used_underscore_binding.rs:45:5
++ --> $DIR/used_underscore_binding.rs:31:22
+ |
+LL | assert_eq!(_foo, _foo);
+ | ^^^^
+
+error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
- --> $DIR/used_underscore_binding.rs:100:16
++ --> $DIR/used_underscore_binding.rs:44:5
+ |
+LL | s._underscore_field += 1;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
++ --> $DIR/used_underscore_binding.rs:99:16
+ |
+LL | uses_i(_i);
+ | ^^
+
+error: aborting due to 6 previous errors
+
--- /dev/null
- //#![allow(clippy::redundant_pub_crate)]
++// edition:2015
+// run-rustfix
+// aux-build:wildcard_imports_helper.rs
+
++// the 2015 edition here is needed because edition 2018 changed the module system
++// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
++// no longer detects some of the cases starting with Rust 2018.
++// FIXME: We should likely add another edition 2021 test case for this lint
++
+#![warn(clippy::wildcard_imports)]
+#![allow(unused)]
+#![allow(clippy::unnecessary_wraps)]
+#![warn(unused_imports)]
+
+extern crate wildcard_imports_helper;
+
+use crate::fn_mod::foo;
+use crate::mod_mod::inner_mod;
+use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
+#[macro_use]
+use crate::struct_mod::{A, inner_struct_mod};
+
+#[allow(unused_imports)]
+use wildcard_imports_helper::inner::inner_for_self_import;
+use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
+use wildcard_imports_helper::{ExternA, extern_foo};
+
+use std::io::prelude::*;
+use wildcard_imports_helper::prelude::v1::*;
+
+struct ReadFoo;
+
+impl Read for ReadFoo {
+ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
+ Ok(0)
+ }
+}
+
+mod fn_mod {
+ pub fn foo() {}
+}
+
+mod mod_mod {
+ pub mod inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod multi_fn_mod {
+ pub fn multi_foo() {}
+ pub fn multi_bar() {}
+ pub fn multi_baz() {}
+ pub mod multi_inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod struct_mod {
+ pub struct A;
+ pub struct B;
+ pub mod inner_struct_mod {
+ pub struct C;
+ }
+
+ #[macro_export]
+ macro_rules! double_struct_import_test {
+ () => {
+ let _ = A;
+ };
+ }
+}
+
+fn main() {
+ foo();
+ multi_foo();
+ multi_bar();
+ multi_inner_mod::foo();
+ inner_mod::foo();
+ extern_foo();
+ inner_extern_bar();
+
+ let _ = A;
+ let _ = inner_struct_mod::C;
+ let _ = ExternA;
+ let _ = PreludeModAnywhere;
+
+ double_struct_import_test!();
+ double_struct_import_test!();
+}
+
+mod in_fn_test {
+ pub use self::inner_exported::*;
+ #[allow(unused_imports)]
+ pub(crate) use self::inner_exported2::*;
+
+ fn test_intern() {
+ use crate::fn_mod::foo;
+
+ foo();
+ }
+
+ fn test_extern() {
+ use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
+ use wildcard_imports_helper::{ExternA, extern_foo};
+
+ inner_for_self_import::inner_extern_foo();
+ inner_extern_foo();
+
+ extern_foo();
+
+ let _ = ExternA;
+ }
+
+ fn test_inner_nested() {
+ use self::{inner::inner_foo, inner2::inner_bar};
+
+ inner_foo();
+ inner_bar();
+ }
+
+ fn test_extern_reexported() {
+ use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
+
+ extern_exported();
+ let _ = ExternExportedStruct;
+ let _ = ExternExportedEnum::A;
+ }
+
+ mod inner_exported {
+ pub fn exported() {}
+ pub struct ExportedStruct;
+ pub enum ExportedEnum {
+ A,
+ }
+ }
+
+ mod inner_exported2 {
+ pub(crate) fn exported2() {}
+ }
+
+ mod inner {
+ pub fn inner_foo() {}
+ }
+
+ mod inner2 {
+ pub fn inner_bar() {}
+ }
+}
+
+fn test_reexported() {
+ use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
+
+ exported();
+ let _ = ExportedStruct;
+ let _ = ExportedEnum::A;
+}
+
+#[rustfmt::skip]
+fn test_weird_formatting() {
+ use crate:: in_fn_test::exported;
+ use crate:: fn_mod::foo;
+
+ exported();
+ foo();
+}
+
+mod super_imports {
+ fn foofoo() {}
+
+ mod should_be_replaced {
+ use super::foofoo;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass {
+ use super::*;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_inside_function {
+ fn with_super_inside_function() {
+ use super::*;
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_further_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::*;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod should_be_replaced_futher_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::insidefoo;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod use_explicit_should_be_replaced {
+ use super_imports::foofoo;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod use_double_super_should_be_replaced {
+ mod inner {
+ use super::super::foofoo;
+
+ fn with_double_super() {
+ let _ = foofoo();
+ }
+ }
+ }
+
+ mod use_super_explicit_should_be_replaced {
+ use super::super::super_imports::foofoo;
+
+ fn with_super_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod attestation_should_be_replaced {
+ use super::foofoo;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+}
--- /dev/null
- //#![allow(clippy::redundant_pub_crate)]
++// edition:2015
+// run-rustfix
+// aux-build:wildcard_imports_helper.rs
+
++// the 2015 edition here is needed because edition 2018 changed the module system
++// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
++// no longer detects some of the cases starting with Rust 2018.
++// FIXME: We should likely add another edition 2021 test case for this lint
++
+#![warn(clippy::wildcard_imports)]
+#![allow(unused)]
+#![allow(clippy::unnecessary_wraps)]
+#![warn(unused_imports)]
+
+extern crate wildcard_imports_helper;
+
+use crate::fn_mod::*;
+use crate::mod_mod::*;
+use crate::multi_fn_mod::*;
+#[macro_use]
+use crate::struct_mod::*;
+
+#[allow(unused_imports)]
+use wildcard_imports_helper::inner::inner_for_self_import;
+use wildcard_imports_helper::inner::inner_for_self_import::*;
+use wildcard_imports_helper::*;
+
+use std::io::prelude::*;
+use wildcard_imports_helper::prelude::v1::*;
+
+struct ReadFoo;
+
+impl Read for ReadFoo {
+ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
+ Ok(0)
+ }
+}
+
+mod fn_mod {
+ pub fn foo() {}
+}
+
+mod mod_mod {
+ pub mod inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod multi_fn_mod {
+ pub fn multi_foo() {}
+ pub fn multi_bar() {}
+ pub fn multi_baz() {}
+ pub mod multi_inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod struct_mod {
+ pub struct A;
+ pub struct B;
+ pub mod inner_struct_mod {
+ pub struct C;
+ }
+
+ #[macro_export]
+ macro_rules! double_struct_import_test {
+ () => {
+ let _ = A;
+ };
+ }
+}
+
+fn main() {
+ foo();
+ multi_foo();
+ multi_bar();
+ multi_inner_mod::foo();
+ inner_mod::foo();
+ extern_foo();
+ inner_extern_bar();
+
+ let _ = A;
+ let _ = inner_struct_mod::C;
+ let _ = ExternA;
+ let _ = PreludeModAnywhere;
+
+ double_struct_import_test!();
+ double_struct_import_test!();
+}
+
+mod in_fn_test {
+ pub use self::inner_exported::*;
+ #[allow(unused_imports)]
+ pub(crate) use self::inner_exported2::*;
+
+ fn test_intern() {
+ use crate::fn_mod::*;
+
+ foo();
+ }
+
+ fn test_extern() {
+ use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
+ use wildcard_imports_helper::*;
+
+ inner_for_self_import::inner_extern_foo();
+ inner_extern_foo();
+
+ extern_foo();
+
+ let _ = ExternA;
+ }
+
+ fn test_inner_nested() {
+ use self::{inner::*, inner2::*};
+
+ inner_foo();
+ inner_bar();
+ }
+
+ fn test_extern_reexported() {
+ use wildcard_imports_helper::*;
+
+ extern_exported();
+ let _ = ExternExportedStruct;
+ let _ = ExternExportedEnum::A;
+ }
+
+ mod inner_exported {
+ pub fn exported() {}
+ pub struct ExportedStruct;
+ pub enum ExportedEnum {
+ A,
+ }
+ }
+
+ mod inner_exported2 {
+ pub(crate) fn exported2() {}
+ }
+
+ mod inner {
+ pub fn inner_foo() {}
+ }
+
+ mod inner2 {
+ pub fn inner_bar() {}
+ }
+}
+
+fn test_reexported() {
+ use crate::in_fn_test::*;
+
+ exported();
+ let _ = ExportedStruct;
+ let _ = ExportedEnum::A;
+}
+
+#[rustfmt::skip]
+fn test_weird_formatting() {
+ use crate:: in_fn_test:: * ;
+ use crate:: fn_mod::
+ *;
+
+ exported();
+ foo();
+}
+
+mod super_imports {
+ fn foofoo() {}
+
+ mod should_be_replaced {
+ use super::*;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass {
+ use super::*;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_inside_function {
+ fn with_super_inside_function() {
+ use super::*;
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_further_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::*;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod should_be_replaced_futher_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::*;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod use_explicit_should_be_replaced {
+ use super_imports::*;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod use_double_super_should_be_replaced {
+ mod inner {
+ use super::super::*;
+
+ fn with_double_super() {
+ let _ = foofoo();
+ }
+ }
+ }
+
+ mod use_super_explicit_should_be_replaced {
+ use super::super::super_imports::*;
+
+ fn with_super_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod attestation_should_be_replaced {
+ use super::*;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+}
--- /dev/null
- --> $DIR/wildcard_imports.rs:12:5
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:13:5
++ --> $DIR/wildcard_imports.rs:17:5
+ |
+LL | use crate::fn_mod::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
+ |
+ = note: `-D clippy::wildcard-imports` implied by `-D warnings`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:14:5
++ --> $DIR/wildcard_imports.rs:18:5
+ |
+LL | use crate::mod_mod::*;
+ | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:16:5
++ --> $DIR/wildcard_imports.rs:19:5
+ |
+LL | use crate::multi_fn_mod::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:20:5
++ --> $DIR/wildcard_imports.rs:21:5
+ |
+LL | use crate::struct_mod::*;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:21:5
++ --> $DIR/wildcard_imports.rs:25:5
+ |
+LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:92:13
++ --> $DIR/wildcard_imports.rs:26:5
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:98:75
++ --> $DIR/wildcard_imports.rs:97:13
+ |
+LL | use crate::fn_mod::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:99:13
++ --> $DIR/wildcard_imports.rs:103:75
+ |
+LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
+ | ^ help: try: `inner_extern_foo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:110:20
++ --> $DIR/wildcard_imports.rs:104:13
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:110:30
++ --> $DIR/wildcard_imports.rs:115:20
+ |
+LL | use self::{inner::*, inner2::*};
+ | ^^^^^^^^ help: try: `inner::inner_foo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:117:13
++ --> $DIR/wildcard_imports.rs:115:30
+ |
+LL | use self::{inner::*, inner2::*};
+ | ^^^^^^^^^ help: try: `inner2::inner_bar`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:146:9
++ --> $DIR/wildcard_imports.rs:122:13
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:155:9
++ --> $DIR/wildcard_imports.rs:151:9
+ |
+LL | use crate::in_fn_test::*;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:156:9
++ --> $DIR/wildcard_imports.rs:160:9
+ |
+LL | use crate:: in_fn_test:: * ;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:167:13
++ --> $DIR/wildcard_imports.rs:161:9
+ |
+LL | use crate:: fn_mod::
+ | _________^
+LL | | *;
+ | |_________^ help: try: `crate:: fn_mod::foo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:202:17
++ --> $DIR/wildcard_imports.rs:172:13
+ |
+LL | use super::*;
+ | ^^^^^^^^ help: try: `super::foofoo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:210:13
++ --> $DIR/wildcard_imports.rs:207:17
+ |
+LL | use super::*;
+ | ^^^^^^^^ help: try: `super::insidefoo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:219:17
++ --> $DIR/wildcard_imports.rs:215:13
+ |
+LL | use super_imports::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:228:13
++ --> $DIR/wildcard_imports.rs:224:17
+ |
+LL | use super::super::*;
+ | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
+
+error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:236:13
++ --> $DIR/wildcard_imports.rs:233:13
+ |
+LL | use super::super::super_imports::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
+
+error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:241:13
+ |
+LL | use super::*;
+ | ^^^^^^^^ help: try: `super::foofoo`
+
+error: aborting due to 21 previous errors
+
--- /dev/null
- // edition:2018
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+#[derive(Clone, Copy)]
+struct Foo;
+
+impl Foo {
+ fn as_i32(self) {}
+ fn as_u32(&self) {}
+ fn into_i32(self) {}
+ fn is_i32(self) {}
+ fn is_u32(&self) {}
+ fn to_i32(self) {}
+ fn from_i32(self) {}
+
+ pub fn as_i64(self) {}
+ pub fn into_i64(self) {}
+ pub fn is_i64(self) {}
+ pub fn to_i64(self) {}
+ pub fn from_i64(self) {}
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ pub fn from_cake(self) {}
+
+ fn as_x<F: AsRef<Self>>(_: F) {}
+ fn as_y<F: AsRef<Foo>>(_: F) {}
+}
+
+struct Bar;
+
+impl Bar {
+ fn as_i32(self) {}
+ fn as_u32(&self) {}
+ fn into_i32(&self) {}
+ fn into_u32(self) {}
+ fn is_i32(self) {}
+ fn is_u32(&self) {}
+ fn to_i32(self) {}
+ fn to_u32(&self) {}
+ fn from_i32(self) {}
+
+ pub fn as_i64(self) {}
+ pub fn into_i64(&self) {}
+ pub fn is_i64(self) {}
+ pub fn to_i64(self) {}
+ pub fn from_i64(self) {}
+
+ // test for false positives
+ fn as_(self) {}
+ fn into_(&self) {}
+ fn is_(self) {}
+ fn to_(self) {}
+ fn from_(self) {}
+ fn to_mut(&mut self) {}
+}
+
+// Allow Box<Self>, Rc<Self>, Arc<Self> for methods that take conventionally take Self by value
+#[allow(clippy::boxed_local)]
+mod issue4293 {
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ struct T;
+
+ impl T {
+ fn into_s1(self: Box<Self>) {}
+ fn into_s2(self: Rc<Self>) {}
+ fn into_s3(self: Arc<Self>) {}
+
+ fn into_t1(self: Box<T>) {}
+ fn into_t2(self: Rc<T>) {}
+ fn into_t3(self: Arc<T>) {}
+ }
+}
+
+// False positive for async (see #4037)
+mod issue4037 {
+ pub struct Foo;
+ pub struct Bar;
+
+ impl Foo {
+ pub async fn into_bar(self) -> Bar {
+ Bar
+ }
+ }
+}
+
+// Lint also in trait definition (see #6307)
+mod issue6307 {
+ trait T: Sized {
+ fn as_i32(self) {}
+ fn as_u32(&self) {}
+ fn into_i32(self) {}
+ fn into_i32_ref(&self) {}
+ fn into_u32(self) {}
+ fn is_i32(self) {}
+ fn is_u32(&self) {}
+ fn to_i32(self) {}
+ fn to_u32(&self) {}
+ fn from_i32(self) {}
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ fn from_cake(self) {}
+
+ // test for false positives
+ fn as_(self) {}
+ fn into_(&self) {}
+ fn is_(self) {}
+ fn to_(self) {}
+ fn from_(self) {}
+ fn to_mut(&mut self) {}
+ }
+
+ trait U {
+ fn as_i32(self);
+ fn as_u32(&self);
+ fn into_i32(self);
+ fn into_i32_ref(&self);
+ fn into_u32(self);
+ fn is_i32(self);
+ fn is_u32(&self);
+ fn to_i32(self);
+ fn to_u32(&self);
+ fn from_i32(self);
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ fn from_cake(self);
+
+ // test for false positives
+ fn as_(self);
+ fn into_(&self);
+ fn is_(self);
+ fn to_(self);
+ fn from_(self);
+ fn to_mut(&mut self);
+ }
+
+ trait C: Copy {
+ fn as_i32(self);
+ fn as_u32(&self);
+ fn into_i32(self);
+ fn into_i32_ref(&self);
+ fn into_u32(self);
+ fn is_i32(self);
+ fn is_u32(&self);
+ fn to_i32(self);
+ fn to_u32(&self);
+ fn from_i32(self);
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ fn from_cake(self);
+
+ // test for false positives
+ fn as_(self);
+ fn into_(&self);
+ fn is_(self);
+ fn to_(self);
+ fn from_(self);
+ fn to_mut(&mut self);
+ }
+}
+
+mod issue6727 {
+ #[derive(Clone, Copy)]
+ struct FooCopy;
+
+ impl FooCopy {
+ fn to_u64(self) -> u64 {
+ 1
+ }
+ // trigger lint
+ fn to_u64_v2(&self) -> u64 {
+ 1
+ }
+ }
+
+ struct FooNoCopy;
+
+ impl FooNoCopy {
+ // trigger lint
+ fn to_u64(self) -> u64 {
+ 2
+ }
+ fn to_u64_v2(&self) -> u64 {
+ 2
+ }
+ }
+}
--- /dev/null
- --> $DIR/wrong_self_convention.rs:17:17
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention.rs:23:21
++ --> $DIR/wrong_self_convention.rs:16:17
+ |
+LL | fn from_i32(self) {}
+ | ^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention.rs:35:15
++ --> $DIR/wrong_self_convention.rs:22:21
+ |
+LL | pub fn from_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
- --> $DIR/wrong_self_convention.rs:37:17
++ --> $DIR/wrong_self_convention.rs:34:15
+ |
+LL | fn as_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
- --> $DIR/wrong_self_convention.rs:39:15
++ --> $DIR/wrong_self_convention.rs:36:17
+ |
+LL | fn into_i32(&self) {}
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by reference or no `self`
- --> $DIR/wrong_self_convention.rs:41:15
++ --> $DIR/wrong_self_convention.rs:38:15
+ |
+LL | fn is_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
- --> $DIR/wrong_self_convention.rs:43:17
++ --> $DIR/wrong_self_convention.rs:40:15
+ |
+LL | fn to_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention.rs:45:19
++ --> $DIR/wrong_self_convention.rs:42:17
+ |
+LL | fn from_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
- --> $DIR/wrong_self_convention.rs:46:21
++ --> $DIR/wrong_self_convention.rs:44:19
+ |
+LL | pub fn as_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
- --> $DIR/wrong_self_convention.rs:47:19
++ --> $DIR/wrong_self_convention.rs:45:21
+ |
+LL | pub fn into_i64(&self) {}
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by reference or no `self`
- --> $DIR/wrong_self_convention.rs:48:19
++ --> $DIR/wrong_self_convention.rs:46:19
+ |
+LL | pub fn is_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
- --> $DIR/wrong_self_convention.rs:49:21
++ --> $DIR/wrong_self_convention.rs:47:19
+ |
+LL | pub fn to_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention.rs:94:19
++ --> $DIR/wrong_self_convention.rs:48:21
+ |
+LL | pub fn from_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
- --> $DIR/wrong_self_convention.rs:97:25
++ --> $DIR/wrong_self_convention.rs:93:19
+ |
+LL | fn as_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
- --> $DIR/wrong_self_convention.rs:99:19
++ --> $DIR/wrong_self_convention.rs:96:25
+ |
+LL | fn into_i32_ref(&self) {}
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by reference or no `self`
- --> $DIR/wrong_self_convention.rs:103:21
++ --> $DIR/wrong_self_convention.rs:98:19
+ |
+LL | fn is_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention.rs:118:19
++ --> $DIR/wrong_self_convention.rs:102:21
+ |
+LL | fn from_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
- --> $DIR/wrong_self_convention.rs:121:25
++ --> $DIR/wrong_self_convention.rs:117:19
+ |
+LL | fn as_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
- --> $DIR/wrong_self_convention.rs:123:19
++ --> $DIR/wrong_self_convention.rs:120:25
+ |
+LL | fn into_i32_ref(&self);
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by reference or no `self`
- --> $DIR/wrong_self_convention.rs:127:21
++ --> $DIR/wrong_self_convention.rs:122:19
+ |
+LL | fn is_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention.rs:145:25
++ --> $DIR/wrong_self_convention.rs:126:21
+ |
+LL | fn from_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
- --> $DIR/wrong_self_convention.rs:151:21
++ --> $DIR/wrong_self_convention.rs:144:25
+ |
+LL | fn into_i32_ref(&self);
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention.rs:175:22
++ --> $DIR/wrong_self_convention.rs:150:21
+ |
+LL | fn from_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value
- --> $DIR/wrong_self_convention.rs:184:19
++ --> $DIR/wrong_self_convention.rs:174:22
+ |
+LL | fn to_u64_v2(&self) -> u64 {
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
++ --> $DIR/wrong_self_convention.rs:183:19
+ |
+LL | fn to_u64(self) -> u64 {
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 24 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>) {}
+ }
+}
--- /dev/null
- --> $DIR/wrong_self_convention2.rs:55:29
+error: methods called `from_*` usually take no `self`
- --> $DIR/wrong_self_convention2.rs:64:25
++ --> $DIR/wrong_self_convention2.rs:54:29
+ |
+LL | pub fn from_be_self(self) -> Self {
+ | ^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
++ --> $DIR/wrong_self_convention2.rs:63:25
+ |
+LL | fn from_be_self(self) -> Self;
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 2 previous errors
+
--- /dev/null
- // edition:2018
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+mod issue6758 {
+ pub enum Test<T> {
+ One(T),
+ Many(Vec<T>),
+ }
+
+ impl<T> Test<T> {
+ // If a method starts with `to_` and not ends with `_mut` it should expect `&self`
+ pub fn to_many(&mut self) -> Option<&mut [T]> {
+ match self {
+ Self::Many(data) => Some(data),
+ _ => None,
+ }
+ }
+
+ // If a method starts with `to_` and ends with `_mut` it should expect `&mut self`
+ pub fn to_many_mut(&self) -> Option<&[T]> {
+ match self {
+ Self::Many(data) => Some(data),
+ _ => None,
+ }
+ }
+ }
+}
--- /dev/null
- --> $DIR/wrong_self_conventions_mut.rs:15:24
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
- --> $DIR/wrong_self_conventions_mut.rs:23:28
++ --> $DIR/wrong_self_conventions_mut.rs:14:24
+ |
+LL | pub fn to_many(&mut self) -> Option<&mut [T]> {
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `*_mut`) usually take `self` by mutable reference
++ --> $DIR/wrong_self_conventions_mut.rs:22:28
+ |
+LL | pub fn to_many_mut(&self) -> Option<&[T]> {
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 2 previous errors
+